一、IPC与多线程

1、IPC简介

线程:① CPU最小的调度单元 ② 一种有限的系统资源
进程:一个执行单元。一般指一个程序或一个应用。一个进程可以包含多个线程。
IPC:进程间通信。

多线程的情况
1)因为某些原因自身需要采用多线程模式来实现。比如:某些模块需要运行在单独进程;为了加大一个应用可以使用的内存。
2)需要从其他应用获取数据。

2、Android中的多进程模式

2.1、开启多进程模式

在Android中一个应用开启多进程唯一办法:给四大组件在AndroidMenifest.xml中指定android:process属性。

默认进程名是包名。“:remote”是一种省略写法,完整名为“com.example.xiang.myapplication:remote”进程名,以“:”开头,属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。

2.2、多进程模式的运行机制

Android系统为每一个进程分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间。将导致存在如下问题:
1)静态成员和单例完全失效
2)线程同步机制完全失效
3)SharedPreferences的可靠性下降
4)Application会多次创建
不同的进程拥有独立的虚拟机、Application和内存空间,导致通过内存来共享数据,都会共享失败。

Android的IPC方式
1)Intent
2)文件共享方式
3)Binder(AIDL和Messenger)
4)ContentProvider
5)Socket

二、IPC的基础概念

为什么要序列化?
1)永久性保存对象的字节序列到本地文件中
2)通过序列化在网络中传递对象
3)通过序列化在进程间传递对象

1、Serializable接口

Serializable是Java提供的一个序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。

private static final long serialVersionUID = 8154678445665565611L;

serialVersionUID是用来辅助序列化和序列化的过程,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够正常地被反序列化。一般我们应该手动指定serialVersionUID的值,比如1L(或者根据类结构生成hash值)。若不指定,反序列化时当前类有所改变(比如增加或者删除了成员变量),那么系统会重新计算当前类的hash值并赋给serialVersionUID,导致serialVersionUID不一致,于是反序列化失败,程序就会crash。

静态成员变量属于类不属于对象,不会参加序列化
用transient标记的成员变量不会参与序列化

2、Parcelable接口

