天天看點

Android 插件化開發——基礎底層知識(Service)

上一篇我們講了Context和Activity的相關知識,

Android 插件化開發——基礎底層知識(Context家族史,Activity啟動流程)

本篇我們講述Service的工作流程

關于Service的建立啟動流程,其實和Activity的建立啟動流程是相似的。都是和AMS互動,通信媒體是:Binder

Service工作流程

關于Service的啟動方式,我們分為兩種:startService和bindService, 關于這兩個啟動方式的差別以及使用方法,在此不再贅述。

啟動建立Service,我們分為兩種:同一程序,不同程序。

新程序啟動Service

假設要在新程序啟動一個Service,可分為5個階段:

  • APP給AMS發送消息,附帶Manifest定義的相關資訊。
  • AMS檢查新的程序是否存在,如果不存在,就建立一個新的程序,并且把Service資訊儲存起來。
  • 啟動新程序,然後通知AMS,:新的程序已經存在
  • 此時AMS發送剛才存儲的Service資訊給新程序
  • 新程序啟動Service

1: APP給AMS發送消息,附帶Manifest定義的相關資訊。

以startService為例,該方法最終調用的是ContextWrapper中的startService方法。

@Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
           

我們知道mBase實際上就是ContextImp, 進入ContextImp,最終調用的是:startServiceCommon方法,可以看到這麼一句話:

ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
           

發送消息給AMS。

2.AMS檢查新的程序是否存在,如果不存在,就建立一個新的程序,并且把Service資訊儲存起來。

AMS會首先檢查Manifest中是否生命了Service,如果沒有直接報錯。有的話,就建立程序,并且把Service的相關資訊封裝成ServiceRecord對象儲存。

3.啟動新程序,然後通知AMS,:新的程序已經存在

Service在啟動的新程序的過程中和上一篇部落格講的APP的啟動流程相似。

此時會建立一個ActivityThread。然後ActivityThread會告訴AMS:新程序啟動成功。

4.此時AMS發送剛才存儲的Service資訊給新程序

AMS收到消息時候,會把之前存儲的ServiceRecord通過ApplicationThread發送給新程序。這個ServiceRecord就是将要啟動的Service的相關資訊。

5:新程序啟動Service

還是以startService為例,AMS發送消息給APT(ApplicationThread),執行:scheduleCreateService, 再給H 執行handleCreateService方法,其中可以看到這個代碼塊:

LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to instantiate service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }
           

可以看出通過classloader反射出(建立)了一個新的Service,這裡用的是反射。

注意:實際上可以回顧一下,建立Activity的時候,用到的也是反射,可以在Instrumentation中看到,在此不贅述。

至此,新程序的Service就建立啟動完成了。

同一程序綁定Service

在同一程序綁定Service,大緻分為五個步驟:

  • APP發送消息給AMS,附帶要啟動的Service資訊。
  • AMS接收到資訊之後,會注冊Service在AMS這邊,會通知APP啟動Service
  • APP收到消息1, 啟動Service,啟動完成之後,會發送一個消息給AMS:啟動完成
  • APP在收到消息2, 綁定Service,把Binder對象傳遞給AMS。
  • AMS再把接收到的Binder對象傳回給APP。

最後兩把繞了一圈 Binder又給了APP,考慮的因素可能是:不在同一個程序。是以這樣代碼公用。

調用bindService還是走的ContextWrapper,

@Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }
           

接着走了ContextImp的bindServiceCommon方法

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
       .....
        ActivityManager.getService().bindService(
                    mMainThread.getApplicationThread(), getActivityToken(), service,
                    service.resolveTypeIfNeeded(getContentResolver()),
                    sd, flags, getOpPackageName(), user.getIdentifier());
    }
           

最終傳遞給AMS。

AMS收到資訊之後,檢查Manifest,并且會檢視新的程序是否存在。

最終發送消息給APP(假設同一程序),然後回調給APP的APT的scheduleCreateService方法, 在傳遞給H,

private void handleCreateService(CreateServiceData data) {
       service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            service.onCreate();
            mServices.put(data.token, service);
            try {
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
    }
           

建立完了Service, 最後調用了serviceDoneExecuting方法,是告訴AMS:我建立完畢了,是否有下一步操作。

然後AMS收到消息之後,最終間接會調用APP的handleBindService方法,這是開始了綁定操作。

private void handleBindService(BindServiceData data) {
					....
      				 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);
                    }
                    ......
   		 }
           

執行了Service的onBind方法,并且告訴調用AMS的publishService方法告訴它:綁定完畢。

其中傳遞了Binder對象,這個Binder對象是ServiceDispatcher内部類的InnerConnection。 InnerConnection實作了IServiceConnection.Stub。

重新回到ContextImp:

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        .....
       sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
       ......
    }
           

進入到LoadedApk.java中可自行檢視ServiceDispatcher類, 把Binder包裝成了ServiceDispatcher對象發送給了AMS。最終調用:connected方法 --> doConnected方法:

// If there is a new viable service, it is now connected.
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            } else {
                // The binding machinery worked, but the remote returned null from onBind().
                mConnection.onNullBinding(name);
            }
           

看到了熟悉的onServiceConnected回調。 如果不在同一個程序,Service 通信的流程是:Service -->AMS -->Activity, 中間是Binder驅動。

附上網上的一張流程圖,個人覺得bindService流程跟這幅圖完全比配:

Android 插件化開發——基礎底層知識(Service)

至此綁定流程講述完了。整體的流程和Activity的啟動流程是相似的。

繼續閱讀