Android 中如何开启多进程
这里说一下一个应用开启多个进程的情况,不讨论多个应用多进程,Android开启多进程只有一种方法,在AndroidManifest文件中给四大组件指定 android:process属性,其实还有一种非常规的方法通过JNI在native层fork一个进程,这种属于特殊情况
复制代码
我们分别给SecondActivity和ThreeActivity指定了process属性,并且他们的属性值不同,所以现在又三个进程,MainActivity默认进程,默认进程进程名是包名,还有俩个我们自己指定的进程 我们通过shell命令,看一下是否有这些进程
$ adb shell ps | grep om.baidu.bpit.aibaidu.ipcu0_a59 2343 204 1606528 98700 0 0000000000 S com.baidu.bpit.aibaidu.ipcu0_a59 2360 204 1609616 97952 0 0000000000 S com.baidu.bpit.aibaidu.ipc:remoteu0_a59 2388 204 1766960 116352 0 0000000000 S om.baidu.bpit.aibaidu.ipc.remote.aa复制代码
我们发现SecondActivity和ThreeActivity指定进程名的方式不同一个是 android:process=":remote" ,一个是android:process="om.baidu.bpit.aibaidu.ipc.remote.aa这俩种有什么区别吗?
区别有两方面
- “:”的意思是在当前进程名上加上本包名,另一种是一个完整的命名方式
- “:”开头的进程,属于当前应用的私有进程,其他应用组件不可以和他跑在同一进程,完整包名的属于全局进程,应用可以通过ShareUid的方式可以和他跑在同一进程
Android 系统会为每一个进程分配一个唯一的Uid,具有相同Uid的应用才可以共享数据,比如data目录,组件信息等,不管他们是否跑在同一进程,如果他们跑在同一进程,那么除了共享data目录,组件信息,还可以共享内存
多进程会遇到的问题
每一个进程都会有一个独立的虚拟机,不同的虚拟机在内存分配不同的空间,所以只要需要通过共享内存,共享数据的操作都会失败
- 静态成员和单例完全失效
- 线程同步机制完全失效
- SharePreference可靠性下降,因为sp底层是通过读写xml来实现的,那么并发读写会发生问题
- Application多次创建
什么是Binder
- 直观来说Binder是Android中的一个类,实现了IBinder
- IPC角度来说,Binder是Android中一种跨进程通信的方式
- Binder还可以理解为一种虚拟的物理设备,他的驱动是dev/binder,这种通信方式linux中没有
- 从Android Framework角度来说,Binder是ServerManager连接各种Manager(如ActivityManager)和ManagerService的桥梁
- 从Android应用层来说,他是服务端和客户端通信的媒介
Android 中的IPC方式
- 使用Bundle,四大组件都是支持Bundle的,由于Bundle实现了Parcelable接口,可以在进程中传递数据,我们在一个进程中启动另一个进程的Bundle,可以进程间通信
- 使用文件共享
- 使用Messager
- 使用Aidl
- 使用ContentProvider
- 使用socket
分析一下AIDL源码
上篇博客我们分析了如何使用AIDL,这篇我们分析一下之前定义的BookName.aidl文件到底生成了什么,编译项目后我们可以在gen目录下找到BookName.java文件,这个就是aidl生成文件,我们看一下文件内容
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /Users/v_renxiaohui01/Downloads/aidl/app/src/main/aidl/com/baidu/bpit/aibaidu/aidl/BookName.aidl */package com.baidu.bpit.aibaidu.aidl;public interface BookName extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.baidu.bpit.aibaidu.aidl.BookName { private static final java.lang.String DESCRIPTOR = "com.baidu.bpit.aibaidu.aidl.BookName"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.baidu.bpit.aibaidu.aidl.BookName interface, * generating a proxy if needed. */ public static com.baidu.bpit.aibaidu.aidl.BookName asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.baidu.bpit.aibaidu.aidl.BookName))) { return ((com.baidu.bpit.aibaidu.aidl.BookName) iin); } return new com.baidu.bpit.aibaidu.aidl.BookName.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getName(); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_getList: { data.enforceInterface(DESCRIPTOR); java.util.List_result = this.getList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addInout: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; if ((0 != data.readInt())) { _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addInout(_arg0); reply.writeNoException(); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } case TRANSACTION_addIn: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; if ((0 != data.readInt())) { _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addIn(_arg0); reply.writeNoException(); return true; } case TRANSACTION_addout: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; _arg0 = new com.baidu.bpit.aibaidu.aidl.User(); this.addout(_arg0); reply.writeNoException(); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.baidu.bpit.aibaidu.aidl.BookName { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public java.lang.String getName() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public java.util.List getList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.baidu.bpit.aibaidu.aidl.User.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addInout, _data, _reply, 0); _reply.readException(); if ((0 != _reply.readInt())) { user.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } @Override public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addIn, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_addout, _data, _reply, 0); _reply.readException(); if ((0 != _reply.readInt())) { user.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_addInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); static final int TRANSACTION_addIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); static final int TRANSACTION_addout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4); } public java.lang.String getName() throws android.os.RemoteException; public java.util.List getList() throws android.os.RemoteException; public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException; public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException; public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;}复制代码
我们分析一下这个类,首先BookName.java继承自android.os.IInterface接口,同时他自己也是一个接口,所有在Binder传输的都需要继承IInterface接口,这个类首先声明了5个方法getName,getList,addInout,addin,addout,这些是我们在aidl文件定义的,同时他还为每个方法声明一个整形,为了在transct区分方法,当应用处于不同进程时需要走transct方法,这个逻辑由sub内部类proxy代理
IBinder
IBinder是一个接口,它代表了一种跨进程传输的能力;只要实现了这个接口,就能将这个对象进行跨进程传递;这是驱动底层支持的;在跨进程数据流经驱动的时候,驱动会识别IBinder类型的数据,从而自动完成不同进程Binder本地对象以及Binder代理对象的转换。
IInterface
这里的IInterface代表的就是远程server对象具有什么能力。具体来说,就是aidl里面的接口。
DESCRIPTOR
Binder的唯一标识,一般用Binder的类名表示,
private static final java.lang.String DESCRIPTOR = "com.baidu.bpit.aibaidu.aidl.BookName";复制代码
asInterface
用于将服务端的Binder转换为客户端所需的AIDL接口类型对象,这种转换是区分进程的,如果客户端和服务端位于同一进程,那么返回的就是服务端本身Stub的对象,如果是不同进程,就返回Stub.Proxy对象
asBinder
此方法用于返回当前的Binder对象
onTransact
该方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,请求会通过底层封装后,交由此方法处理,服务端通过code来判断客户端请求的方法是什么,接着从data取出目标方法需要的参数(如果目标方法有参数的话),然后执行目标方法,执行完之后就向reply中写入返回值(如果目标方法有返回值的话),这就是这个方法的执行过程,如果此方法返回false,那么客户端请求失败,可以以此做验证
Proxy/addList
此方法运行在客户端,当客户端调用从方法时,他的内部实现是这样的,首先准备输入型Parcel对象_data,输出型Parcel对象_reply,和返回值对象List,然后把该方法的参数写入_data(如果有参数的话),接着调用transact方法发起RPC(远程过程调用),同时当前线程挂起,然后服务端的onTransact方法会被调用,知道RPC过程返回后,当前线程继续执行,并从_reply取出返回结果
其他几个Proxy中的方法和此过程一样
这就是Binder工作机制,我们需要注意俩点
- 当客户端发起远程请求是时,当前线程会被挂起,知道服务端返回,如果远程方法是一个耗时方法,那不能在UI线程做
- 服务端的Binder方法运行在Binder线程池中,所以Binder方法不管是否耗时都可以同步调用
自定义一个Binder
从上方流程看出,其实我们可以不通过aidl就可以实现Binder,之所以有了Aidl是为了方便生成代码,那我们手动生成一个Bidner
首先生成一个aidl性质的接口,继承自IInterface即可,如下:
public interface CustomBookName extends IInterface { static final java.lang.String DESCRIPTOR = "com.baidu.bpit.aibaidu.aidl.BookName"; static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_addInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); static final int TRANSACTION_addIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); static final int TRANSACTION_addout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4); public java.lang.String getName() throws android.os.RemoteException; public java.util.ListgetList() throws android.os.RemoteException; public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException; public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException; public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;}复制代码
之后我们需要实现一个Binder继承自Binder,和上方的接口
public abstract class CustomBinder extends Binder implements CustomBookName { /** * Construct the stub at attach it to the interface. */ public CustomBinder() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.baidu.bpit.aibaidu.aidl.BookName interface, * generating a proxy if needed. */ public static CustomBookName asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof CustomBookName))) { return (CustomBookName) iin; } return new CustomBinder.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getName(); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_getList: { data.enforceInterface(DESCRIPTOR); java.util.List_result = this.getList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addInout: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; if ((0 != data.readInt())) { _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addInout(_arg0); reply.writeNoException(); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } case TRANSACTION_addIn: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; if ((0 != data.readInt())) { _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addIn(_arg0); reply.writeNoException(); return true; } case TRANSACTION_addout: { data.enforceInterface(DESCRIPTOR); com.baidu.bpit.aibaidu.aidl.User _arg0; _arg0 = new com.baidu.bpit.aibaidu.aidl.User(); this.addout(_arg0); reply.writeNoException(); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements CustomBookName { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public java.lang.String getName() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(TRANSACTION_getName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public java.util.List getList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(TRANSACTION_getList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.baidu.bpit.aibaidu.aidl.User.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(TRANSACTION_addInout, _data, _reply, 0); _reply.readException(); if ((0 != _reply.readInt())) { user.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } @Override public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((user != null)) { _data.writeInt(1); user.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(TRANSACTION_addIn, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(TRANSACTION_addout, _data, _reply, 0); _reply.readException(); if ((0 != _reply.readInt())) { user.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } }}复制代码
这里面的主要方法,极其作用我们上方都讲过
下面我们使用一下自定义的Bidner类
- 首先实现自己的binder
class MyCustomBinder extends CustomBinder { @Override public String getName() throws RemoteException { return "我是自定义的binder"; } @Override public ListgetList() throws RemoteException { return null; } @Override public void addInout(User user) throws RemoteException { } @Override public void addIn(User user) throws RemoteException { } @Override public void addout(User user) throws RemoteException { } }复制代码
- 然后在service中返回自定义的binder
@Override public IBinder onBind(Intent intent) { return new MyCustomBinder(); }复制代码
- 把自定义的binder类拷进客户端
- 客户端绑定service获取binder
private void bindServer() { Intent mIntent = new Intent(); //你定义的service的action mIntent.setAction("com.aaa.aaa"); //这里你需要设置你应用的包名 mIntent.setPackage("com.baidu.bpit.aibaidu.aidl"); bindService(mIntent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { customBookName = CustomBinder.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } }, BIND_AUTO_CREATE); }复制代码
- 点击按钮调用自定义binder的getName方法
private void initView() { findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { String name = customBookName.getName(); Log.d("mmm",name); } catch (RemoteException e) { e.printStackTrace(); } } }); }复制代码
- 看下log
01-18 19:14:45.688 3554-3554/com.baidu.bpit.aibaidu.client D/mmm: 我是自定义的binder复制代码
成功了