IPC-进程间通信

开启多进程

进程名以":"开头的讲程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中,而进程名不以开头的进程属于全局进程, 其他应用通过ShareUID方式可以和它跑在同一个进程中。

我们知道Andrid系统会为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。这里要说明的是,两个应用通过ShareUID跑在同一个进程中是有要求的,需要这两个应用有相同的ShareUID并且签名相同才可以。在这种情况下,它们可以互相访问对方的私有数据,比如data目录、组件信息等,不管它们是否跑在同一个进程中。 当然如果它们跑在同一个进程中,那么除了能共享data目录、组件信息,还可以共享内存数据,或者说它们看起来就像是一个应用 的两个部分。

多进程造成问题

  • 静态成员变量和单例对象完全失效
  • 线程同步失效
  • sp可靠性下降
  • Application多次创建

IPC方式

  • 使用Bundle
  • 共享文件(sp不可靠,系统对sp的读写做了缓存策略)
  • Binder
  • ContentProvider,天生支持跨进程
  • 网络通信实现数据传递,socket
  • Messenger,底层由AIDL实现,一次处理一个请求(Handler)

Messenger

Messenger可以翻译为信使,顾名思义,通过它可以在不同进程中传递Mesge对象, 在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了

  • 服务端进程

    首先,我们需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。

  • 客户端进程

    客户端进程中,首先要绑定服务端的Service, 绑定成功后用服务端返回的IBinder 对 象创建一个Messenger,通过这个Messenger 就可以向服务端发送消息了,发消息类型为 Message 对象。如果需要服务端能够回应客户端,就和服务端一*样,我们还需要创建 一个 Handler并创建一个新的Messenger, 并把这个Messenger对象通过Message的replyTo参数 传递给服务端,服务端通过这个replyTo参数就可以回应客户端。

Binder

直观来说,Binder 是Android中的一个类, 它实现了IBinder 接口。从IPC 角度来说, Binder是Android中的一种跨进 程通信方式,Binder 还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有;从Android Framework角度来说Binder是ServiceManager连接各种Manager (ActivityManager、WindowManager,等等)相应ManagerService的桥梁;

从Android 应用层来说,Binder 是客户端和服务端进行通化的媒介,当bindService 的时候,服务端会返回一一个包含了服务端业务调用的Binder对象通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括通服务和基于AIDL的服务。

Android开发中,Binder主要用在Service中,包括AIDL和Messenger,其中普通Serv中的Binder不涉及进程间通信,所以较为简单,无法触及Binder的核心,而Messenget底层其实是AIDL。

AIDL支持的数据类型

  • 基本数据类型
  • String和CharSequence
  • List,只支持ArrayList,里面每个元素都必须能够被AIDL支持
  • Map,只支持HashMap,里面每个元素都必须能够被AIDL支持
  • Parcelable,所有实现了Parcelable接口的对象
  • AIDL,所有的aidl接口本身也可以在adil文件中使用
aidl注意
  • aidl文件中使用了自定义的Parcelable对象和aidl对象必须显式import进来,同一个包路径下也要导入

  • aidl文件中使用了自定义的Parcelable对象,那必须创建一个和自定义的Parcelable对象同名的aidl文件,并且在使用到这个对象的aidl文件中声名它为Parcelable类型

    parcelable User;
    复制代码
  • aidl文件中每个实现Parcelable接口的类都需要声名parcelable,之外除了基本数据类型,其他类型参数必须标上方向:in out inout

    • in:输入型参数
    • out:输出型参数
    • inout:输入输出参数
    interface IBookManager{
        void addBook(in Book book);
    }
    复制代码
  • aidl接口只支持方法,不支持声明静态常量

  • aidl的包结构在服务端和客户端要保持一直.因为客户端需要反序列化服务端中和aidl接口相关的所有类,类的完整路径不一样反序列化失败

  • aidl方法是在服务端的Binder线程池中执行,CopyOnWriteArrayList自动线程同步,还有ConcurrentHashMap

  • RemoteCallbackList,自动线程同步,进程终止时自动移除注册的listener;beginBroadcast()和finishBroadcast()必须配对使用

  • 服务端aidl接口方法全部运行在Binder线程池中,接口方法可以做耗时操作,如果调用接口的客户端是在UI线程中,那么不要再接口方法中做耗时操作否则就会导致ANR;如果明确知道必须做耗时操作,那么在调用接口的客户端中需要开启线程来调用耗时的aidl接口方法

  • 客户端中的aidl接口运行在客户端的线程池中,不能做刷新UI;

  • 服务端在UI线程中(比如在onBind方法中获取客户端的相关信息)调用客户端耗时的aidl接口,会导致服务端ANR

  • 客户端的onServiceConnected()和onServiceDisconnected()运行在UI线程

Binder死亡、Binder连接断裂
  • 两种方法收到Binder死亡通知
    • 设置DeathRecipient监听-->binderDied()
    • ServiceConnection-->onServiceDisconnected()方法
    • 二者区别
    • onServiceDisconnected()在客户端的UI线程中被回调,binderDied()在客户端的Binder线程池中被回调,binderDied()不能访问UI
  • Binder提供两个配对使用方法:linkToDeath()和unlinkToDeath(),通过linkToDeath方法设置一个死亡代理,Binder死亡时收到通知,此时可以重新发起请求重新连接
private IBinder.DeathRecipient recipient = new IBinder.DeathRecipient(){
    
    @override
    public void binderDied(){
        if(iBookManager == null){
            return;
        }
        iBookManager.asBinder().unlinkToDeath(recipient,0);
        iBookManager = null;
        //这里重新绑定远程Service
    }
};
复制代码

其次,在客户端绑定远程服务成功之后,给Binder设置死亡代理

 @Override
public void onServiceConnected(ComponentName name, IBinder binder) {
    iBookManager =  IBookManager.Stub.asInterface(binder);
    binder.linkToDeath(recipient,0);
}
复制代码
aidl权限验证
  • 在onBind()中通过自定义权限验证,没有权限直接返回null
  • 通过UID和PID验证,使用getCallingUid()和getCallingPid()可以拿到客户端所属应用的UID和PID
  • 通过UID和PID这两个参数拿到包名进行包名验证
ContentProvider
//authority与表名关联,根据传入的URI取出来外界想要操作哪张表
//content://com.icbc.im.provider/user
//content://com.icbc.im.provider/book
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(authority,  path, code);
uriMatcher.addURI(authority,  path, code);
复制代码
  • onCreate()主线程
  • insert query update delete Binder线程池中
  • 数据发生改变通知外界
getContext().getContentResolver().notifyChange(uri, null);
复制代码

外界注册数据改变监听

getContext().getContentResolver().registerContentObserver(uri,true,new ContentObserver(null) {
            @Override
            public void onChange(boolean selfChange, Uri uri) {
                super.onChange(selfChange, uri);
            }
        });
//解除注册
getContext().getContentResolver().unregisterContentObserver()复制代码


猜你喜欢

转载自juejin.im/post/5bff4ae16fb9a049e65ff586