Android进程间的通信 - IPC(机制)Binder的原理和源码阅读
2017-03-24 18:51
756 查看
1. 概述
当初决定分享内涵段子的这个项目我有些担心,担心很多东西心里虽然有了轮廓和细节。但是如果涉及到的东西比较多,那么就有可能没办法去讲太多的细节,况且某些哥们还不会C和C++,所以如果的确觉得IPC这方面比较难可以多去找找这方面的文章看看。这里我们就从三个方面去讲解:1.1:进程间通信的一个小事例;
1.2:应用层的具体流程分析;
1.3:google源码层的具体走向;
所有分享大纲:2017Android进阶之路与你同行
视频讲解地址:http://pan.baidu.com/s/1eRDIivK
2. 进程间通信的一个小事例
为什么会出现IPC这个概念,Android操作系统为了确保进程之间不会产生相互干扰,就是为了你挂了不会影响我,所以采用了进程隔离的机制,即为每个进程分配独立虚拟地址空间,进程之间感觉不到彼此的存在,感觉自己仿佛占用整个内存空间。这样保证了进程的数据安全,但是必然存在另外的问题,那就是进程间通信,进程不可能完全独立运行,有时候需要相互通信获取别的进程的运行结果等,因此需要想办法解决进程间通信的问题,所以就出现了IPC这个概念。其他就不说了,假设我A应用要去B里面应用获取的数据该怎么办,接下来我们就写这么一个实例,这里就涉及到两个单独的应用,我们就把A应用作为客户端,B应用作为服务端。[b]2.1 应用服务端:[/b]
// 编写aidl文件 interface UserCalcAIDL { String getUserName(); String getUserPassword(); }
上面是编写aidl文件,类似于interface,这里我就不介绍aidl了,到后面再去介绍。然后我们在项目中新建一个服务Service,代码如下:
public class UserService extends Service{ private static final String TAG = "server"; // 应用间通信进行绑定 public IBinder onBind(Intent t) { Log.e(TAG, "onBind"); return mBinder; } // 应用间解绑 public boolean onUnbind(Intent intent) { Log.e(TAG, "onUnbind"); return super.onUnbind(intent); } // mBinder 的实例 private final UserCalcAIDL.Stub mBinder = new UserCalcAIDL.Stub() { @Override public String getUserName() throws RemoteException { return "Darren@163.com"; } @Override public String getUserPasword() throws RemoteException { return "940223"; } }; }
最后我们在AndroidManifest中注册配置Service,然后在主Activity中运行该服务,那么服务端代码就算告成了。
<service android:name="com.example.study.aidl.UserService" > <intent-filter> <action android:name="com.study.aidl.user" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
[b]2.2 应用客户端:[/b]
另外再建一个工程,创建与服务端同样的aidl,然后再创建一个布局,里面包含4个按钮,分别为绑定服务,解除绑定,获取用户名,获取用户密码。布局代码我就不贴了,直接上Activity里面的代码:
public class MainActivity extends Activity{ private UserCalcAIDL mCalcAidl; private ServiceConnection mServiceConn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Log.e("client", "onServiceDisconnected"); mCalcAidl = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e("client", "onServiceConnected"); mCalcAidl = UserCalcAIDL.Stub.asInterface(service); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } /** * 点击BindService按钮时调用 * @param view */ public void bindService(View view) { Intent intent = new Intent(); intent.setAction("com.study.aidl.user"); // 在Android 5.0之后google出于安全的角度禁止了隐式声明Intent来启动Service.也禁止使用Intent filter.否则就会抛个异常出来 intent.setPackage("com.study.aidl"); bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE); } /** * 点击unBindService按钮时调用 * @param view */ public void unbindService(View view) { if(mCalcAidl != null){ unbindService(mServiceConn); } } /** * 获取用户密码 * @param view */ public void getUserPassword(View view) throws Exception { if (mCalcAidl != null) { String userPassword = mCalcAidl.getUserPasword(); Toast.makeText(this, "用户密码:"+userPassword, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "服务器未绑定或被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT) .show(); } } /** * 获取用户名 * @param view */ public void getUserName(View view) throws Exception { if (mCalcAidl != null) { String userName = mCalcAidl.getUserName(); Toast.makeText(this, "用户名:"+userName, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "服务器未绑定或被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT) .show(); } } }
[b]2.3 运行的效果:[/b]
首先我们先把服务端B应用运行起来,然后我们把客户端A应用运行起来测试一下效果试试:
3. 应用层的具体流程分析
接下来我们就来看一下,跨进程间的通信在应用层的具体走向,有请aidl出场,我们在客户端会通过bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE)去请求连接,在onServiceConnected()通过IBinder获取UserCalcAIDL实例:
private UserCalcAIDL mCalcAidl; private ServiceConnection mServiceConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e("client", "onServiceConnected"); // 请求连接后会调用该方法,通过IBinder service获取UserCalcAIDL实例mCalcAidl mCalcAidl = UserCalcAIDL.Stub.asInterface(service); } }; /** * 点击BindService按钮时调用 * @param view */ public void bindService(View view) { Intent intent = new Intent(); intent.setAction("com.study.aidl.user"); // 在Android 5.0之后google出于安全的角度禁止了隐式声明Intent来启动Service.也禁止使用Intent filter.否则就会抛个异常出来 intent.setPackage("com.study.aidl"); // 请求绑定连接 服务端 bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE); }
客户端在获取到aidl实例之后就可以调用里面的getUserName和getUserPassword方法,我们点击进去看一下,发现是一个抽象方法,这里我们就不得不说一下 UserCalcAIDL.java文件。这个文件我们自己并没有写我们只写了UserCalcAIDL.aidl文件是系统赠送给我们的,他里面有很多代码:
public interface UserCalcAIDL extends android.os.IInterface { /** * Stub 继承自Binder 实现了 UserCalcAIDL ,连接后服务端返回的mBinder 我们是这么new的 * new UserCalcAIDL.Stub() 也就是客服端连接后的 IBinder service */ public static abstract class Stub extends android.os.Binder implements com.hc.androidipc.UserCalcAIDL { private static final java.lang.String DESCRIPTOR = "com.hc.androidipc.UserCalcAIDL"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * 这个方法是在客户端 onServiceConnected 中调用的返回是一个 Stub.Proxy */ public static com.hc.androidipc.UserCalcAIDL asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.hc.androidipc.UserCalcAIDL))) { return ((com.hc.androidipc.UserCalcAIDL) iin); } return new com.hc.androidipc.UserCalcAIDL.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_getUserName: { // 服务端写数据 写入到reply里面 data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getUserName(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.hc.androidipc.UserCalcAIDL { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { // 这是传递进来的服务端给我们返回的 IBinder其实也是Stub mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } // 这个才是客户端获取用户名的实现方法 @Override public java.lang.String getUserName() 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); // 首先调用服务端返回的Ibinder实例调用transact方法写入到_reply中 mRemote.transact(Stub.TRANSACTION_getUserName, _data, _reply, 0); _reply.readException(); // 然后进行读取 _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } // 返回读取到的用户名 return _result; } static final int TRANSACTION_getUserName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getUserPassword = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public java.lang.String getUserName() throws android.os.RemoteException; public java.lang.String getUserPassword() throws android.os.RemoteException; }
总结一下画一个流程图,客户端通过bindService连接服务端,会调用服务端Service的onBind方法返回一个UserCalcAIDL.Stub的mBinder实例,然后讲该实例返回给客户端的onServiceConnected()方法里面有两个参数有一个IBinder就是服务端返回的mBinder,然后客户端通过该实例建立一个新的AIDL.Stub.Proxy对象,我们在客户端调用获取信息方法的时候其实就是调用的AIDL.Stub.Proxy里面的getUserName()方法,通过mBinder的onTransact()方法写入数据,然后获取数据,就这么个流程了。
4. google源码层的具体走向
你怎么知道调用bindService就会来到服务端的service的onBind()方法呢?当然是源码,记得前面有人说我很喜欢看源码,这其实是一种习惯不是喜不喜欢就像学习一样要渐渐成为一种习惯,有的时候我们宁愿反复的去百度搜索问题的解决方案,却忘记了有一个更好的办法就是源码:@Override public boolean bindService(Intent service, ServiceConnection conn, int flags) { warnIfCallingFromSystemProcess(); return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), Process.myUserHandle()); } private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { IServiceConnection sd; if (conn == null) { throw new IllegalArgumentException("connection is null"); } if (mPackageInfo != null) { sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); } else { throw new RuntimeException("Not supported in system context"); } try { // 调用ActivityManagerNative.getDefault().bindService方法 int res = ActivityManagerNative.getDefault().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier()); return res != 0; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() { protected IActivityManager create() { // binderService获取的也是一个远程服务的Binder对象,也是跨进程,而ServiceManager就是上面 // 流程图里用来管理这些服务和Binder驱动 IBinder b = ServiceManager.getService("activity"); if (false) { Log.v("ActivityManager", "default service binder = " + b); } // 获取到ActivityManager的管理类,最终调用ActivityManagerService是一个典型的跨进程通讯, // 别问为什么千万别纠结 IActivityManager am = asInterface(b); if (false) { Log.v("ActivityManager", "default service = " + am); } return am; } }; // 省略掉一些有关Activity的启动流程,我们再后面再说 private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { if (app.thread == null) { throw new RemoteException(); } requestServiceBindingsLocked(r, execInFg); } private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException { if (r.app == null || r.app.thread == null) { // If service is not currently running, can't yet bind. return false; } if ((!i.requested || rebind) && i.apps.size() > 0) { bumpServiceExecutingLocked(r, execInFg, "bind"); r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE); // IApplicationThread r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState); } return true; } // 找了半天才找到这个方法,在ActivityThread的一个内部类 public final void scheduleBindService(IBinder token, Intent intent, boolean rebind, int processState) { updateProcessState(processState, false); BindServiceData s = new BindServiceData(); s.token = token; s.intent = intent; s.rebind = rebind; if (DEBUG_SERVICE) Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid=" + Binder.getCallingUid() + " pid=" + Binder.getCallingPid()); // 请求绑定 sendMessage(H.BIND_SERVICE, s); } public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case BIND_SERVICE: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind"); handleBindService((BindServiceData)msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; } } private void handleBindService(BindServiceData data) { Service s = mServices.get(data.token); if (DEBUG_SERVICE) Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind); if (s != null) { try { data.intent.setExtrasClassLoader(s.getClassLoader()); data.intent.prepareToEnterProcess(); try { if (!data.rebind) { // 果真调用了service的onBind方法 IBinder binder = s.onBind(data.intent); // 然后把返回的binder实例公开回调出去 ActivityManagerNative.getDefault().publishService( data.token, data.intent, binder); } else { // 调用 onRebind 方法 s.onRebind(data.intent); ActivityManagerNative.getDefault().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } ensureJitEnabled(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } catch (Exception e) { if (!mInstrumentation.onException(s, e)) { throw new RuntimeException( "Unable to bind to service " + s + " with " + data.intent + ": " + e.toString(), e); } } } }
看到这里其实Android系统进程间通信机制Binder的总体架构,它由Client、Server、Service Manager和驱动程序Binder四个组件构成。那么Service Manager在这里起到了什么作用呢?其实Service Manager,它是整个Binder机制的守护进程,用来管理开发者创建的各种Server,并且向Client提供查询Server远程接口的功能。
Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager目录下,主要是由binder.h、binder.c和service_manager.c三个文件组成。Service Manager的入口位于service_manager.c文件中的main函数:
int main(int argc, char **argv) { struct binder_state *bs; void *svcmgr = BINDER_SERVICE_MANAGER; // 这个函数位于frameworks/base/cmds/servicemanager/binder.c文件中 // 通过文件操作函数open来打开/dev/binder设备文件。 // 设备文件/dev/binder是在Binder驱动程序模块初始化的时候创建的, // 大家可以先看一下这个设备文件的创建过程。 // 进入到kernel/common/drivers/staging/android目录中,打开binder.c文件, // 可以看到模块初始化入口binder_init: bs = binder_open(128*1024); if (binder_become_context_manager(bs)) { LOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } svcmgr_handle = svcmgr; // binder_loop函数进入循环,等待Client来请求了。 // binder_loop函数定义在frameworks/base/cmds/servicemanager/binder.c文件中 binder_loop(bs, svcmgr_handler); return 0; }
关于Native的代码我就不贴了,如果再贴下去很多人估计都要开始骂人了,上次有人跟我说他做了好几年的程序员一上来就蒙B。
Native层具体的流程大致是:
1.先打开binder文件 -> 2.建立内存映射 -> 3.通知binder程序驱动 -> 4.进入循环等待请求的到来。
最近有人反应视频更新有点慢,我只能说还真是抱歉,因为我毕竟还是要生活所以还得去公司上班,视频也没收取大家任何的费用,目前还没有达到佛陀的心态,所以只能在周末跟大家分享了。看了这么多也不知道到底怎么用,这么麻烦干嘛?其实是为了后面的双进程守护做准备的,建立打不死的Service小强。
所有分享大纲:2017Android进阶之路与你同行
视频讲解地址:http://pan.baidu.com/s/1eRDIivK
相关文章推荐
- Android进程间的通信 - IPC(机制)Binder的原理和源码阅读
- 图文详解 Android Binder跨进程通信机制 原理
- 图文详解 Android Binder跨进程通信机制 原理
- Android中跨进程通信的IPC机制(Binder框架)
- 图文详解 Android Binder跨进程通信机制和原理
- [置顶] Android开发知识(三)Android进程间Binder通信机制的源码分析(上)
- [置顶] Android开发知识(四)Android进程间Binder通信机制的源码分析(下)
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
- android IPC通信机制中BBinder与BpBinder的区别
- android IPC通信机制中BBinder与BpBinder的区别
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
- Binder---- Android 的IPC 通信机制
- AIDL/IPC Android AIDL/IPC 进程通信机制——超详细讲解及用法案例剖析(播放器)
- Binder---- Android 的IPC 通信机制
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路