美文网首页
Service通过onBind启动流程源码探究

Service通过onBind启动流程源码探究

作者: 小天使999999 | 来源:发表于2019-01-23 20:05 被阅读0次

    根据《Activity启动流程源码探究》我们可以清楚以下几点:
    1)Context的通用实现是在ContextIml这个类中
    2)Activity的启动过程需要借助ActivityManagerService(AMS)这个服务端来完成,其本质是通过Binder通信
    3)Binder通信使用了2次,第一次Context作为客户端向AMS发起start请求,第二次AMS作为客户端向IApplicationThread发起最终的启动请求,我们暂且称为“双Binder切换机制”。
    4)第二次Binder通信后,通过H这个Handler进行线程切换,并且切回了主线程。Application、Activity、ContextImpl等实例创建都在主线程中,那些耗时操作其实是在Binder线程完成的。
    鉴于此,我们分析Service的绑定启动过程从ContextImpl的bindService方法开始分析。

    1.第一次Binder机制

    在ContextImpl中提供了bindService和bindServiceAsUser两种方法启动Service,后者提供了两个重载方法,他们最终都会调用bindServiceCommon方法。通过对比bindService和bindServiceAsUser方法的时候不难发现,他们都同时传了mMainThread.getHandler()这个Handler,追踪代码会发现它就是我们ActivityThread中的H,最终传给 LoadedApk.ServiceDispatcher的构造方法,用于切换到主线程。
    先来看一下bindServiceCommon这个方法,它完成了两个任务:
    1)将H这个handler对象传递给LoadedApk.ServiceDispatcher的构造方法,创建IServiceConnection实例,其实是其内部的ServiceDispatcher实例。IServiceConnection是Binder代理接口,IServiceConnection.Stub存根的派生类的具体实现是LoadedApk.ServiceDispatcher的静态内部类InnerConnection。这一调用流程的核心代码如下:

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
                handler, UserHandle user) {
            // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
            IServiceConnection sd;
            if (conn == null) {
                throw new IllegalArgumentException("connection is null");
            }
            if (mPackageInfo != null) {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
            } else {
                throw new RuntimeException("Not supported in system context");
            }
    

    这段代码的意图其实很明显,就是为后期调用ServiceConnection#onServiceConnected方法后切回主线程做准备。点击进入getServiceDispatcher的实现会看到,传入的参数被用来构建ServiceDispatcher,核心代码如下:

    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
                Context context, Handler handler, int flags) {
            synchronized (mServices) {
                LoadedApk.ServiceDispatcher sd = null;
                ...
                if (sd == null) {
                    sd = new ServiceDispatcher(c, context, handler, flags);
                    if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c)
                } 
                ...
                return sd.getIServiceConnection();
            }
        }
    

    来看一下ServiceDispatcher的构造方法如下:

            ServiceDispatcher(ServiceConnection conn,
                    Context context, Handler activityThread, int flags) {
                mIServiceConnection = new InnerConnection(this);
                mConnection = conn;
                mContext = context;
                mActivityThread = activityThread;
                mLocation = new ServiceConnectionLeaked(null);
                mLocation.fillInStackTrace();
                mFlags = flags;
            }
    

    mIServiceConnection = new InnerConnection(this)说明IServiceConnection的最终实现是ServiceDispatcher.InnerConnection,然后在getServiceDispatcher返回了sd.getIServiceConnection()这个方法,它当然就是mIServiceConnection这个InnerConnection的实例。注意,Handler被命名为mActivityThread,并通过activityThread这个Handler变量赋值,就是为了便于提醒,要切回到主线程。
    虽然这里先开了一个Binder线程,其实它什么都没干,就是在为Service绑定成功后的调用ServiceConnection#onServiceConnected方法做准备工作。当然,ServiceConnection#onServiceConnected方法在子线程执行,需要通过mActivityThread这个主线程Handler切回到主线程。
    2)调用ActivityManager.getService().bindService方法,开启第二个Binder线程。当然,这里就顺畅多了,跟我们之前的分析一致,ContextImpl这个代理或者叫客户端开始通知AMS这个服务端发起执行绑定任务了。

    2.第二次Binder机制

    直接来看bindServiceCommon方法中的核心代码部分,如下:

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
                handler, UserHandle user) {
            // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
            IServiceConnection sd;
            ...
            if (mPackageInfo != null) {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
            } 
            ...
            try {
                ...
                int res = ActivityManager.getService().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier());
                    ...
                return res != 0;
            } 
        }
    

    其实只有一行,调用了ActivityManager.getService().bindService方法,并且把sd这个IServiceConnection也传了进去,这里ContextImpl这个客户端通过bindService这个方法向AMS这个服务端发起了启动请求。第二次Binder正式启动,接下来就是AMS的表演时间了~
    AMS#bindService并无特殊之处,将任务交给了ActiveServices的bindServiceLocked方法,代码如下:

    return mServices.bindServiceLocked(caller, token, service,resolvedType, connection, flags, callingPackage, userId);
    

    注意,这里的connection参数就是IServiceConnection。在bindServiceLocked内部会调用bringUpServiceLocked方法唤起服务,调用核心代码如下:

    ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
                       ...
                if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                    s.lastActivity = SystemClock.uptimeMillis();
                    if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) != null) {
                        return 0;
                    }
                }
    

    因为是绑定创建,所以系统会自动添加BIND_AUTO_CREATE标记,注意
    ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);
    这行代码已经将IServiceConnection存入ConnectionRecord中,变量c又被存入ServiceRecord s这个变量里。在bringUpServiceLocked方法的参数s既是。在bringUpServiceLocked方法里会调用realStartServiceLocked(r, app, execInFg)方法(注意,这个r就是刚刚的s参数)。

    3.第三次Binder机制

    realStartServiceLocked(r, app, execInFg)方法首先会通过调用app.thread.scheduleCreateService方法创建Service,这个逻辑跟《Service启动流程源码探究
    的创建逻辑一致。ActiveServices是客户端向ApplicationThread这个服务端发起创建Service请求。这是第三次Binder机制。

    4.第四次Binder机制

    接着,bringUpServiceLocked内部会执行requestServiceBindingsLocked(r, execInFg)方法,该方法又会调用requestServiceBindingLocked(r, ibr, execInFg, false)方法,内部有一句核心代码如下:

     //i.requested这个条件是为了标记,如果已经bind过就不会再次绑定,也就是Service.onBind方法只执行一次
     if ((!i.requested || rebind) && i.apps.size() > 0) {
       r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState);
    

    这就是第四次Binder机制,这样一级一级的将r往下传递。r.app.thread仍然是IApplicationThread.stub的具体实现,也就是ActivityThread.ApplicationThread,ActiveServices作为客户端向服务端ApplicationThread发起bind请求,ApplicationThread的处理代码如下:

    public final void scheduleBindService(IBinder token, Intent intent,
                    boolean rebind, int processState) {
                updateProcessState(processState, false);
                BindServiceData s = new BindServiceData();
                s.token = token;
                s.intent = intent;
                s.rebind = rebind;
    
                if (DEBUG_SERVICE)
                    Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                            + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
                sendMessage(H.BIND_SERVICE, s);
            }
    

    跟ActivityThread.ApplicationThread#scheduleCreateService一致,通过一个静态内部类BindServiceData初始化绑定数据,通过H.BIND_SERVICE切换到主线程完成具体的绑定工作。

    5.BIND_SERVICE消息处理

    在主线程中处理H.BIND_SERVICE的方法是handleBindService处理也很简单,它首先执行s.onBind(data.intent)获得了IBinder实例,这里的s就是app.thread.scheduleCreateService方法创建的Service,注意:Service的onBind方法是在主线程调用的,所以不可以执行耗时任务。
    然后将绑定任务又交给了AMS的publishService,核心代码如下:

    if (!data.rebind) {
      IBinder binder = s.onBind(data.intent); 
      ActivityManager.getService().publishService( data.token, data.intent, binder);
    } else {
      s.onRebind(data.intent);
      ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
    }
    

    刚切到主线程,立马有开启了Binder线程,这次ActivityThread.ApplicationThread又担当起了客户端的角色,向服务端AMS发起publishService请求,既然选择在Binder线程里面做,必然是相当耗时啊!第五次Binder机制的开始了。

    6.第五次Binder机制

    看一下最后的狂欢吧,代码如下:

    mServices.publishServiceLocked((ServiceRecord)token, intent, service)
    

    AMS通过桥接方式,将任务交给了ActiveServices的publishServiceLocked方法,注意token就是s。该方法仍然只有一行关键代码如下:

    try {
          c.conn.connected(r.name, service, false);
        }
    

    这里的c就是上面存储的ConnectionRecord c变量,它内部的c.conn就是我们传入IServiceConnection实例,也就是InnerConnection的实例。这样我们进入LoadedApk.ServiceDispatcher.InnerConnection#connected(r.name, service, false)方法,看到如下代码:

    public void connected(ComponentName name, IBinder service, boolean dead) throws RemoteException {
                    LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                    if (sd != null) {
                        sd.connected(name, service, dead);
                    }
                }
    

    点击进入ServiceDispatcher的connected方法,会发现如下代码:

    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只有一个赋值在ServiceDispatcher的构造方法中,它是一个Handler,而且是我们在第1节谈到构造ServiceDispatcher实例时传入的H,mActivityThread.post清晰地告诉我们它将RunConnection切换到了主线程。

    ServiceDispatcher(ServiceConnection conn,
                    Context context, Handler activityThread, int flags) {
                mIServiceConnection = new InnerConnection(this);
                mConnection = conn;
                mContext = context;
                mActivityThread = activityThread;
                mLocation = new ServiceConnectionLeaked(null);
                mLocation.fillInStackTrace();
                mFlags = flags;
            }
    

    当然,我们还是要看一下RunConnection内部是如何执行的。代码如下:

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

    它的run方法调用了doConnected方法,看看下面这行代码吧:

                old = mActiveConnections.get(name);
                // 这里表示重复绑定没用,不会向下执行调用
                if (old != null && old.binder == service) {
                   // Huh, already have this one.  Oh well!
                   return;
                }
                // If there is a new service, it is now connected.
                if (service != null) {
                    mConnection.onServiceConnected(name, service);
                }
    

    是的,服务被绑定成功了,并且通知了客户端。注意:这里的意思是ServiceConnection.onServiceConnected方法在Binder工作线程执行连接成功后,被H切换到了主线程。
    终于绑定成功了~

    7.总结

    很显然,相对于Activity的启动和Service的启动,Service的绑定任务相对复杂,回顾《Service启动流程源码探究》 可以发现Service绑定任务多了2次Binder机制。分别是ContextImpl作为客户端LoadedApk作为服务端时的Binder机制,和AMS作为客户端ApplicationThread作为服务端的Binder机制。那么,整个流程就很明朗了,就是5次Binder机制。
    第1次Binder机制,ContextImpl作为客户端LoadedApk.ServiceDispather.InnerConnection作为服务端创建IServiceConnection实例,其本质就是将H这个handler和ServiceConnection封装在ServiceDispatcher中。
    第2次Binder机制,ContextImpl作为客户端向AMS这个服务端发起绑定请求,并将IServiceConnection实例传给AMS。这样AMS就具备了切换回主线程的H了。
    第3次Binder机制,AMS作为客户端向ActivityThread.ApplicationThread这个服务端发起CreateService的请求,ActivityThread.ApplicationThread接到请求后,通过H这个Handler切回主线程处理H.CREATE_SERVICE这个消息。处理过程包括:创建ContextImpl、Application、Service等任务。
    第4次Binder机制,创建完任务之后,AMS作为客户端向ActivityThread.ApplicationThread这个服务端发起BindService的请求,ApplicationThread接到请求后,通过H这个Handler切回主线程处理H.BIND_SERVICE这个消息。处理过程相当简单,调用Service的onBind方法返回IBinder实例,然后将该参数传给AMS#publishService方法。注意:这个IBinder其实是ServiceRecord的一个实例,记录了我们要绑定的服务的所有信息。这样就进入第5次Binder机制了。
    第5次Binder机制,一旦接到绑定任务,AMS#ActiveServices这个服务端就会提取ServiceRecord的服务信息,ConnectionRecord和IServiceConnection实例,执行绑定任务,最后通过第2次Binder机制传入的H这个Handler将连接结果回传给主线程。如果绑定成功,系统会调用客户端的mConnection.onServiceConnected接口通知客户端。

    诗云:

    早岁那知世事艰,中原北望气如山。楼船夜雪瓜洲渡,铁马秋风大散关。
    塞上长城空自许,镜中双鬓已先斑。出师一表真名世,千载谁堪伯仲间!

    笔者在写这篇文章的时候,参考了任玉刚老师《Android开发艺术探索》第9章中四大组件的启动相关内容,请知悉~

    相关文章

      网友评论

          本文标题:Service通过onBind启动流程源码探究

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