美文网首页
Android跨进程通信IPC之21--binderServic

Android跨进程通信IPC之21--binderServic

作者: 凯玲之恋 | 来源:发表于2018-08-27 14:10 被阅读195次

    移步系列Android跨进程通信IPC系列
    app是没有权限向ServiceManager注册service的。
    bindService要负责找到service的代理binder并传递到app进程。这期间涉及到binder实体的跨进程传输,也就是所谓的匿名binder。因为这些binder并没有在ServiceManager中注册。

    • 对于通过startService启动的Service,只能作为接收方,启动方可以通过onStartCommand来传递参数。这种启动方式其可以有多个启动者,不过在销毁的时候,一旦有任意一个启动调用了stopService或者自身stopSelf后,该Service就会停止,而启动者的生命周期无法影响到该Service
    • 对于通过bindService启动的Service,其和启动方有个“绑定”的过程,启动方可以通过Service的binder引用来调用Service的方法。不过这种方式Service的生命周期会关联着启动方的,启动方生命周期结束后,会默认unbindService来结束。
    • bindService的过程要比startService的过程复杂一些,因为bingService之后,发起者可以跨进程调用service的某些方法。
    • 使用bindService启动的service的开发过程中一般都会借助aidl。

    1 ServiceConnection

    • 当一个app进程bindService()时,它需要先准备好一个实现了ServiceConnection接口的对象。
    • ServiceConnection的定义如下:
    public interface ServiceConnection {
    
        public void onServiceConnected(ComponentName name, IBinder service);
    
        public void onServiceDisconnected(ComponentName name);
    }
    
    • bindService()见名知意即绑定服务,建立一个逻辑连接。当连接建立之后,AMS会回调ServiceConnection接口对象的onServiceConnected()方法。把远端service的代理binder传递过来,这样就可以通过这个代理binder跨进程调用service中的方法了。

    • ServiceConnection接口的onServiceDisconnected()方法并不会在unbindService()操作断开逻辑连接时执行。而是在远端service进程终止时,AMS才会回调onServiceDisconnected()。

    • 另外要注意的是纵然service进程的因为某些原因被终止,之前的绑定在该service上的逻辑连接仍处于激活状态,当service再次运行的时候,AMS会回调onServiceConnected。

    • bindService的过程,我们要关心的binder有两个

      • 一个是远端service的binder,有了它客户端进程才能跨进程调用service中的方法;
      • 另一个是binder是客户端传递给AMS的,有了这个binder,AMS才会在逻辑连接建立之后,跨进程调用客户端中的ServiceConnection接口的对象中的onServiceConnected()和onServiceDisconnected()。
    • 另外bindservice在建立连接时,如果发现service还没启动,会根据flag是否设置BIND_AUTO_CREATE,决定是否启动这个service。

    • 同startService()一样,bindService()也是一个异步的过程,也就是说当该方法返回时,逻辑连接很可能还没有建立,通俗的说就是该方法返回时,onServiceConnected()方法很可能还没执行。

    2 bindService的过程

    整个过程大体上是这样的:

    1. 检查要绑定的额service是否启动,没有的话,要先启动service,然后执行service的生命周期方法。
    2. 执行绑定,先将远端service的binder传递到AMS中,然后AMS在将其传递到客户端组件进程中

    3 bindService的过程分析

    ContextImpl.bindService():

    public boolean bindService(Intent service, ServiceConnection conn,
                int flags) {
            warnIfCallingFromSystemProcess();
            return bindServiceCommon(service, conn, flags, Process.myUserHandle());
        }
    

    ContextImpl.bindServiceCommon():

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
                UserHandle user) {
            IServiceConnection sd;
          ................
            if (mPackageInfo != null) {
              //从LoadedApk中拿到一个binder实体
              //首先会把ServiceConnection转成Binder对象
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                        mMainThread.getHandler(), flags);
            } else {
                throw new RuntimeException("Not supported in system context");
            }
            //  Android5.0之后不允许使用隐式调用
            // 这里对传入的intetn进行检查,如果是android5.0以上,
            //是隐式intent的话抛出异常
            validateServiceIntent(service);
            try {
                IBinder token = getActivityToken();
                ....................
                service.prepareToLeaveProcess();
                // 向AMS发起跨进程调用其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 new RuntimeException("Failure from system", e);
            }
        }
    
    • 这里便引出了第一个binder:IServiceConnection sd。
    • 这是一个实体binder,也就是说跨进程传输到AMS进程之后,AMS进程中最终得到的是一个代理binder。而且binder实体跨进程传输过程中,会在binder驱动层中为其创建相应的数据结构,这样binder便在binder驱动中扎根了。
    • 那么这个binder实体最初是从哪里来的呢?

    3.1 LoadedApk.getServiceDispatcher()

    • 此处的mPackageInfo是用户进程里和apk对应的LoadedApk对象
    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
               Context context, Handler handler, int flags) {
           synchronized (mServices) {
               LoadedApk.ServiceDispatcher sd = null;
                // 先从LoadedApk.mService中查找,
                // 看看发起绑定操作的组件是否已经存在一个用来处理传入的
                //ServiceConnection接口对象的ServiceDispatcher
               ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
               if (map != null) {
                  // 存在的话直接返回这个ServiceDispatcher
                   sd = map.get(c);
               }
               if (sd == null) {
                  // 不存在的话创建一个ServiceDispatcher
                  // 这里要注意第三个参数handler,是主线程的handler
                  // 保存在了ServiceDispatcher.mActivityThread中
                   sd = new ServiceDispatcher(c, context, handler, flags);
                   if (map == null) {
                       map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                       mServices.put(context, map);
                   }
                    // 将创建的ServiceDispatcher缓存到LoadedApk.mService
                   map.put(c, sd);
               } else {
                   sd.validate(context, handler);
               }
               return sd.getIServiceConnection();
           }
       }
    
    
    • LoadedApk.ServiceDispatcher从它的名字中可以看出它类似于一个Dispatcher的作用,这个类的作用就是当client bind某个service成功之后,负责向client分配service IBinder的;以及当client unbind service时,负责通知client unbind service的状态。
    • 对于ServiceDispatcher的管理,是以apk,即package为载体的,也就是说对于某个package,定义在其中的component如果请求去bind一个service,那么LoadedApk将为这个component分配一个ServiceDispatcher。
    • component每请求bind一个service,都会为其指定一个ServiceConnection接口的对象,同一component组件内,LoadedApk将会为这个ServiceConnection接口的对象分配一个唯一的ServiceDispatcher。
    • LoadApk会为每个context保存一个key为ServiceConnection、value为ServiceDispatcher的map,这样尽量做到了ServiceConnection对应的Binder对象的复用。如果没有可复用,则把ActivityThread的主Handler和ServiceConnection对应起来,这样在复用的时候,ServiceDispatcher会调用validate方法检查handler为当前主线程handler,保证不错乱。
    • 每个app进程中是可以加载多个apk包的,记录在ActivityThread.mPackages,整个关系如下所示:


      2780242-c11bbd267ebbd52a.png
    • packages中的组件component,例如某个activity中使用bindService去绑定一个service时,都需要提供一个实现ServiceConnection接口的对象,每一个这样的对象都会与一个ServiceDispatcher对象绑定。
    • LoadedApk.mServices中存储了本package中所有组件中所有的ServiceDispatcher。它是一个ArrayMap,key是context,实际上就是组件,因为组件继承自Context。
    • value又是一个ArrayMap,因为一个组件中使可以绑定多个service的嘛。这个map的可以是实现了ServiceConnection接口的对象,value是与之绑定的ServiceDispatcher对象。

    3.1.1 ServiceDispatcher

    static final class ServiceDispatcher {
        // 一个binder实体对象
        private final ServiceDispatcher.InnerConnection mIServiceConnection;
        // 逻辑连接建立时,执行的回调接口对象
        private final ServiceConnection mConnection;
        // 所在的组件,即client
        private final Context mContext;
        // 进程主线程即UI线程中的handler
        private final Handler mActivityThread;
        private final ServiceConnectionLeaked mLocation;
        // 绑定service时,传入的flag,例如BIND_AUTO_CREATE
        private final int mFlags;
    
        private RuntimeException mUnbindLocation;
        private boolean mDied;
        private boolean mForgotten;
        // 因为一个ServiceConnection可以被bindService方法多次调用,用来启动不同的service,
        // 那么ServiceDispatcher中自然要存储这些连接信息了:
        // ConnectionInfo很简单只有两个属性成员
        // IBinder binder;远端service的引用binder
        // IBinder.DeathRecipient deathMonitor; 远端binder的死亡通知方法
        private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
           = new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
    ................
      }
    

    ServiceDispatcher.InnerConnection是一个继承自 IServiceConnection.Stub的binder实体类:

    3.1.2 InnerConnection

    private static class InnerConnection extends IServiceConnection.Stub {
                final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
    
                InnerConnection(LoadedApk.ServiceDispatcher sd) {
                    mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
                }
    
                public void connected(ComponentName name, IBinder service) throws RemoteException {
                    LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                    if (sd != null) {
                        sd.connected(name, service);
                    }
                }
    }
    

    其中ServiceDispatcher.connected():

    public void connected(ComponentName name, IBinder service) {
        // 这里的mActivityThread是主线程的handler
        // 也就是说,实际上ServiceConnection中的回调是在主线程中执行的
          if (mActivityThread != null) {
              mActivityThread.post(new RunConnection(name, service, 0));
          } else {
              doConnected(name, service);
          }
    }
    
    • ServiceDispatcher.InnerConnection是一个继承自 IServiceConnection.Stub的binder实体类,从其名字上来看是一个内部连接,该怎么理解呢?
    • 因为bindService()传递的ServiceConnection接口对象,并没有跨进程传输到AMS中,从bindServiceCommon()方法可以看出最终传递到AMS的是LoadedApk.getServiceDispatcher()返回的ServiceDispatcher.InnerConnection。AMS通过它跨进程间接调用ServiceConnection中的方法。
    • 因为一个ServiceConnection可以在同一个客户端组件内被bindService方法多次调用,用来绑定不同的service,那么ServiceDispatcher中自然要存储这些使用同一个serviceConnection绑定的service的连接信息了——ServiceDispatcher.mActiveConnections。

    3.1.3 ConnectionInfo

    • 它是ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>类型对象,
    private static class ConnectionInfo {
      // 远端service的代理binder
      IBinder binder;
      // 远端service 死亡通知回调
      IBinder.DeathRecipient deathMonitor;
    }
    

    3.2 AMS中绑定service过程

    • ContextImpl.bindServiceCommon()方法中得到ServiceConnection对应的ServiceDispatcher对象之后,便向AMS发起请求,跨进程调用AMS.bindService():
    public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String callingPackage,
            int userId) throws TransactionTooLargeException {
        enforceNotIsolatedCaller("bindService");
    
        ..............
        synchronized(this) {
          // mServices是ActiveService
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
        }
    }
    
    • AMS.bindService()对参数做了简单检查之后,又调用ActiveService.bindServiceLocked()方法。
    • 这里先对ActiveService.bindServiceLocked()方法的几个重要参数做一些说明:
    int bindServiceLocked(
             IApplicationThread caller, // 客户端进程ActivityThread.mAppThread的代理binder
             IBinder token,
             Intent service,// 客户端绑定service时的intent
             String resolvedType,
             IServiceConnection connection, //客户端进程的中ServiceDispatcher.InnerConnection的代理binder
             int flags,
             String callingPackage, int userId) throws TransactionTooLargeException {
    

    ActiveService.bindServiceLocked()startService的ActiveService.startServiceLocked()方法中有很多相似的逻辑。

    • 要先查找要启动的servicee在AMS中的代表ServiceRecord是否存在,存在的话,意味着service已经启动;不存在的话,要创建一个ServiceRecord对象,这些操作还是由retrieveServiceLocked()方法负责的。
    • 然后通过bringUpServiceLocked()调用service的生命周期方法。只不过bindservice除了这两个基本操作外,还要执行绑定操作,即将service的binder传跨进程传输到app客户端组件中。

    3.2.1 bindServiceLocked绑定前的准备工作

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
               String resolvedType, IServiceConnection connection, int flags,
               String callingPackage, int userId) throws TransactionTooLargeException {
      ...............
      // 得到service对应的ServiceRecord
      ServiceLookupResult res =
          retrieveServiceLocked(service, resolvedType, callingPackage,
                  Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
      ServiceRecord s = res.record;
      ....
      // 根据传入的intent和发起者进程,查找到一个合适的AppBindRecord对象,
      // 查找不到就创建一个,下面会介绍规则
      AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
      // 为本次连接创建ConnectionRecord对象
      ConnectionRecord c = new ConnectionRecord(b, activity,
              connection, flags, clientLabel, clientIntent);
      // 客户端进程的中ServiceDispatcher.InnerConnection的代理binder
      IBinder binder = connection.asBinder();
      // 将创建的逻辑连接对象ConnectionRecord,记录在ServiceRecord中
      ArrayList<ConnectionRecord> clist = s.connections.get(binder);
      if (clist == null) {
          clist = new ArrayList<ConnectionRecord>();
          s.connections.put(binder, clist);
      }
      clist.add(c);
      // 同时记录在AppBindRecord.connections中
      b.connections.add(c);
      // AppBindRecord.client是ProcessRecord,代表客户端的进程
      b.client.connections.add(c);
      .......
      // 除了ServiceRecord.connections记录了该service连接信息外
      // ActiveService.mServiceConnections记录了当前系统中所有的连接信息
      // 所以也要将创建的连接对象,加入ActiveService.mServiceConnections
      clist = mServiceConnections.get(binder);
      if (clist == null) {
          clist = new ArrayList<ConnectionRecord>();
          mServiceConnections.put(binder, clist);
      }
      clist.add(c);
     ............
    

    在前面关于service机制介绍的文章中提到过ServiceRecord中有一些数据成员是bindservice时用到的:

    final class ServiceRecord extends Binder {
        ...............
        final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
                    = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();// IBinder -> ConnectionRecord of all bound clients
        // service所在的进程
        ProcessRecord app;      // where this service is running or null.
    }
    

    AMS描述绑定service时的intent的IntentBindRecord类

    • app客户端组件中发起绑定service操作时,一定要用到intent,一般是显示的intent(android 5.0 之后要求必须是显示intent),而且只设置intetn.mComponent字段。
    • AMS中对绑定操作传入的intent是用Intent.FilterComparison类来描述,也就是说当客户端绑定service时使用的intent指定的参数都一致的话,AMS会将其看做是同一类intent。
    • AMS会为这类intent为其创建一个IntentBindRecord对象。
    final class IntentBindRecord {
        // 绑定的service在AMS中的代表
        final ServiceRecord service;
        /** The intent that is bound.*/
        final Intent.FilterComparison intent; //
        /** All apps that have bound to this Intent. */
        final ArrayMap<ProcessRecord, AppBindRecord> apps
                = new ArrayMap<ProcessRecord, AppBindRecord>();
        // service的binder代理binder
        IBinder binder;
    

    其中IntentBindRecord.service 最终会保存绑定的service的ServiceRecord;

    • IntentBindRecord.intent 就是前面所说的绑定service时设定的参数一致的intent在AMS中的表示Intent.FilterComparison对象;
    • IntentBindRecord.apps用来记录所有使用该类intetn绑定同一个service的客户端信息。key是客户端进程ProcessRecord,value是AppBindRecord。因为不同的客户端可能使用相同的intent参数来绑定同一个service,所以IntentBindRecord要记录下这些客户端信息;
    • IntentBindRecord.binder最终保存service的代理binder;
    • AMS通过ServiceRecord.retrieveAppBindingLocked()方法为判断是否为传入的intent创建一个IntentBindRecord:

    3.2.1.1 ServiceRecord.retrieveAppBindingLocked()

    public AppBindRecord retrieveAppBindingLocked(
               Intent intent,// 客户端发起绑定操作时传入的intent
               ProcessRecord app // 客户端组件进程) {
           // 为传入的intent创建一个Intent.FilterComparison
           Intent.FilterComparison filter = new Intent.FilterComparison(intent);
           // 查找传入的intent是否已经有IntentBindRecord
           IntentBindRecord i = bindings.get(filter);
           if (i == null) {
              // 没有的话创建
               i = new IntentBindRecord(this, filter);
               //并缓存到ServiceRecord.bings中
               bindings.put(filter, i);
           }
           // 查找客户端的组件是否已经绑定过该service
           AppBindRecord a = i.apps.get(app);
           // 绑定过的话,返回找到的AppBindRecord
           if (a != null) {
               return a;
           }
           // 没有的话创建一个AppBindRecord对象
           a = new AppBindRecord(this, i, app);
           // 并缓存到IntentBindRecord.apps中
           i.apps.put(app, a);
           return a;
       }
    
    

    retrieveAppBindingLocked()另一个主要作用是查找并创建AppBindRecord对象。
    AMS用于描述绑定service的客户端整体信息的AppBindRecord类

    • 对于一个Service来说,有多少app客户端进程和它建立了绑定关系,就会有多少个AppBindRecord对象。一个app客户端进程里可以有多个地方发起绑定动作,所以AppBindRecord里需要用一个ArraySet<ConnectionRecord>记录下每个绑定动作对应的逻辑连接对象。
    final class AppBindRecord {
        // 所在的service
        final ServiceRecord service;    // The running service.
        // 客户端发起的bindservice时传入的intent,AMS会为其创建一个对应的IntentBindRecord
        final IntentBindRecord intent;  // The intent we are bound to.
        // 客户端进程
        final ProcessRecord client;     // Who has started/bound the service.
        // 客户端所在的app,其他组件绑定该service的逻辑连接
        final ArraySet<ConnectionRecord> connections = new ArraySet<>();
                                        // All ConnectionRecord for this client.
    
    • AppBindRecord.service 用于描述客户端绑定的service;
    • AppBindRecord.intent 用于描述客户端绑定该service时使用的intent;
    • AppBindRecord.client 用于描述客户端的进程;
    • AppBindRecord.connections 用于描述客户端中所有组件绑定该service时创建的逻辑连接;

    AMS通过ServiceRecord.retrieveAppBindingLocked()来查找并创建一个合适的AppBindRecord对象。

    3.2.1.2 为本次连接创建ConnectionRecord对象

    • AMS为每次绑定过程中创建的连接分配一个ConnectionRecord类型对象.
    • ConnectionRecord用来描述一个连接信息,即绑定信息,要对客户端和service端进行描述。
    ConnectionRecord用来描述一个连接信息,即绑定信息,要对客户端和service端进行描述。
    
    • ConnectionRecord.binding代表这个连接所在的AppBindRecord.
    • ConnectionRecord.coon是一个binder代理对象,其实体binder是app进程中ServiceDispatcher.mIServiceConnection。用来回调客户端进程中当连接成功建立时的回调方法。
    • 客户端组件在bindservice时,都要创建一个实现ServiceConnection接口的对象,每个这样的对象在客户端组件所在的LoadedApk中都会分配一个ServiceDispatcher对象,这个对象用于处理所有使用该ServiceConnection接口的对象绑定service的客户端组件的回调方法的执行。
    • ConnectionRecord.flags 用于描述绑定该service时,指定的flags,例如BIND_AUTO_CREATE.
    • AMS创建的ConnectionRecord对象会存储在ServiceRecord.connections成员变量中.
    • ServiceRecord.connections是一个map,key是ServiceDispatcher.InnerConnection的代理binder,value是ArrayList<ConnectionRecord>类型的。
    • 因为客户端进程中一个Component中可能使用同一个ServiceConnection接口对象来多次绑定同一个service,因为每次绑定都会创建一个ConnectionRecord对象,那么这些ConnectionRecord需要使用ArrayList<ConnectionRecord>来保存。
    • value中ConnectionRecord.IServiceConnection实际上一致与key是一致的。
    • AMS除了将这个"逻辑连接"ConnectionRecord对象,记录在ServiceRecord.connections中外,还要向至少下面的几处位置做记录:
    1. 客户端所在的进程在AMS中的代表ProcessRecord.connections
    2. ActiveService.mServiceConnections,这里面记录了AMS中所有app的service的连接
    3. AppBindRecord.connections中

    之所以要在这么多地方做记录,可能是为了在不同的场合下迅速查找到连接吧。

    了解了以上内容后,就可以通过下图简明的描述客户端进程和AMS之间的关系:


    2780242-c11bbd267ebbd52a.png

    3.2.2 bringUpServiceLocked 执行service生命周期方法

    在做好前面的准备工作之后,binderservice()就开始准备与Service建立连接了。那么自然要先对service进行一些操作,说白了就是执行service的生命周期方法,这是由bringUpServiceLocked()方法来负责的。

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
               String resolvedType, IServiceConnection connection, int flags,
               String callingPackage, int userId) throws TransactionTooLargeException {
    .................
    if ((flags&Context.BIND_AUTO_CREATE) != 0) {
        s.lastActivity = SystemClock.uptimeMillis();
        // 只有当要绑定的service所在的进程还启动的时候,该方法返回非null
        // 因为启动进程需要一段时间,所以就要先退出来
        // 这里暂时假设service所在的进程已经启动
        if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
            return 0;
        }
    }
    ....................
    

    此时可以分为两大情况:

    • 1.service进程还没启动
    • 2.service进程已经启动,此时有可分为两种情况:service还没启动和service已经运行。bringUpServiceLocked()方法依据ServiceRecord.app区分以上两大情况:
      • 1.ServiceRecord.app不为null,而且ServiceRecord.app.thread也不为null,预示着service已经运行了,但是这时候并不会向startService()启动service那样跨进程调用service.onStartCommand()生命周期方法,因为bindService启动service时没有将信息记录到ServiceRecord.pendingStarts。
      • .ServiceRecord.app为null,说明ServiceRecord还没有和service所在的进程关联。此时在依据ServiceRecord.processName,也就是service要求运行在的进程的名字,在AMS中查找是否有这样的进程存在,如果有的话,只需要启动service,也就是在找到的进程中创建service对象,并执行service.onCreate()生命周期方法。

    如果没有在AMS中找到名字为ServiceRecord.processName的进程,那么就要先创建进程了,这里不考虑这种情况。

    3.2.2.1 绑定service bindServiceLocked

    绑定实际上就是想办法拿到service的binder,并将其传递到客户端组件进程,另外还要对前面准备工作期间创建的数据结构设置相关的字段。

    这部分代码如下:

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
               String resolvedType, IServiceConnection connection, int flags,
               String callingPackage, int userId) throws TransactionTooLargeException {
    .................
    // b.intent.received为true,表明已经拿到了service的binder
    f (s.app != null && b.intent.received) {
            // Service is already running, so we can immediately
            // publish the connection.
            try {
                // 这里就可以直接远程调用客户端组件中onServiceConnected()方法将service的binder传递过去了
                c.conn.connected(s.name, b.intent.binder);
            } catch (Exception e) {
                Slog.w(TAG, "Failure sending service " + s.shortName
                        + " to connection " + c.conn.asBinder()
                        + " (in " + c.binding.client.processName + ")", e);
            }
    
            // If this is the first app connected back to this binding,
            // and the service had previously asked to be told when
            // rebound, then do so.
            if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                requestServiceBindingLocked(s, b.intent, callerFg, true);
            }
        } else if (!b.intent.requested) {
            // 之前没绑定过,那么就调用下面的额方法进行绑定
            requestServiceBindingLocked(s, b.intent, callerFg, false);
        }
    
        getServiceMap(s.userId).ensureNotStartingBackground(s);
    
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
    

    这里分两种情况:

    • 1.客户端组件之前已经绑定过该service,现在这个组件又要再次绑定,也就是客户端同一组件重复绑定
    • 2.客户端组件首次绑定该service
      这两种情况的区分是依据AppBindRecord.intent,即AMS为bindservice()传入的intent分配的IntentBindRecord对象来决定的。
    final class IntentBindRecord {
      /** Binder published from service. */
      IBinder binder;
      /** Set when we have initiated a request for this binder. */
      boolean requested;
      /** Set when we have received the requested binder. */
      boolean received;
      /** Set when we still need to tell the service all clients are unbound. */
      boolean hasBound;
    }
    
    • IntentBindRecord.received为true,表明IntentBindRecord.binder已经指向远端service的binder;
    • IntentBindRecord.requested为false,表明IntentBindRecord.binder还没有指向远端service的binder;

    这里分析第二种情况,时序图如下:


    2780242-52921787ad1c7e6b.png
    • requestServiceBindingLocked()方法中跨进程调用service所在的进程中方法,最终会导致service.onBind()执行,该方法返回service的实体binder。
    • 这里有一点要贴别注意,那只要service被某个客户端组件绑定过了,就不会再执行service.onBind()方法了,原因就在requestServiceBindingLocked()方法中:

    3.2.2.2 requestServiceBindingLocked

    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
                boolean execInFg, boolean rebind) throws TransactionTooLargeException {
    
            ..............
            // 传入的rebind为false还是true,取决于AMS调用service.onUnbind()返回值
            // 如果希望客户端下一次绑定到服务时接收 onRebind() 调用(而不是接收 onBind() 调用),onUnbind()返回true
            // service首次被绑定时,rebind肯定为false
            if ((!i.requested || rebind) && i.apps.size() > 0) {
                try {
                    bumpServiceExecutingLocked(r, execInFg, "bind");
                    r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                    // 跨进程调用service所在进程的scheduleBindService()方法,执行绑定操作,这是一个异步方法,会立即返回
                    r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                            r.app.repProcState);
                    if (!rebind) {
                        // 只要service被绑定过了,IntentBindRecord.requested就会被设置为true
                        i.requested = true;
                    }
                    i.hasBound = true;
                    i.doRebind = false;
                } catch (TransactionTooLargeException e) {
                  .................
                } catch (RemoteException e) {
                  .................
                }
            }
            return true;
        }
    
    • 通过以上代码可知,只要service被绑定过一次,那么IntentBindRecord.requested救回被设置为true,其他客户端组件再次绑定的时候,由于if条件为假,所以不会再次跨进程调用service进程中的scheduleBindService()方法,也就不会调用service.onBind()方法了。
    • 因为bindservice时传入的intent一般都是显示intent,不会设置其他参数,所以只要客户端绑定的是同一个service,那么在AMS中只会有一个IntentBindRecord对象。
    • 除非该servcie绑定过之后,所有绑定它的客户端组件都执行了unbindService(),那么最终会导致service.onUnbind()方法执行,如果该方法返回了true,那么下次再有客户端组件绑定该service时,rebind会被设置为true,这导致不会调用service.onBind(),而是调用service.rebind()方法。
    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 {
                        // 传入flase时执行onBind()
                        if (!data.rebind) {
                            IBinder binder = s.onBind(data.intent);
                            ActivityManagerNative.getDefault().publishService(
                                    data.token, data.intent, binder);
                        } else {
                          // 传入true时执行onRebind()
                            s.onRebind(data.intent);
                            ActivityManagerNative.getDefault().serviceDoneExecuting(
                                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                        }
                        ensureJitEnabled();
                    } catch (RemoteException ex) {
                    }
                } catch (Exception e) {
                    if (!mInstrumentation.onException(s, e)) {
                        throw new RuntimeException(
                                "Unable to bind to service " + s
                                + " with " + data.intent + ": " + e.toString(), e);
                    }
                }
            }
        }
    

    这里考虑首次绑定时的情况,所以rebind肯定为false,那么service调用过onBind()之后,又通过AMS的代理,跨进程调用AMS的publishService()将service的binder传递到AMS中,然后在传递到客户端。

    3.2.2.3 publishServiceLocked

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    .......
    IntentBindRecord b = r.bindings.get(filter);
    // 初次绑定
    if (b != null && !b.received) {
          // 保存service的代理binder
          b.binder = service;
          // 设置下面的两个标志为true
          b.requested = true;
          b.received = true;
          // 一般情况下,首次绑定时,connections.size为1
          for (int conni=r.connections.size()-1; conni>=0; conni--) {
               ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
               for (int i=0; i<clist.size(); i++) {
                   ConnectionRecord c = clist.get(i);
                   if (!filter.equals(c.binding.intent.intent)) {
                       if (DEBUG_SERVICE) Slog.v(
                               TAG_SERVICE, "Not publishing to: " + c);
                       if (DEBUG_SERVICE) Slog.v(
                               TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);
                       if (DEBUG_SERVICE) Slog.v(
                               TAG_SERVICE, "Published intent: " + intent);
                       continue;
                   }
                   if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
                   try {
                       // 将service的binder传递到客户端
                       c.conn.connected(r.name, service);
                   } catch (Exception e) {
                       Slog.w(TAG, "Failure sending service " + r.name +
                             " to connection " + c.conn.asBinder() +
                             " (in " + c.binding.client.processName + ")", e);
                   }
               }
           }
       }
    ...........
    

    3.2.2.4 bindServiceLocked

    那么当首次绑定之后,又有其他客户端组件来绑定这个service,那么在bindServiceLocked()方法中,会直接跨进程调用客户端的connected():

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags,
            String callingPackage, int userId) throws TransactionTooLargeException {
            ................
            if (s.app != null && b.intent.received) {
                  // Service is already running, so we can immediately
                  // publish the connection.
                  try {
                      // 直接跨进程调用客户端的connected()
                      // 不在需要跨进程调用service.onBind()
                      c.conn.connected(s.name, b.intent.binder);
                  } catch (Exception e) {
                      Slog.w(TAG, "Failure sending service " + s.shortName
                              + " to connection " + c.conn.asBinder()
                              + " (in " + c.binding.client.processName + ")", e);
                  }
            ..........................
    
    }
    

    在看客户端进程的connected()

    3.2.2.5 connected()

    public void connected(ComponentName name, IBinder service) throws RemoteException {
        // 先得到处理ServiceConnection的ServiceDispatcher
        LoadedApk.ServiceDispatcher sd = mDispatcher.get();
        if (sd != null) {
            sd.connected(name, service);
        }
    }
    
    public void connected(ComponentName name, IBinder service) {
       // mActivityThread是在创建 ServiceDispatcher对象时,传入的组件所在进程的主线程的handler
       // 也就是说RunConnection是在组件所在的主线程中执行的
       if (mActivityThread != null) {
           mActivityThread.post(new RunConnection(name, service, 0));
       } else {
           doConnected(name, service);
       }
    }
    

    RunConnection.run()中会调用LoadedApk.doConnected()方法:

    public void run() {
        if (mCommand == 0) {
            doConnected(mName, mService);
        } else if (mCommand == 1) {
            doDeath(mName, mService);
        }
    }
    

    3.2.2.6 LoadedApk.doConnected():

    public void doConnected(ComponentName name, IBinder service) {
        ServiceDispatcher.ConnectionInfo old;
        ServiceDispatcher.ConnectionInfo info;
    
        synchronized (this) {
            .....................
            ServiceDispatcher.ConnectionInfo old;
            //很有意思,也就是说同一组件中对同一个service重复绑定,onServiceConnected()只会执行一次
            old = mActiveConnections.get(name);
                  if (old != null && old.binder == service) {
                      // Huh, already have this one.  Oh well!
                      return;
            }
            if (service != null) {
                // A new service is being connected... set it all up.
                mDied = false;
                info = new ConnectionInfo();
                info.binder = service;
                info.deathMonitor = new DeathMonitor(name, service);
                try {
                    // 设置死亡回调
                    service.linkToDeath(info.deathMonitor, 0);
                    // 将info保存在LoadedApk.mActiveConnections
                    mActiveConnections.put(name, info);
                } catch (RemoteException e) {
                    // This service was dead before we got it...  just
                    // don't do anything with it.
                    mActiveConnections.remove(name);
                    return;
                }
    
            } else {
                // The named service is being disconnected... clean up.
                mActiveConnections.remove(name);
            }
    
            if (old != null) {
                old.binder.unlinkToDeath(old.deathMonitor, 0);
            }
        }
    
        // If there was an old service, it is not disconnected.
        if (old != null) {
            mConnection.onServiceDisconnected(name);
        }
        // If there is a new service, it is now connected.
        if (service != null) {
          // 执行回调
            mConnection.onServiceConnected(name, service);
        }
    }
    

    doConnected()比较有意思的是下面的代码:

    ServiceDispatcher.ConnectionInfo old;
    old = mActiveConnections.get(name);
          if (old != null && old.binder == service) {
              // Huh, already have this one.  Oh well!
              return;
    }
    

    mActiveConnections来自ServiceDispatcher:

    private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
                = new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
    

    ServiceDispatcher.mActiveConnections用来记录该LoadedApk中的组件所绑定的service的连接信息。key是service的组件名,value是ServiceDispatcher.ConnectionInfo。

    ConnectionInfo中记录了service的代理binder以及死亡通知回调。

    private static class ConnectionInfo {
      // 远端service的代理binder
      IBinder binder;
      // 远端service 死亡通知回调
      IBinder.DeathRecipient deathMonitor;
    }
    

    connected(ComponentName name, IBinder service)方法的第一个参数是service的组件名,第二个参数是service的代理binder。

    • connected()方法中首先以name为key在mActiveConnections查找,如果索引到的ConnectionInfo的binder与传入的binder一致的话,说明是同一组件内使用同一个ServiceConnection接口对象对同一个service重复绑定,此时不会执行onServiceConnected()方法。

    4 unbindeService

    现在在看一下unbindeService()的过程,直接看AMS.unbindServiceLocked(),整个过程大体上就是找到相关的连接对象ConnectionRecord,将其从相关的map中移除,然后根据情况决定是否调用serive.onUnbind生命周期方法

    4.1 unbindServiceLocked

    boolean unbindServiceLocked(IServiceConnection connection) {
           // 得到ServiceDispatcher.InnerConnection的binder
           IBinder binder = connection.asBinder();
           if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "unbindService: conn=" + binder);
           // 前面说了ActiveService.mServiceConnections,这里面记录了AMS中所有app的service的连接
           // 自然也包括同一组件内使用同一个ServiceConnection绑定同一个service的情况
           ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
           // 说明没有使用该ServiceConnection接口对象绑定过service
           // 所以无需unbind
           if (clist == null) {
               Slog.w(TAG, "Unbind failed: could not find connection for "
                     + connection.asBinder());
               return false;
           }
    
           final long origId = Binder.clearCallingIdentity();
           try {
               // 依次取出同一组件内使用该ServiceConnection接口对象绑定的service的连接信息对象ConnectionRecord
               // 这里要注意的是,这相当于在发起unbindService()操作的组件中,对所有使用该ServiceConnection接口对象绑定的sercvice
               // 发起unbindService操作
               while (clist.size() > 0) {
                   ConnectionRecord r = clist.get(0);
                   removeConnectionLocked(r, null, null);
                   if (clist.size() > 0 && clist.get(0) == r) {
                       // In case it didn't get removed above, do it now.
                       Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);
                       clist.remove(0);
                   }
                   if (r.binding.service.app != null) {
                       // This could have made the service less important.
                       if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
                           r.binding.service.app.treatLikeActivity = true;
                           mAm.updateLruProcessLocked(r.binding.service.app,
                                   r.binding.service.app.hasClientActivities
                                   || r.binding.service.app.treatLikeActivity, null);
                       }
                       mAm.updateOomAdjLocked(r.binding.service.app);
                   }
               }
           } finally {
               Binder.restoreCallingIdentity(origId);
           }
    
           return true;
       }
    

    4.2 removeConnectionLocked():

    void removeConnectionLocked(
            ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
            IBinder binder = c.conn.asBinder();
            AppBindRecord b = c.binding;
            ServiceRecord s = b.service;
            // 从ServiceRecord中取出所有绑定该service的连接
            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
            if (clist != null) {
                // 将使用要unbindService()的ServiceConnection创建的连接
                // 从ServiceRecord.connections中移除
                clist.remove(c);
                if (clist.size() == 0) {
                    s.connections.remove(binder);
                }
            }
            // 将使用要unbindService()的ServiceConnection创建的连接
            // 从AppBindRecord.connections中移除
            b.connections.remove(c);
            if (c.activity != null && c.activity != skipAct) {
                if (c.activity.connections != null) {
                    c.activity.connections.remove(c);
                }
            }
            if (b.client != skipApp) {
                b.client.connections.remove(c);
                if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                    b.client.updateHasAboveClientLocked();
                }
                if (s.app != null) {
                    updateServiceClientActivitiesLocked(s.app, c, true);
                }
            }
            clist = mServiceConnections.get(binder);
            if (clist != null) {
                // 将使用要unbindService()的ServiceConnection创建的连接
                // 从ActiveService.mServiceConnections中移除
                clist.remove(c);
                if (clist.size() == 0) {
                    mServiceConnections.remove(binder);
                }
            }
    
            mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name);
    
            // 如果AppBindRecord.connections.size为0
            // 表示某客户端已经没有组件与该service绑定了
            // 那么将客户端从IntentBindRecord.apps中移除
            if (b.connections.size() == 0) {
                b.intent.apps.remove(b.client);
            }
    
            if (!c.serviceDead) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Disconnecting binding " + b.intent
                        + ": shouldUnbind=" + b.intent.hasBound);
                // 如果IntentBindRecord.apps.size为0,表示没有客户端与该service绑定了
                // 那么开始回调service进程的scheduleUnbindService(),执行service.unbind()
                if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
                        && b.intent.hasBound) {
                    try {
                        bumpServiceExecutingLocked(s, false, "unbind");
                        if (b.client != s.app && (c.flags&Context.BIND_WAIVE_PRIORITY) == 0
                                && s.app.setProcState <= ActivityManager.PROCESS_STATE_RECEIVER) {
                            // If this service's process is not already in the cached list,
                            // then update it in the LRU list here because this may be causing
                            // it to go down there and we want it to start out near the top.
                            mAm.updateLruProcessLocked(s.app, false, null);
                        }
                        mAm.updateOomAdjLocked(s.app);
                        b.intent.hasBound = false;
                        // Assume the client doesn't want to know about a rebind;
                        // we will deal with that later if it asks for one.
                        b.intent.doRebind = false;
                        s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
                    } catch (Exception e) {
                        Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
                        serviceProcessGoneLocked(s);
                    }
                }
    
                if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
                    boolean hasAutoCreate = s.hasAutoCreateConnections();
                    if (!hasAutoCreate) {
                        if (s.tracker != null) {
                            s.tracker.setBound(false, mAm.mProcessStats.getMemFactorLocked(),
                                    SystemClock.uptimeMillis());
                        }
                    }
                    // 根据情况决定是否调用service.onDestroy()方法
                    bringDownServiceIfNeededLocked(s, true, hasAutoCreate);
                }
            }
        }
    

    scheduleUnbindService()会导致下面的方法在service的主线程中执行:

    4.3 scheduleUnbindService()

    private void handleUnbindService(BindServiceData data) {
            Service s = mServices.get(data.token);
            if (s != null) {
                try {
                    data.intent.setExtrasClassLoader(s.getClassLoader());
                    data.intent.prepareToEnterProcess();
                    // 执行service.onUnbind()生命周期方法
                    boolean doRebind = s.onUnbind(data.intent);
                    try {
                        if (doRebind) {
                            // 如果onUnbind()的返回值设置为true的话,
                            // 调用AMS.unbindFinished()
                            ActivityManagerNative.getDefault().unbindFinished(
                                    data.token, data.intent, doRebind);
                        } else {
                            ActivityManagerNative.getDefault().serviceDoneExecuting(
                                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                        }
                    } catch (RemoteException ex) {
                    }
                } catch (Exception e) {
                    if (!mInstrumentation.onException(s, e)) {
                        throw new RuntimeException(
                                "Unable to unbind to service " + s
                                + " with " + data.intent + ": " + e.toString(), e);
                    }
                }
            }
        }
    

    unbindeService()代码就说道这里,接下来总结下:

      1. ServiceConnection.onServiceDisconnected()并不会在unbindeService()过程中调用,它只会在service进程被终止时回调通知绑定的客户端;
      1. 只有在绑定这个service的所有客户端中的组件都执行了unbindeService()时,service才会被销毁,执行servcice.onDestroy(),这也就要求暗示我们当完成于服务的交互后,最好unbindeService,这样有利于系统及时回收service资源;
      1. 当目标service所在的进程被杀掉时(即除了正常回收service),系统并不会销毁之前的与该service绑定的组件创建的连接,一旦service后续再次运行,系统会再次回调onServiceConnected();
      1. 如果service是通过startService()启动的,那么service将一直运行到其通过 stopSelf() 自行停止,或其他组件调用 stopService() 为止,无论其是否绑定到任何客户端;
      1. unbindeService()操作也是异步操作的;
      1. 一般情况下,只要service被客户端绑定过了,当其再被绑定时,不会在调用service.onBind()方法了,也就是说通常service.onBind()只会执行一次;
      1. 同一个组件内使用同一个ServiceConnection接口对象,重复绑定一个service时,会在AMS中为起创建连接,但是不会导致ServiceConnection.onServiceConnected()方法执行
      1. 同一个组件内执行unbindeService(ServiceConnection sc)时,相当于对所有使用sc绑定的service执行一次unbindeService操作;

    这里比较有趣的是,假设同一组件内使用sc重复绑定的一个servcie N次,那么这一次unbindeService(),当对于该该组件绑定该service来说执行了N次unbindeService操作。

    5 生命周期的总结:

    • 单独使用bindService(),unbindService()会经历:->onCreate()->onBind()->Service running->onUnbind() -> onDestroy();
    • 单独使用startService(),stopService()会经历:->onCreate()->onStartCommand()->Service running-> onDestroy();
    • 先调用startService(),再调用bindService()方法:
      • a. 如果结束只调用unbindService(),那么只会执行到onUnbind(),将不会执行onDestroy():->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind();
      • b. 如果在unbindService后,在调用stopService(),那么:->onCreate()->onStartCommand()->onBind()->Service running-> onUnbind()->onDestroy();

    service的生命周期方法都运行在主线程中,所以如果要在生命周期中执行耗时操作,请额外开启线程。

    参考

    Android6.0之App的Service组件运行机制之bindService
    Android 7.0 中 Service bind 流程详解

    相关文章

      网友评论

          本文标题:Android跨进程通信IPC之21--binderServic

          本文链接:https://www.haomeiwen.com/subject/wekoiftx.html