java世界的binder:App进程间Binder通信
一、一个实例:development/samples/ApiDemos
不会用aidl的先去学会怎么使用aidl再来读这篇文章。
aidl使用之官方指导文档:Android Interface Definition Language (AIDL)
网上不错的例子:Android AIDL使用详解
不过我们今天不打算用自己写的app,而是用aosp里的demo:ApiDemos,考虑是这个例子是官方提供的,经典全面,且一直没怎么变动,上次提交是在Date: Thu May 5 18:00:07 2016
......
1.1 通信两端的关键代码之Client端
客户端要发起通信关键两步,
1、通过bindService方法发起绑定;
2、实现回调方法。ServiceConnection接口的onServiceConnected和onServiceDisconnected。
先看下客户端代码位置。是一个Activity里。
development/samples/ApiDemos/AndroidManifest.xml
619 <activity android:name=".app.RemoteService$Binding"
620 android:label="@string/activity_remote_service_binding">
621 <intent-filter>
622 <action android:name="android.intent.action.MAIN" />
623 <category android:name="android.intent.category.SAMPLE_CODE" />
624 </intent-filter>
625 </activity>
development/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
260 /**
261 * Example of binding and unbinding to the remote service.
262 * This demonstrates the implementation of a service which the client will
263 * bind to, interacting with it through an aidl interface.</p>
264 *.
265 * <p>Note that this is implemented as an inner class only keep the sample
266 * all together; typically this code would appear in some separate class.
267 */
268 // BEGIN_INCLUDE(calling_a_service)
269 public static class Binding extends Activity {
273 ISecondary mSecondaryService = null;
注释第一段说明了这个叫Binding的Activity是用来作为客户端。第二段解释,这个Activity之所以放在内部类里,是为了演示方便。一般的跨进程调用是在不同apk里,例如微信暴露给你个aidl接口,例如你们公司开发了一堆apk,需要做沟通。
看下两步重点的代码的第一步,1、通过bindService方法发起绑定
development/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
269 public static class Binding extends Activity {
365 private OnClickListener mBindListener = new OnClickListener() {
366 public void onClick(View v) {
367 // Establish a couple connections with the service, binding
368 // by interface names. This allows other applications to be
369 // installed that replace the remote service by implementing
370 // the same interface.
371 Intent intent = new Intent(Binding.this, RemoteService.class);
372 intent.setAction(IRemoteService.class.getName());
373 bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
374 intent.setAction(ISecondary.class.getName());
375 bindService(intent, mSecondaryConnection, Context.BIND_AUTO_CREATE);
376 mIsBound = true;
377 mCallbackText.setText("Binding.");
378 }
379 };
372、373行的绑定我们不管,我们这次跟的是ISecondary这个aidl。因为他简单。
371行第一个参数是我们Activity自己,然后374行传入目标的className,指定要找某个远程组件。
375行发起绑定。第二个参数是一个实现ServiceConnection接口的匿名内部类,下面贴。参数三是一个flag,BIND_AUTO_CREATE标志着,服务进程不在时要把他拉起来,就是新建进程。
下面是ServiceConnection接口的实现。
development/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
269 public static class Binding extends Activity {
347 /**
348 * Class for interacting with the secondary interface of the service.
349 */
350 private ServiceConnection mSecondaryConnection = new ServiceConnection() {
351 public void onServiceConnected(ComponentName className,
352 IBinder service) {
353 // Connecting to a secondary interface is the same as any
354 // other interface.
355 mSecondaryService = ISecondary.Stub.asInterface(service);
356 mKillButton.setEnabled(true);
357 }
358
359 public void onServiceDisconnected(ComponentName className) {
360 mSecondaryService = null;
361 mKillButton.setEnabled(false);
362 }
363 };
ServiceConnection接口就俩回调方法,都给他实现了。
第一个方法351行的onServiceConnected。传进来的参数看不懂不要紧,我们后面会走流程分析到。355行ISecondary.Stub.asInterface
是不是眼前一亮。又是熟悉的套路,返回给我们一个Ixxx.Stub.Proxy对象。这个对象保存了355行的入参service,也就是一个BinderProxy对象,其transact方法能穿越jni使用native的IPCThreadState同binder驱动对话,talkWithdriver。
当然入参service是BinderProxy对象这个事情我们现在还是不知道的,等回调发生,我们就明白IBinder service
是BinderProxy还是Binder对象了。(IBinder是个接口,要么实现为Binder代表server,要么实现为BinderProxy代表client)。
第二个方法onServiceDisconnected是断开连接的回调,可以在这里处理一些事情。
另外聪明如你肯定也关注到了mKillButton这个东东,可以猜测我们客户端是要杀掉谁。要杀掉的是服务端的进程,按钮代码如下。
development/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
269 public static class Binding extends Activity {
285 protected void onCreate(Bundle savedInstanceState) {
296 mKillButton.setOnClickListener(mKillListener);
300 mCallbackText.setText("Not attached.");
301 }
//---------------------------------------------------------------------------
405 private OnClickListener mKillListener = new OnClickListener() {
406 public void onClick(View v) {
410 if (mSecondaryService != null) {
411 try {
412 int pid = mSecondaryService.getPid();
421 Process.killProcess(pid);
422 mCallbackText.setText("Killed service process.");
423 } catch (RemoteException ex) {
430 }
431 }
432 }
412行是binder调用,跨进程调用服务端的getPid()方法,然后服务端再次跨进程返回给我们数据。
421行在客户端进程里杀掉服务端,由于我们的client和server都在一个app里uid一样所以是可以做到的,此处只是为了演示。一般没有这么无聊跨进程拿到pid再杀了谁,直接给他发消息,让他自己exit更方便。。。
客户端的代码就罗列到这里,下面看看服务端的关键代码。
1.2 通信两端的关键代码之Server端
上面我们提到,为了演示方便,没有新建app写服务端,但是我们可以改组件的进程名在AndroidManifest.xml中。这个大家应该都懂。
development/samples/ApiDemos/AndroidManifest.xml
608 <service android:name=".app.RemoteService" android:process=":remote" />
上面的activity是这个service的内部类。但是不影响,因为我们指定了另外的进程android:process
。反直觉的就是同一个java文件里的东西竟然不在同一进程O(∩_∩)O哈哈~,这又要从Activity、Service组件的启动流程说起了。可以参考:
startActivity启动过程、startServie启动过程
简单的说就是字面意思,指定了就新建进程,即走的冷启动流程。组件的这个process属性系统如何拿到的呢?
是在安装app时,PMS扫描AndroidManifest.xml的Activity配置,保存了process名称
frameworks/base/core/java/android/content/pm/PackageParser.java
7361 public Component(final ParseComponentArgs args, final ComponentInfo outInfo) {
7362 this(args, (PackageItemInfo)outInfo);
7367 if (args.processRes != 0) {
7368 CharSequence pname;
7369 if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
7370 pname = args.sa.getNonConfigurationString(args.processRes,
7371 Configuration.NATIVE_CONFIG_VERSION);
7372 }
7378 outInfo.processName = buildProcessName(owner.applicationInfo.packageName,
7379 owner.applicationInfo.processName, pname,
7380 args.flags, args.sepProcesses, args.outError);
7381 }
这个方法是parseActivity$Component类的构造函数,在PackageParser.java的parseActivity方法里会new一个parseActivity$Activity实例,这个Activity是parseActivity$Component类的子类。所以在这个父类的构造函数里就把android::process属性拿到了,放到ActivityInfo的processName里存储。当然这个Activity会放到packageInfo里,packageInfo又会放到package里层层存储。
frameworks/base/core/res/res/values/attrs_manifest.xml
470 <!-- Specify a specific process that the associated code is to run in.
471 Use with the application tag (to supply a default process for all
472 application components), or with the activity, receiver, service,
473 or provider tag (to supply a specific icon for that component).
474
475 <p>Application components are normally run in a single process that
476 is created for the entire application. You can use this tag to modify
477 where they run. If the process name begins with a ':' character,
478 a new process private to that application will be created when needed
479 to run that component (allowing you to spread your application across
480 multiple processes). If the process name begins with a lower-case
481 character, the component will be run in a global process of that name,
482 provided that you have permission to do so, allowing multiple
483 applications to share one process to reduce resource usage. -->
484 <attr name="process" format="string" />
这块代码是上面7370行的参数1,也就是这个东东R.styleable.AndroidManifestActivity_process,这你应该很熟悉,R类嘛
跑题了,把android:process属性发散了一下。现在看server的服务端代码吧。
development/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java
58 public class RemoteService extends Service {
106 @Override
107 public IBinder onBind(Intent intent) {
114 if (ISecondary.class.getName().equals(intent.getAction())) {
115 return mSecondaryBinder;
116 }
117 return null;
118 }
//---------------------------------------------------------------------
135 private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() {
136 public int getPid() {
137 return Process.myPid();
138 }
142 };
关键点1、实现onBind回调函数,当client调用bindService方法时,通过系统的中转,会回调到这里,我们作为服务端会把这个Stub实现类返回去。
1.3 ISecondary的aidl代码
最后别忘了我们一直在使用的接口ISecondary,是定义在aidl文件里的,编译时才会转换成java文件里的interface。
由于我们的client和server都在同一个工程同一个包里,所以aidl只需要放一份就够了,也不用import。当然,将所有使用到的包和field显示地列在import里是非常好的编码习惯。
development/samples/ApiDemos/src/com/example/android/apis/app/ISecondary.aidl
17 package com.example.android.apis.app;
18
19 /**
20 * Example of a secondary interface associated with a service. (Note that
21 * the interface itself doesn't impact, it is just a matter of how you
22 * retrieve it from the service.)
23 */
24 interface ISecondary {
25 /**
26 * Request the PID of this service, to do evil things with it.
27 */
28 int getPid();
36 }
看26行注释,要做点邪恶的事情。这个aidl很简单,只有简单数据类型int的方法。
我们的重点是关注流程,关注主干的流程,所以用简单的例子突出重点。
二、大回调的细节:bindService-->onBind-->onServiceConnected
关键代码贴完了,我们会自然的产生这样一个疑问:
1、为什么Client端bindService就可以到达Server端onBind?
2、然后又能回调到Client端的onServiceConnected方法呢?
3、这一个来回都传递了什么数据?
2.1 了解一下Context家族
bindService是Context的方法,由于我们的Activity继承了Context所以能调用到ContextImpl里的bindService方法实现。
Context家族应用了Decorator设计模式,详细的可以参考一下两篇文章:
Android深入理解Context(一)Context关联类和Application Context创建过程
Android深入理解Context(二)Activity和Service的Context创建过程
问你个android八股题,APP里有多少个Context实现类?你可能都会抢答了:Activity 数量+ Service数量 + 1,1是application。
但是很不幸,这是错的。。。
如果你认真跟过context创建流程的话,你会得出另一个正确的答案,(Context数量+ Service数量 + 1) x 2。因为每个组件创建的时候都会new一个contextImpl。。。
以Activity的创建为例
frameworks/base/core/java/android/app/ActivityThread.java
3159 private Activity performLaunchActivity(...) {
3178 ContextImpl appContext = createBaseContextForActivity(r);
3179 Activity activity = null;
3182 activity = mInstrumentation.newActivity(
3183 cl, component.getClassName(), r.intent);
//----------------------------------------------------------------------
3347 private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
3355 ContextImpl appContext = ContextImpl.createActivityContext(
3356 this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
frameworks/base/core/java/android/app/ContextImpl.java
2467 static ContextImpl createActivityContext(...) {
2488 ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
2489 activityToken, null, 0, classLoader, null);
可以看到3182行new了一个Activity实例,2488行new了一个ContextImpl实例,也就是俩。Application和Service的创建也是一样。
题外话结束,我们了解了Context家族,现在走进bindService方法,开始client:bindService-->server:onBind旅程。
2.2 bindService:发起者app进程-->ams
从RemoteService.java方法的onClick里出发
365 private OnClickListener mBindListener = new OnClickListener() {
366 public void onClick(View v) {
371 Intent intent = new Intent(Binding.this, RemoteService.class);
37374 intent.setAction(ISecondary.class.getName());
375 bindService(intent, mSecondaryConnection, Context.BIND_AUTO_CREATE);
frameworks/base/core/java/android/content/ContextWrapper.java
702 @Override
703 public boolean bindService(Intent service, ServiceConnection conn,
704 int flags) {
705 return mBase.bindService(service, conn, flags);
706 }
705行mBase是ContextImpl实例,不知道为什么的说明没学习上一小节的context创建流程。
frameworks/base/core/java/android/app/ContextImpl.java
1647 @Override
1648 public boolean bindService(Intent service, ServiceConnection conn, int flags) {
1649 warnIfCallingFromSystemProcess();
1650 return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
1651 getUser());
1652 }
Calling a method in the system process without a qualified user
这句log是不是老熟悉了,就是1649行方法打印的。
frameworks/base/core/java/android/app/ContextImpl.java
1707 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
1708 String instanceName, Handler handler, Executor executor, UserHandle user) {
1709 // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
1710 IServiceConnection sd;
1717 if (mPackageInfo != null) {
1718 if (executor != null) {
1719 sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
1720 } else {
1721 sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
1722 }
1727 try {
1734 service.prepareToLeaveProcess(this);
1735 int res = ActivityManager.getService().bindIsolatedService(
1736 mMainThread.getApplicationThread(), getActivityToken(), service,
1737 service.resolveTypeIfNeeded(getContentResolver()),
1738 sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
1743 return res != 0;
1744 } catch (RemoteException e) {}
1747 }
1735行的方法调用需重点关注这几个参数。
2.2.1 bindIsolatedService的参数
参数1,mMainThread.getApplicationThread()
获取APP进程的binder服务端Stub实例,这个服务端之后用来接收ams的binder调用。我们简单看下这个服务端的样貌。
frameworks/base/core/java/android/app/ActivityThread.java
219 public final class ActivityThread extends ClientTransactionHandler {
293 final ApplicationThread mAppThread = new ApplicationThread();
904 private class ApplicationThread extends IApplicationThread.Stub {
911 public final void scheduleReceiver(Intent intent, ActivityInfo info,
943 public final void scheduleCreateService(IBinder token,
//---------------------------------------------------------------------------
954 public final void scheduleBindService(IBinder token, Intent intent,
955 boolean rebind, int processState) {
956 updateProcessState(processState, false);
957 BindServiceData s = new BindServiceData();
958 s.token = token;
959 s.intent = intent;
960 s.rebind = rebind;
965 sendMessage(H.BIND_SERVICE, s);
966 }
//---------------------------------------------------------------------------
968 public final void scheduleUnbindService(IBinder token, Intent intent) {
969 BindServiceData s = new BindServiceData();
970 s.token = token;
971 s.intent = intent;
973 sendMessage(H.UNBIND_SERVICE, s);
974 }
//---------------------------------------------------------------------------
1665 public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
219行和904行可以看出,ApplicationThread是ActivityThread的内部类,是一个binder服务端,提供的服务是什么呢?
是一些操作app进程的方法,例如1665行转换activity的生命周期,854行、968行的绑定解绑Service,也就是我们本文关注的内容,911行的分发广播等等。
这些方法的实现也都是类似的,有套路的。像我们看到的654行scheduleBindService,968行scheduleUnbindService一样,先拿到system_server进程发过来的数据,然后解析,然后sendMessage给主线程的handler大H类。这个类你当然也知道,上面有讲过。
需要注意这里的sendMessage是自封装的,里面的实现仍然是mH.sendMessage,不要被迷惑。
frameworks/base/core/java/android/app/ActivityThread.java
3118 private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
3123 Message msg = Message.obtain();
3124 msg.what = what;
3125 msg.obj = obj;
3126 msg.arg1 = arg1;
3127 msg.arg2 = arg2;
3131 mH.sendMessage(msg);
3132 }
参数2,getActivityToken()
这个参数简单说下吧,其实是比较复杂的。他沟通了AMS和WMS还有app进程,是一个activity的唯一标志。代表唯一window的windowToke是基于activityToke产生的。
本文我们不关注这个。
参数5,sd
这个很重要,和我们的bindService流程有莫大的关系。bind成功后ams会跨进程回调mSecondaryConnection对象的回调函数OnServiceConnected。
需要注意区分我们APP里new的ServiceConnection接口,和此处的IServiceConnection接口。一个是普通的回调接口,另一个是binder的interface
//frameworks/base/core/java/android/content/ServiceConnection.java
28 public interface ServiceConnection {
//-------------------------------------------------------------------------------
//out/soong/.intermediates/frameworks/base/framework/android_common/gen/aidl/frameworks/base/core/java/android/app/IServiceConnection.java
6 public interface IServiceConnection extends android.os.IInterface
20 public static abstract class Stub extends android.os.Binder implements android.app.IServiceConnection
100 private static class Proxy implements android.app.IServiceConnection
我们深入分析下这个binder接口。从这里开始
frameworks/base/core/java/android/app/ContextImpl.java
1719 sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
frameworks/base/core/java/android/app/LoadedApk.java
1654 public final IServiceConnection getServiceDispatcher(ServiceConnection c,
1655 Context context, Handler handler, int flags) {
1656 return getServiceDispatcherCommon(c, context, handler, null, flags);
1657 }
//----------------------------------------------------------------------------------
1659 public final IServiceConnection getServiceDispatcher(ServiceConnection c,
1660 Context context, Executor executor, int flags) {
1661 return getServiceDispatcherCommon(c, context, null, executor, flags);
1662 }
//----------------------------------------------------------------------------------
1664 private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
1665 Context context, Handler handler, Executor executor, int flags) {
1666 synchronized (mServices) {
1667 LoadedApk.ServiceDispatcher sd = null;
1668 ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
1669 if (map != null) {
1671 sd = map.get(c);
1672 }
1673 if (sd == null) {
1674 if (executor != null) {
1675 sd = new ServiceDispatcher(c, context, executor, flags);
1676 } else {
1677 sd = new ServiceDispatcher(c, context, handler, flags);
1678 }
1680 if (map == null) {
1681 map = new ArrayMap<>();
1682 mServices.put(context, map);
1683 }
1684 map.put(c, sd);
1685 } else {
1686 sd.validate(context, handler, executor);
1687 }
1688 return sd.getIServiceConnection();
1689 }
1690 }
走到1664行的getServiceDispatcherCommon方法。我们只看初始化的流程,也就是map未做缓存。
1675行new一个ServiceDispatcher实例,将ServiceConnection实例也就是app里那个回调放进来。
frameworks/base/core/java/android/app/LoadedApk.java
1756 static final class ServiceDispatcher {
1757 private final ServiceDispatcher.InnerConnection mIServiceConnection;
//------------------------------------------------------------------------------------
1776 private static class InnerConnection extends IServiceConnection.Stub {
1778 final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
1780 InnerConnection(LoadedApk.ServiceDispatcher sd) {
1781 mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
1782 }
1783
1784 public void connected(ComponentName name, IBinder service, boolean dead)
1785 throws RemoteException {
1786 LoadedApk.ServiceDispatcher sd = mDispatcher.get();
1787 if (sd != null) {
1788 sd.connected(name, service, dead);
1789 }
1790 }
1791 }
//------------------------------------------------------------------------------------
1797 ServiceDispatcher(ServiceConnection conn,
1798 Context context, Handler activityThread, int flags) {
1799 mIServiceConnection = new InnerConnection(this);
1800 mConnection = conn;
1801 mContext = context;
1802 mActivityThread = activityThread;
1803 mActivityExecutor = null;
1804 mLocation = new ServiceConnectionLeaked(null);
1805 mLocation.fillInStackTrace();
1806 mFlags = flags;
1807 }
//------------------------------------------------------------------------------------
1862 IServiceConnection getIServiceConnection() {
1863 return mIServiceConnection;
1864 }
InnerConnection类是真正的binder的服务实现,继承了IServiceConnection.Stub。现在我们可以回答本小节的题目,参数5sd是什么。
sd = new ServiceDispatcher(c, context, executor, flags)-->getServiceDispatcherCommon()-->sd = new ServiceDispatcher.getIServiceConnection()-->mIServiceConnection。
即一个ServiceDispatcher.InnerConnection对象,这个对象可以实现跨进程回调。目前我们在发起者APP进程里,后续这个对象会跨进程传到ams保存。之后Service进程会onBind-->publishService再次回到ams,下一步就会跨进程回调回来了。
2.2.2 bindIsolatedService
上面针对参数,尤其是针对IServiceConnection这个binder服务,这个跨进程的回调实现澄清了一下。
frameworks/base/core/java/android/app/ContextImpl.java
1707 private boolean bindServiceCommon(
1735 int res = ActivityManager.getService().bindIsolatedService
现在出发,发给AMS,让系统服务做处理。1735的ActivityManager.getService()调用就不细说了,在之前的java世界的binder里有分析,我们直接跳转到服务端实现代码。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
14068 public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
14069 String resolvedType, IServiceConnection connection, int flags, String instanceName,
14070 String callingPackage, int userId) throws TransactionTooLargeException {
14071 enforceNotIsolatedCaller("bindService");
14094 synchronized(this) {
14095 return mServices.bindServiceLocked(caller, token, service,
14096 resolvedType, connection, flags, instanceName, callingPackage, userId);
14097 }
14098 }
14095行继续跳转。mServices是ActiveServices类实例。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
mServices = hasHandlerThread ? new ActiveServices(this) : null;
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
1557 int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
1558 String resolvedType, final IServiceConnection connection, int flags,
1559 String instanceName, String callingPackage, final int userId)
1560 throws TransactionTooLargeException {
bindServiceLocked的参数
这些参数来自bindService发起的app进程,我们做下解析。
IApplicationThread caller
是发起者app进程中的ApplicationThread 的BinderProxy代理对象
- IBinder token
是发起者app进程中那个getActivityToken拿到的mToken,目前在ams进程里就是Binder对象了。
- Intent service
虽然叫service但是是一个Intent对象,即我们发起bindeService时传的参
- final IServiceConnection connection
我们花了一个小节分析的ServiceDispatcher.InnerConnection对象,他是ServiceDispatcher的内部类。ServiceDispatcher实例持有这俩容易混淆的成员对象。
1811 mIServiceConnection = new InnerConnection(this);
1812 mConnection = conn;
1811是binder的服务端实例,1812只是个普通的回调实例,我们在app代码里new ServiceConnection()创建出来的。
现在是在ams进程里,所以是个代理对象BinderProxy。
题外话:为什么Binder对象跨进程了就变成了BinderProxy对象了?
这是binder驱动和用户空间共同作用的结果。从前文的surfaceflinger注册服务和bootanimation获取服务提炼一下。
1、发起端进程在writeStrongBinder方法里将BBinder对象拍扁为flat_binder_obj结构体。
2、在驱动的binder_transaction方法中,会translate服务端node成为一个ref,计算出handle给目的端进程持有。
3、到了目的端进程用户空间,会使用对应于writeStrongBinder的readStrongBinder方法,unflat拿到handle创建一个BpBinder对象:BpBinder::create(handle)。
我们从这个BBinder对象到BpBinder的转化流程可以了解到binder驱动与libbinder库的分工。
binder驱动仅记录一个进程为node,根据BC和BR命名码对ioctl传下来的数据做操作,提供node和ref的互相转换、数据拷贝功能。
我们的libbinder为了编码方便,用parcel、flat_binder_obj结构体封装数据。如果本次传输想传递BBinder对象,那么程序员就需要在发起端调用writeStrongBinder方法,就需要在接收端调用readStrongBinder根据驱动传回的ref,handle转化为一个BpBinder::create(handle)对象。反映到java层就是Binder对象到BinderProxy对象的转换。
驱动解决了基础的机制问题,而用户空间的代码libbinder,更多是关注业务,即更方便的让使用者编码。
这里有篇文章很不错,扩展地讲了这个问题:Android Binder框架实现之Parcel详解之read/writeStrongBinder实现
bindServiceLocked
参数的部分解析完毕,下面走入bindServiceLocked方法。
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
1557 int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
1558 String resolvedType, final IServiceConnection connection, int flags,
1559 String instanceName, String callingPackage, final int userId)
1560 throws TransactionTooLargeException {
1634 ServiceLookupResult res =
1635 retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
1636 Binder.getCallingPid(), Binder.getCallingUid(), userId, true,
1637 callerFg, isBindExternal, allowInstant);
1644 ServiceRecord s = res.record;
1724 try {
1754 AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
1755 ConnectionRecord c = new ConnectionRecord(b, activity,
1756 connection, flags, clientLabel, clientIntent,
1757 callerApp.uid, callerApp.processName, callingPackage);
1758
1759 IBinder binder = connection.asBinder();
1760 s.addConnection(binder, c);
1761 b.connections.add(c);
1765 b.client.connections.add(c);
1766 c.startAssociationIfNeeded();
1785
1786 if ((flags&Context.BIND_AUTO_CREATE) != 0) {
1787 s.lastActivity = SystemClock.uptimeMillis();
1788 if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
1789 permissionsReviewRequired) != null) {}
1792 }
1815 if (s.app != null && b.intent.received) {
1816 // Service is already running, so we can immediately
1817 // publish the connection.
1818 try {
1819 c.conn.connected(s.name, b.intent.binder, false);
1820 } catch (Exception e) {}
1832 }else if (!b.intent.requested) {
1833 requestServiceBindingLocked(s, b.intent, callerFg, false);
1834 }
1838 } finally {}
1842 return 1;
这个方法里有两处重点。1788行将对端的Service拉起来,如果他的进程不存在的话,这是Service启动流程,可以看这篇文章:startServie启动过程
由于我们的service进程新启动的,所以不会走入1815行分支,而是走到1833行的Service的onBind流程,明显,这又是一次binder跨进程通信。ams-->Service进程。
2.3 requestServiceBindingLocked:ams-->服务提供者app进程
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
2294 private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
2295 boolean execInFg, boolean rebind) throws TransactionTooLargeException {
2302 if ((!i.requested || rebind) && i.apps.size() > 0) {
2303 try {
2306 r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
2307 r.app.getReportedProcState());
2306行,r.app.thread是指向服务提供者app进程的IApplicationThread的Stub.Proxy对象,ams通过这个对象与服务提供者app进程通信。
所以scheduleBindService方法就调用我们直接走到服务提供者app进程进程里。
out/soong/.intermediates/frameworks/base/framework/android_common/gen/aidl/frameworks/base/core/java/androi
d/app/
182 public static abstract class Stub extends android.os.Binder implements android.app.IApplicationThread{
437 @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
658 case TRANSACTION_scheduleBindService:
659 {
660 data.enforceInterface(descriptor);
661 android.os.IBinder _arg0;
662 _arg0 = data.readStrongBinder();
663 android.content.Intent _arg1;
664 if ((0!=data.readInt())) {
665 _arg1 = android.content.Intent.CREATOR.createFromParcel(data);
666 }
667 else {
668 _arg1 = null;
669 }
670 boolean _arg2;
671 _arg2 = (0!=data.readInt());
672 int _arg3;
673 _arg3 = data.readInt();
674 this.scheduleBindService(_arg0, _arg1, _arg2, _arg3);
675 return true;
676 }
662行_arg0是一个BinderProxy对象,readStrongBinder的实现我们之前分析过,可以回顾前文。
674行,这里的this是谁?是ApplicationThread类实例,服务端进程里的。走进去
frameworks/base/core/java/android/app/ActivityThread.java
954 public final void scheduleBindService(IBinder token, Intent intent,
955 boolean rebind, int processState) {
956 updateProcessState(processState, false);
957 BindServiceData s = new BindServiceData();
958 s.token = token;
959 s.intent = intent;
960 s.rebind = rebind;
965 sendMessage(H.BIND_SERVICE, s);
966 }
此时我们处于binder线程,所以需要通过handler将消息发给mH,在主线程中处理组件的生命周期onBind方法。
frameworks/base/core/java/android/app/ActivityThread.java
1853 public void handleMessage(Message msg) {
1855 switch (msg.what) {
1878 case BIND_SERVICE:
1880 handleBindService((BindServiceData)msg.obj);
1882 break;
frameworks/base/core/java/android/app/ActivityThread.java
3970 private void handleBindService(BindServiceData data) {
3971 Service s = mServices.get(data.token);
3975 try {
3976 data.intent.setExtrasClassLoader(s.getClassLoader());
3977 data.intent.prepareToEnterProcess();
3978 try {
3979 if (!data.rebind) {
3980 IBinder binder = s.onBind(data.intent);
3981 ActivityManager.getService().publishService(
3982 data.token, data.intent, binder);
3983 }
3988 } catch (RemoteException ex) {}
3991 } catch (Exception e) {}
3999 }
3980行,调用Service的onBind生命周期方法,我们知道,aidl的服务端Service实现了这个方法,并且return 一个准备好的ISecondary.Stub mSecondaryBinder对象。所以这里的3980行IBinder binder就是一个Binder对象。
下一步3981行,将Service发布到ams。
2.4 publishService:服务提供者app进程-->ams
现在走到ams所在进程里
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
14112 public void publishService(IBinder token, Intent intent, IBinder service) {
14118 synchronized(this) {
14122 mServices.publishServiceLocked((ServiceRecord)token, intent, service);
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
1845 void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
1847 try {
1850 if (r != null) {
1851 Intent.FilterComparison filter
1852 = new Intent.FilterComparison(intent);
1853 IntentBindRecord b = r.bindings.get(filter);
1854 if (b != null && !b.received) {
1855 b.binder = service;
1856 b.requested = true;
1857 b.received = true;
1858 ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
1859 for (int conni = connections.size() - 1; conni >= 0; conni--) {
1860 ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
1861 for (int i=0; i<clist.size(); i++) {
1862 ConnectionRecord c = clist.get(i);
1863 if (!filter.equals(c.binding.intent.intent)) {
1870 continue;
1871 }
1873 try {
1874 c.conn.connected(r.name, service, false);
1875 } catch (Exception e) {
1874行我们见过,在onBind流程那里,只不过没有走进去。现在我们onBind走完了,走到publish的流程。正是在此处触发的发起者app进程里onServiceConnected回调方法。
1859行和1861行是两层遍历,遍历什么呢?遍历ArrayMap<IBinder, ArrayList<ConnectionRecord>>
这个map,找到和我们的intent匹配的ConnectionRecord。c.conn对象存放了IServiceConnection
类型Stub.Proxy对象。还记得new InnerConnection(this)吗,可以回顾下前文创建流程。
2.5 最后的旅途:ams-->发起者app进程onServiceConnected回调
走进c.conn.connected方法的服务端实现,现在我们把视角转回发起者app进程
out/soong/.intermediates/frameworks/base/framework/android_common/gen/aidl/frameworks/base/core/java/androi
d/app/IServiceConnection.java
20 public static abstract class Stub extends android.os.Binder implements android.app.IServiceConnection{
67 @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
77 case TRANSACTION_connected:
78 {
79 data.enforceInterface(descriptor);
80 android.content.ComponentName _arg0;
81 if ((0!=data.readInt())) {
82 _arg0 = android.content.ComponentName.CREATOR.createFromParcel(data);
83 }
84 else {
85 _arg0 = null;
86 }
87 android.os.IBinder _arg1;
88 _arg1 = data.readStrongBinder();
89 boolean _arg2;
90 _arg2 = (0!=data.readInt());
91 this.connected(_arg0, _arg1, _arg2);
92 return true;
93 }
98 }
91行的this是InnerConnection对象,所以走进LoadedApk.java的内部类ServiceDispatcher的内部类InnerConnection的connected方法。
frameworks/base/core/java/android/app/LoadedApk.java
1776 private static class InnerConnection extends IServiceConnection.Stub {
1778 final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
1779
1780 InnerConnection(LoadedApk.ServiceDispatcher sd) {
1781 mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
1782 }
1784 public void connected(ComponentName name, IBinder service, boolean dead)
1785 throws RemoteException {
1786 LoadedApk.ServiceDispatcher sd = mDispatcher.get();
1787 if (sd != null) {
1788 sd.connected(name, service, dead);
1788行,此处sd是其外部类ServiceDispatcher,继续走
frameworks/base/core/java/android/app/LoadedApk.java
1878 public void connected(ComponentName name, IBinder service, boolean dead) {
1879 if (mActivityExecutor != null) {
1880 mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
1881 } else if (mActivityThread != null) {
1882 mActivityThread.post(new RunConnection(name, service, 0, dead));
1883 } else {
1884 doConnected(name, service, dead);
1885 }
1886 }
当初ServiceDispatcher类实例化时,参数ActivityExecutor是null,Handler不是,所以走到1882行。
frameworks/base/core/java/android/app/LoadedApk.java
1970 private final class RunConnection implements Runnable {
1978 public void run() {
1979 if (mCommand == 0) {
1980 doConnected(mName, mService, mDead);
1981 } else if (mCommand == 1) {
frameworks/base/core/java/android/app/LoadedApk.java
1898 public void doConnected(ComponentName name, IBinder service, boolean dead) {
1899 ServiceDispatcher.ConnectionInfo old;
1900 ServiceDispatcher.ConnectionInfo info;
1901
1902 synchronized (this) {
1914 if (service != null) {
1916 info = new ConnectionInfo();
1917 info.binder = service;
1918 info.deathMonitor = new DeathMonitor(name, service);
1919 try {
1920 service.linkToDeath(info.deathMonitor, 0);
1921 mActiveConnections.put(name, info);
1922 } catch (RemoteException e) {}
1929 } else {
1931 mActiveConnections.remove(name);
1932 }
1937 }
1946 // If there is a new viable service, it is now connected.
1947 if (service != null) {
1948 mConnection.onServiceConnected(name, service);
1949 } else {
1918行和1920行是注册死亡监听,又是一次binder调用,此处按下不表,以后分析。
关键点是1948行,调用我们activity里ServiceConnection mSecondaryConnection的回调方法onServiceConnected。这里的参数name是服务端Service的ComponentName,service是BinderProxy对象,是服务提供者app进程的代理。
回来了O(∩_∩)O哈哈~。
至此,activity就可以使用这个ISecondary.Stub.Proxy(BinderProxy)对象同进程外的Service通信了,binder-ipc通信。
总结
在发起者app进程里:
1、new一个ServiceConnection实例,实现回调方法。(普通回调接口无关binder)
2、在bindService的流程里ContextImpl中,new一个IServiceConnection类型Binder对象,填入上面的回调对象和其他关键信息。
3、通过IActivityManager的接口bindIsolatedService跨进程将上面准备的东西发给AMS。
在AMS进程里:
4、AMS进程里处理这些信息,找到提供Service的app进程(进程不在则创建),走入其onBind方法
在Service提供者app进程里:
6、收到AMS的数据,走进ApplicationThread-->handleBindService方法。在这个方法里
6.1执行onBind回调方法,拿到一个ISecondary.Stub mSecondaryBinder,Binder对象
6.2发起binder通信ActivityManager.getService().publishService,传送这个Binder对象给ams
在AMS的进程里:
7、在publishServiceLocked方法里,发起binder通信,c.conn.connected,使用先前发起者APP进程传送,ams通过readStrongBinder拿到的IServiceConnection的BinderProxy对象,去沟通发起者APP进程,走入其onServiceConnected回调方法
在发起者app进程里:
8、执行了onServiceConnected回调方法,我们在这里会拿到代表Service提供者app进程的BinderProxy实例,经ISecondary.Stub.asInterface方法处理,得到一个Stub.Proxy(BinderProxy)对象。return new com.example.android.apis.app.ISecondary.Stub.Proxy(obj);
现在一个漫长的binder调用去去回回结束了,我们的请求终于得到了答复。
这样我们就可以使用这个Stub.Proxy(BinderProxy)对象直接和Service提供者app进程的Service进行binder通信了。