public class User implements Parcelable {    private int userId;    private String userName;    private Book book;    protected User(Parcel in) {        userId = in.readInt();        userName = in.readString();        //book是一个可序列化对象,需要传递当前线程的上下文类加载器        book = in.readParcelable(Thread.currentThread().getContextClassLoader());    }    /**     * 实现反序列化     */    public static final Creator CREATOR = new Creator() {        @Override        public User createFromParcel(Parcel in) {            return new User(in);        }        @Override        public User[] newArray(int size) {            return new User[size];        }    };    /**     * 几乎所有情况都返回0     * @return     */    @Override    public int describeContents() {        return 0;    }    /**     * 实现序列化     *     * @param parcel     * @param i     */    @Override    public void writeToParcel(Parcel parcel, int i) {        parcel.writeInt(userId);        parcel.writeString(userName);        parcel.writeParcelable(book, i);    }}

3、Binder

3.1、AIDL接口的创建

Binder就是Android中实现了IBinder接口的一个类,是跨进程通信的媒介。在Android开发中,Binder主要用在Service中,包括Messenger(底层其实是AIDL)和AIDL。

//Book.javapackage com.example.xiang.myapplication;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable {    private int bookId;    private String bookName;    public Book(int bookId, String bookName) {        this.bookId = bookId;        this.bookName = bookName;    }    /**     * 几乎所有情况都返回0     * @return     */    @Override    public int describeContents() {        return 0;    }    /**     * 实现序列化     * @param parcel     * @param i     */    @Override    public void writeToParcel(Parcel parcel, int i) {        parcel.writeInt(bookId);        parcel.writeString(bookName);    }    /**     * 实现反序列化     */    public static final Creator CREATOR = new Creator() {        @Override        public Book createFromParcel(Parcel in) {            return new Book(in);        }        @Override        public Book[] newArray(int size) {            return new Book[size];        }    };    public Book(Parcel in) {        bookId = in.readInt();        bookName = in.readString();    }}//Book.aidlpackage com.example.xiang.myapplication;parcelable Book;// IBookManager.aidlpackage com.example.xiang.myapplication;import com.example.xiang.myapplication.Book;//必须导入,否则报错interface IBookManager {    List getBookList();    void addBook(in Book book);}

Book.aidl是Book类在AIDL中的声明。IBookManager.aidl是定义的一个接口,虽然Book类和IBookManager在同一个包中,但是还是要显示导入Book类。目录结构如下:

AIDL目录结构

(新建Book.aidl时候,直接填Book为名字时候会报错,只有先创建完之后再RENAME才不会报错)

3.2、Binder类分析

系统为IBookManager.aidl自动生成的Binder类

package com.example.xiang.myapplication;public interface IBookManager extends android.os.IInterface {       public static abstract class Stub extends Binder implements IBookManager {        //Binder的唯一标识        private static final String DESCRIPTOR = "com.example.xiang.myapplication.IBookManager";               public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        public static IBookManager asInterface(IBinder obj) {            if ((obj == null)) {                return null;            }            IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof IBookManager))) {                return ((IBookManager) iin);            }            return new IBookManager.Stub.Proxy(obj);        }        @Override        public android.os.IBinder asBinder() {            return this;        }        @Override        public boolean onTransact(int code, Parcel data,        Parcel reply, int flags) throws RemoteException {             switch (code) {                case INTERFACE_TRANSACTION: {                    reply.writeString(DESCRIPTOR);                    return true;                }                case TRANSACTION_getBookList: {                    data.enforceInterface(DESCRIPTOR);                    List _result = this.getBookList();                    reply.writeNoException();                    reply.writeTypedList(_result);                    return true;                }                case TRANSACTION_addBook: {                    data.enforceInterface(DESCRIPTOR);                    Book _arg0;                    if ((0 != data.readInt())) {                        _arg0 = Book.CREATOR.createFromParcel(data);                    } else {                        _arg0 = null;                    }                    this.addBook(_arg0);                    reply.writeNoException();                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }        private static class Proxy implements IBookManager {            private IBinder mRemote;            Proxy(IBinder remote) {                mRemote = remote;            }            @Override            public IBinder asBinder() {                return mRemote;            }            public String getInterfaceDescriptor() {                return DESCRIPTOR;            }            @Override            public List getBookList() throws RemoteException {                Parcel _data = Parcel.obtain();                Parcel _reply = Parcel.obtain();                List _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    //_data写入参数                    //发起远程调用,当前线程挂起                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);                    _reply.readException();                    _result = _reply.createTypedArrayList(Book.CREATOR);                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            @Override            public void addBook(Book book) throws RemoteException {                Parcel _data = Parcel.obtain();                Parcel _reply = Parcel.obtain();                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    if ((book != null)) {                        _data.writeInt(1);                        book.writeToParcel(_data, 0);                    } else {                        _data.writeInt(0);                    }                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);                    _reply.readException();                } finally {                    _reply.recycle();                    _data.recycle();                }            }        }        static final int TRANSACTION_getBookList = (IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_addBook = (IBinder.FIRST_CALL_TRANSACTION + 1);    }    public List getBookList() throws RemoteException;    public void addBook(Book book) throws RemoteException;}
  • asInterface //将服务端的Binder对象转换为客户端所需的AIDL接口类型的对象,如果C/S位于同一进程,此方法返回就是服务端的Stub对象本身,否则返回的就是系统封装后的Stub.proxy对象
  • asBinder //返回当前的Binder对象
  • onTransact//运行在服务端
  • Proxy#getBookList//运行在客户端,内部实现过程如下:首先创建该方法所需要的输入型对象Parcel对象_data,输出型Parcel对象_reply和返回值对象List。然后把该方法的参数信息写入_data( 如果有参数);接着调用transact方法发起RPC( 远程过程调用),同时当前线程挂起(因此不能再UI线程中发起远程请求);然后服务端的onTransact方法会被调用(服务端的Binder方法运行在线程池,所以需要采用同步方式实现),直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果,最后返回_reply中的数据。
Binder工作机制

AIDL的本质:系统提供的一个快速实现Binder的工具而已。

3.3、远程服务端Service的实现
public class BookManagerService extends Service {    private static final String TAG = "BookManagerService";    //CopyOnWriteArrayList支持并发读/写    private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>();    private Binder mBinder = new IBookManager.Stub() {        @Override        public List getBookList() throws RemoteException {            return mBookList;        }        @Override        public void addBook(Book book) throws RemoteException {            mBookList.add(book);        }    };    @Override    public void onCreate() {        super.onCreate();        mBookList.add(new Book(1, "Android开发艺术探索"));        mBookList.add(new Book(2, "Android进阶之光"));    }    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }}//注册Service

AIDL方法(getBookList和addBook)是运行在Binder线程池中的,所以需要处理线程同步,这里采用CopyOnWriteArrayList来进行自动的线程同步。

3.4、客户端的实现
public class BookManagerActivity extends Activity {    private static final String TAG = "BookManagerActivity";        private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            IBookManager bookManager = IBookManager.Stub.asInterface(service);            try {                List list = bookManager.getBookList();                Log.d(TAG, "查询图书列表:" + list.toString());            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Intent intent = new Intent(this, BookManagerService.class);        bindService(intent, connection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onDestroy() {        unbindService(connection);        super.onDestroy();    }}

服务端的方法可能需要很久才能执行完毕,上面这样写的目的是为了更好的了解AIDL的实现步骤。
对象是不能跨进程直接传输的,对象跨进程传输的本质是反序列化过程,这也是为什么AIDL中定义的对象必须实现Parcelable接口。

4、Binder的两个重要方法

linkToDeath(DeathRecipient recipient, int flags) //设置Binder的死亡代理。当Binder死亡时,系统会回调DeathRecipient的binderDied()方法,所以需要在此方法中移除之前绑定的binder代理(调用unlinkToDeath)并重新绑定远程服务
unlinkToDeath(DeathRecipient recipient, int flags) //移除死亡代理
isBinderAlive() //判断Binder是否死亡

DeathRecipient是IBinder的一个内部接口,里面只有一个方法binderDied()

5、补充说明

AIDL文件支持的数据类型
1)基本数据类型(int、long、char、boolean等)
2)String和CharSequence
3)List:只支持ArrayList,里面的每个元素必须被AIDL所支持
4)Map:只支持HashMap,里面的每个元素必须被AIDL所支持,包括key和value
5)Parcelable:所有实现了Parcelable接口的对象
6)AIDL:所有AIDL接口本身也可以在AIDL文件中使用

