Android Framework源码阅读之BindService

这篇主要讲BindService的主要逻辑 Api-27

bindService相比startService无非就是多了个可以持有Service的引用,那我们就针对这个功能作为切入点.

先看下BindService的使用流程

假设一个实现了AIDL的接口IDoSth.aidl
interface IDoSth {  
    void doSth();
}

调用流程:
Context.bindService(intent, connection, Service.BIND_AUTO_CREATE);

private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        IDoSth currentService = IDownloadService.Stub.asInterface(service);
        try {
            currentService.doSth();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        ...
    }
};

开始源码走起.

bindservice的入口为ContextImpl.bindService(Intent service, ServiceConnection conn, int flags);
然后ContextImpl.bindServiceCommon().
bindService和startService使用区别在于启动者是可以持有Service的渠道的,这个渠道就是IServiceConnection

ContextImpl:
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) {
    IServiceConnection sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
    // 拒绝隐式Intent
    validateServiceIntent(service);
    try {
        ...
        //3.调用服务端bindService
        int res = ActivityManager.getService().bindService(..., sd, ...);
        ...
    }
}

在bindServiceCommon这个方法里先是根据conn去获得IServiceConnection sd.sd最终获得的是LoadedApk的一个静态内部类InnerConnection.这个InnerConnection继承了一个Stub,理解过AIDL都知道这是一个用于进程间通信的Binder.

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);
        }
    }
}

InnerConnection的connected方法是去调用ServiceDispatcher的doConnected()方法,然后就调用ServiceConnection.onServiceConnected(),这样一个正常的绑定Service的流程就结束了.先得出一个结论,那就是IServiceConnection是用于服务端回调客户端的接口,最终服务端会把IBinder传回给客户端让客户端持有服务端Binder的句柄,即上述代码:

    IDoSth currentService = IDownloadService.Stub.asInterface(service);//service就是IBinder

准备好IServiceConnection之后,就去调用服务端AMS的bindService()
服务端:

ActivityManagerService:
    public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String callingPackage,
            int userId) throws TransactionTooLargeException {
            ...
            synchronized(this) {
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
            }
            ...
    }
    

这里的mServices就是AMS中用于管理Service启动的ActiveServices

ActiveServices:

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {
        ...
        //1.获得要启动的Service信息
        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
                    Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
        ...
        ServiceRecord s = res.record;
        ...
        //2.得到AppBindRecord,AppBindRecord描述的作用是连接Service和他的绑定者(IntentBindRecord)
        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
        ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
        //3.启动Service
        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) != null) {
                return 0;
            }
        }
        ...
        //4.service如果准备好之后就可以调用ServiceConnection.connected()了
        if (s.app != null && b.intent.received) {
          //Service已经运行, 我们可以立即发布connection.
          try {
              c.conn.connected(s.name, b.intent.binder, false);
          } catch (Exception e) {
              ...
          }
          
          if (b.intent.apps.size() == 1 && b.intent.doRebind) {
              requestServiceBindingLocked(s, b.intent, callerFg, true);
          }
        } else if (!b.intent.requested) {
            //5.
            requestServiceBindingLocked(s, b.intent, callerFg, false);
        }
    }

bindServiceLocked()这个方法里大致是这么个逻辑:

  1. 首先通过retrieveServiceLocked()获得将要启动的Service信息
  2. 然后去找IntentBindRecord,AppBindRecord.这里的操作是去做一系列绑定信息的添加.
  3. 这里如果目标Service还没启动,就会调用bringUpServiceLocked()去启动Service并且返回非空,那么就直接return. 0,下面就不用执行了,原因是启动Service是个异步操作,这样代码就不好继续下去.
  4. 这里有两个判断,一个是s.app != null && b.intent.received.这个received是在publishServiceLocked()之后才会设为true.所以第一次发布会跳过4,进入5
  5. requestServiceBindingLocked()会先调r.app.thread.scheduleBindService()进入客户端调用ActivityThread.handleBindService()然后又会回到服务端,最终回到ActiveServices.publishServiceLocked().在这个方法里才把received设为true并调用c.conn.connected()最终触发ServiceConnection.onServiceConnected()这个开头有分析过.
ActiveServices:
  void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    try {
        ...
        b.requested = true;
        b.received = true;
        for (int conni=r.connections.size()-1; conni>=0; conni--) {
            ArrayList clist = r.connections.valueAt(conni);
            for (int i=0; i

至此,这就是BindService的持有引用的过程.

你可能感兴趣的:(Android Framework源码阅读之BindService)