Android跨进程通信IPC之21--binderService(6.0)

移步系列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 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();
                   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,整个关系如下所示:


    Android跨进程通信IPC之21--binderService(6.0)_第1张图片
    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 mActiveConnections
       = new ArrayMap();
................
  }

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

3.1.2 InnerConnection

private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference(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类型对象,
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 clist = s.connections.get(binder);
  if (clist == null) {
      clist = new ArrayList();
      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();
      mServiceConnections.put(binder, clist);
  }
  clist.add(c);
 ............

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

final class ServiceRecord extends Binder {
    ...............
    final ArrayMap> connections
                = new ArrayMap>();// 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 apps
            = new ArrayMap();
    // 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记录下每个绑定动作对应的逻辑连接对象。
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 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类型的。
  • 因为客户端进程中一个Component中可能使用同一个ServiceConnection接口对象来多次绑定同一个service,因为每次绑定都会创建一个ConnectionRecord对象,那么这些ConnectionRecord需要使用ArrayList来保存。
  • value中ConnectionRecord.IServiceConnection实际上一致与key是一致的。
  • AMS除了将这个"逻辑连接"ConnectionRecord对象,记录在ServiceRecord.connections中外,还要向至少下面的几处位置做记录:
  1. 客户端所在的进程在AMS中的代表ProcessRecord.connections
  2. ActiveService.mServiceConnections,这里面记录了AMS中所有app的service的连接
  3. AppBindRecord.connections中

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

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


Android跨进程通信IPC之21--binderService(6.0)_第2张图片
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;

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


Android跨进程通信IPC之21--binderService(6.0)_第3张图片
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 clist = r.connections.valueAt(conni);
           for (int i=0; i

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 mActiveConnections
            = new ArrayMap();

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 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 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--binderService(6.0))