Android启动系列之六:Service绑定流程

433 阅读5分钟

前言

绑定过程与启动过程类似,也是分成两个部分:既ContextImpl到AMS调用过程和Service的绑定过程

ContextImpl到AMS调用过程

这个过程如图:

20230718154816.jpg

入口是bindService方法,经历ContextWrapper和ContextImpl,最终会执行ContextImpl的bindServiceCommon,代码如下:

private boolean bindServiceCommon(...) {
    ...
    if (mPackageInfo != null) {
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);//1
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    validateServiceIntent(service);
    try {
     ...
        int res = ActivityManagerNative.getDefault().bindService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, getOpPackageName(), user.getIdentifier());  //2
      ...
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

代码1将ServiceConnection封装成IServiceConnection对象,这个IServiceConnection实现了Binder机制,这样Service的绑定就可以跨进程。

代码2调用了AMS的bindService方法,这样就进入了AMS进程。

Service绑定过程

AMS的bindService方法会调用ActiveServcie的bindServiceLocked,这块需要重点说一下,代码如下:

int bindServiceLocked(...) throws TransactionTooLargeException {
    ...

    try {

        ...

        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); //1
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent,
                callerApp.uid, callerApp.processName, callingPackage);

        IBinder binder = connection.asBinder();

        ...

        ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
        if (clist == null) {
            clist = new ArrayList<>();
            mServiceConnections.put(binder, clist);
        }
        clist.add(c);
        ...

        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            s.lastActivity = SystemClock.uptimeMillis();
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                    permissionsReviewRequired) != null) {   //2
                return 0;
            }
        }
        ...

        if (s.app != null && b.intent.received) {  //3
            try {
                c.conn.connected(s.name, b.intent.binder, false);  //4
            } catch (Exception e) {
                ...    
            }

            if (b.intent.apps.size() == 1 && b.intent.doRebind) {   //5
                requestServiceBindingLocked(s, b.intent, callerFg, true);  //6
            }
        } else if (!b.intent.requested) {  //7
            requestServiceBindingLocked(s, b.intent, callerFg, false);  //8
        }      
    }
    ...
    return 1;
}

这里先介绍几个与Service相关的对象类型:

  • ServiceRecord:这个很熟悉了,描述一个Service
  • IntentBindRecord:用于描述绑定Service的Intent
  • AppBindRecord:维护Service与应用程序进程之间的关联,内部记录了绑定Service的Intent和所有绑定通信记录的信息
  • ConnectRecord:用于描述应用程序进程和Service建立的一次通信

这么说大家可能还不是很清楚,下面通过它们的关系来更全面了解一下:

  1. ServiceRecord中有一个IntentBindRecord列表,Service可以通过不同的Intent进行绑,每一种都会有一个IntentBindRecord
  2. IntentBindRecord维护了一个<ProcessRecord,AppBindRecord>的map,会有不同的应用绑定Servcie,这样每个应用程序进程都会对应一个AppBindRecord
  3. AppBindRecord中维护了一个ConnectRecord列表,在这个应用程序进程中每次与Service连接都会生成一个记录

通过上面的解释,大家就应该清楚这几个对象的作用了,也解释了一个Service为什么可以被多次绑定。

下面回到bindServiceLocked代码中。代码1通过ServiceRecord的retrieveAppBindingLocked创建IntentBindRecord和AppBindRecord并返回AppBindRecord;代码2出调用了bringUpServiceLocked,这个在上一篇Servcie启动流程详细讲解了,这一步启动了Servcie,这里就不细说了

代码3中s.app != null表示Service已经启动,而b.intent.received则表示当前应用程序进程已经绑定Service并返回了Binder,这样我们就可以直接通过Binder来与Servcie通信;所以接下来代码4中调用ServcieDespatcher.InnerConnection的connect方法,在这个方法中会通过Handler向主线程发送消息。

代码5表示当前应用程序进程已经解绑了,则需要执行代码6的requestServiceBindingLocked重新绑定

代码7则是如果未绑定过,则执行代码8调用requestServiceBindingLocked进行绑定。

所以执行绑定的都是requestServiceBindingLocked方法,只不过它的最后一个参数是boolean值,用来表示是否时重新绑定。

在requestServiceBindingLocked中最终会执行IApplicationThread的scheduleBindService方法,这块大家应该非常熟悉了,不论是Activity启动、Servcie启动还是Service绑定都会进入IApplicationThread的对应方法,然后再扔给ActivityThread的对应方法来处理,最终都是通过Handler消息进行分发处理,这样就进入了应用程序进程中了。

在这个流程里,最终是在ActivityThread的handlerBindeServcie方法中来处理的,代码如下:

private void handleBindService(BindServiceData data) {
 
     Service s = mServices.get(data.token); //1
     
     if (s != null) {
        try {
            data.intent.setExtrasClassLoader(s.getClassLoader());
            data.intent.prepareToEnterProcess();
            try {
                if (!data.rebind) { //2
                    IBinder binder = s.onBind(data.intent); //3
                    ActivityManager.getService().publishService(
                            data.token, data.intent, binder); //4
                } else {
                    s.onRebind(data.intent); //5
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                }
                ...
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            ...
        }
    }
}

代码1处获取要绑定的Servcie,在上一篇文章Servcie的启动过程中我们知道在创建Servcie后会将它加入mServices,这一步正是从这里获取的,因为到这一步之前我们分析过,如果Service没启动一定会先启动。

然后代码2判断是否是重新绑定,如果不是则执行代码3调用Servcie的onBind方法绑定,并执行代码4调用AMS的publishService方法;如果是则执行代码5调用Servcie的onRebind。

因为这部分流程比较复杂,所以先来看看前面这部分的大致流程:

image.png

继续上面的步骤,在AMS的publishService方法中会调用ActiveServices的publishServcieLocked方法,在这个方法中会执行ServcieDespatcher.InnerConnection的connect方法,是不是很熟悉?

在上面的一开始的bindServiceLocked方法中也有这样一段代码,当时是如果Servcie已经启动并且绑定会进行连接,如果不是则会进入绑定流程。而现在绑定结束了,所以还需要进行一次连接,所以两部分代码目的是一样的。

connect代码非常简单:

public void connected(ComponentName name, IBinder service, boolean dead) {
    if (mActivityThread != null) {
        mActivityThread.post(new RunConnection(name, service, 0, dead));
    } else {
        doConnected(name, service, dead);
    }
}

通过mActivityThread.post在主线程中执行RunConnection,而在RunConnection中最终也是执行doConnected方法,代码如下:

public void doConnected(ComponentName name, IBinder service, boolean dead) {
    ...

    if (old != null) {
        mConnection.onServiceDisconnected(name);
    }
    if (dead) {
        mConnection.onBindingDied(name);
    }

    if (service != null) {
        mConnection.onServiceConnected(name, service); //1
    } else          
        mConnection.onNullBinding(name);
    }
}

可以看到执行了ServiceConnection的onServiceConnected方法,这样在客户端实现了ServiceConnection接口的类的onServiceConnected就会被调用。到这整个Service绑定流程才算真正的完成。后面这部分流程如下:

image.png

总结

通过两篇文章,我们弄清楚了Servcie的启动流程和绑定流程,可以看到启动流程相对简单一些,但是总体的过程都是差不多的,经过AMS再到应用程序进程中处理。