  • 自定义的Parcelable对象(如上例中的Book类),必须新建一个和它同名的AIDL文件(Book.aidl),并添加相应的内容。
  • 自定义Parcelable对象和AIDL对象必须要显式import进来。
  • 除了基本数据类型的其他类型参数,都需要标上方向:in、out或者inout。
  • AIDL接口中只支持方法,不支持声明静态常量。
  • 建议把所有和AIDL相关的类和文件全部放在同一个包中,好处是,若客户端在另一应用(模块),复制整个包即可。
  • AIDL的包结构在服务端和客户端必须保持一致,否则运行出错。

DeathRecipient是IBinder的一个内部接口,里面只有一个方法binderDied()

三、Android中的IPC方式

1、使用Intent

启动另一个进程的Activity、Service和Receiver的时候,在Bundle(实现了Parcelable接口)中附加信息,并通过Intent进行传递

2、使用文件共享

序列化一个对象到文件系统中的同时从另一个进程中恢复这个对象,适合对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读写问题。

SharedPreferences底层实现采用XML文件来存储键值对。系统对它的读/写有一定的缓存策略,即在内存中会有一份 SharedPreferences文件的缓存,因此在多进程模式下,系统对它的读/写变得不可靠,面对高并发读/写时SharedPreferences 有很大几率丢失数据,因此不建议在IPC中使用SharedPreferences。

3、使用Messenger(信使)

一种轻量级的IPC方案,底层实现是AIDL。服务端以串行的方式处理客户端发来的Message对象。
举个例子

//MessengerService.java//服务端代码public class MessengerService extends Service {    private static final String TAG = "MessengerService";    private static class MessengerHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case Constants.MSG_FROM_CLIENT:                    //收到客户端的消息                    Log.d(TAG, "收到客户端发来的信息:" + msg.getData().getString("msg"));                    Messenger client = msg.replyTo;                    Message replyMessage = Message.obtain(null, Constants.MSG_FROM_SERVER);                    Bundle bundle = new Bundle();                    bundle.putString("reply","你的消息我已经收到,稍后回复你。");                    try {                        client.send(replyMessage);                    } catch (RemoteException e) {                        e.printStackTrace();                    }                    break;            }        }    }    private final Messenger mMessenger = new Messenger(new MessengerHandler());    @Override    public IBinder onBind(Intent intent) {        return mMessenger.getBinder();    }}//注册Service//MainActivity.java//客户端实现public class MainActivity extends Activity {    private static final String TAG = "MainActivity";    private Messenger mService;    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {            mService = new Messenger(iBinder);            Message msg = Message.obtain(null, Constants.MSG_FROM_CLIENT);            Bundle data = new Bundle();            data.putString("msg", "hello, this is client");            msg.setData(data);            //注意这一句            msg.replyTo = mGetReplyMessenger;            try {                mService.send(msg);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName componentName) {        }    };    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());    private class MessengerHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case Constants.MSG_FROM_SERVER:                    Log.d(TAG, "收到服务端的回复" + msg.getData().getString("reply"));                    break;            }        }    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        final Intent intent = new Intent(this, MessengerService.class);        findViewById(R.id.btn_bind).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                bindService(intent, connection, Context.BIND_AUTO_CREATE);            }        });    }    @Override    protected void onDestroy() {        unbindService(connection);        super.onDestroy();    }}

Messenger中传递的数据必须将数据放入Message,Message和Messenger都实现了Parcelable接口(放入Message中的对象也要实现Parcelable接口才能传递)。工作原理如图所示:
[站外图片上传中...(image-7382db-1516020836113)]

Messenger常用方法

Messenger(Handler target)
Messenger(IBinder target)
send(Message message)
getBinder() IBinder

Message相关属性

replyTo //返回一个可以答复的Messenger

4、使用AIDL

见上面的例子

5、使用ContentProvider

6、使用Socket

四、Binder连接池

五、IPC总结

更多相关文章

  1. Android之Http网络编程(二)
  2. 【Android(安卓)Training - 09】高效地显示Bitmap图片 [ Lesson
  3. Android(安卓)SDK Document 框架导读的翻译和注解[7]——Intents
  4. Android(安卓)面试题目总结【持续更新...】
  5. Android获取三轴加速度和view的重绘
  6. Android登录界面设计
  7. 每天学习一个Android中的常用框架——2.greenDao
  8. Android(安卓)SDK 2.1 - Dev Guide - Best Practives - Designin
  9. Java->Android并发编程引气入门篇

随机推荐

  1. 【android】解决Viewpager设置高度为wrap
  2. Android:用ViewPager实现广告栏
  3. 我学习android参考的资料
  4. Android(安卓)JNI入门第三篇——jni头文
  5. 经纬度转度分秒 Java/Android
  6. android 自定义权限
  7. Android(安卓)Studio 配置多个代码仓库(m
  8. android studio 添加外部库的问题
  9. android -------- ConstraintLayout 宽高
  10. Android(安卓)下载文件 显示进度条