IPC-进程间通讯

开启多进程

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

咱们知道Andrid系统会为每一个应用分配一个惟一的UID,具备相同UID的应用才能共享数据。这里要说明的是,两个应用经过ShareUID跑在同一个进程中是有要求的,须要这两个应用有相同的ShareUID而且签名相同才能够。在这种状况下,它们能够互相访问对方的私有数据,好比data目录、组件信息等,无论它们是否跑在同一个进程中。 固然若是它们跑在同一个进程中,那么除了能共享data目录、组件信息,还能够共享内存数据,或者说它们看起来就像是一个应用 的两个部分。bash

多进程形成问题

  • 静态成员变量和单例对象彻底失效
  • 线程同步失效
  • sp可靠性降低
  • Application屡次建立

IPC方式

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

Messenger

Messenger能够翻译为信使,顾名思义,经过它能够在不一样进程中传递Mesge对象, 在Message中放入咱们须要传递的数据,就能够轻松地实现数据的进程间传递了网络

  • 服务端进程socket

    首先,咱们须要在服务端建立一个Service来处理客户端的链接请求,同时建立一个Handler并经过它来建立个Messenger对象,而后在Service的onBind中返回这个Messenger对象底层的Binder便可。ide

  • 客户端进程ui

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

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()复制代码
相关文章
相关标签/搜索