Android 8.0 原始碼分析 (五) Service 啟動

DevYK發表於2019-10-31

前言

我們熟知一般 Android 工程師都是在應用層上開發,不會涉及系統原始碼,但是如果你想往底層發展,或者深入外掛化、Framework 系統層等開發工作,如果不瞭解 Android 原始碼可是不行的,那麼接下來我基於自己的理解跟學習來記錄跟 Android 開發息息相關的原始碼分析,大概從 Android 中的 SystemServer 啟動、四大元件啟動、AMS、PMS 等幾個維度來介紹,下面是我的計劃,當然在未來也有可能改變。

還沒有關注的小夥伴,可以先關注一波,系列文章會持續更新。

Android 8.0 原始碼分析 (一) SystemServer 程式啟動

Android 8.0 原始碼分析 (二) Launcher 啟動

Android 8.0 原始碼分析 (三) 應用程式程式建立到應用程式啟動的過程

Android 8.0 原始碼分析 (四) Activity 啟動

Android 8.0 原始碼分析 (五) Service 啟動

Android 8.0 原始碼分析 (六) BroadcastReceiver 啟動

Android 8.0 原始碼分析 (七) ContentProvider 啟動

Android 8.0 原始碼分析 (八) ActivityManagerService

Android 8.0 原始碼分析 (九) WindowManager

Android 8.0 原始碼分析 (十) WindowManagerService 的視窗管理

介紹

上一篇文章我們講解了 Activity 的啟動,那麼這一篇將為大家分析四大元件中的 Service 的 startService 和 bindService 啟動原理。在實際開發中,運用 Service 元件有很多場景,比如,後臺播放音樂,後臺下載檔案等等,只要你想把些任務邏輯放在後臺執行的,那麼都可以開啟一個服務。在現在這個開發環境中,會運用 Service 可不行,得懂點內部執行原理才行,好了,廢話不多說了,下面先來分析 startService。

startService 啟動過程

ContextImpl 到 AMS 的呼叫過程

下面先看一張在 Activity 中呼叫 startService 的時序圖

KT0uxe.png

當我們在 Activity 中呼叫 startActivity 函式的時候,點選 startActivity 看原始碼實現會發現它是在 ContextWrapper 類中實現的。

//ContextWrappaer
	Context mBase;

    @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
複製程式碼

那麼 ContextWrapper 是什麼呢?下面我們以一張圖片看下它的繼承關係

Kootld.png

通過上面的層級,能明白了吧,我們繼續看 Context 的 startService 實現,程式碼如下:

//Context.java
public abstract class Context {
 ...
       @Nullable
    public abstract ComponentName startService(Intent service);
 ...
  
}
複製程式碼

通過上面程式碼可以看到 Context 是一個抽象類,那麼它具體實現是哪個類?在上一篇文章我們介紹了 Activity 的啟動,那麼在啟動的時候,就會建立 Context ,注意看下面程式碼,如下:

//ActivityThread.java
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      

        /**
         * 1. 通過封裝啟動 Activity 資訊類 ActivityClientRecord 來獲取到 ActivityInfo
         */
        ActivityInfo aInfo = r.activityInfo;
        /**
         * 2. 如果 r.packageInfo == null 就獲取 LoadedApk 例項
         */
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        /**
         * 3. 通過封裝啟動 Activity 資訊類 ActivityClientRecord 來獲取啟動 Activity 元件的 ComponentName
         */
        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
        /**
         * 4. 建立Activity 中的上下文環境
         */
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            /**
             * 拿到當前上下文載入的 ClassLoader 載入器
             */
            java.lang.ClassLoader cl = appContext.getClassLoader();
            /**
             * 5. 呼叫 Instrumentation.newActivity 函式 ,內部通過 ClassLoader 類載入器來建立 Activity 的例項
             */
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
           ...
        }

        try {
            /**
             * 6. 得到 Application 物件
             */
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            ....
                /**
                 * 7. 將 appContext 等物件依附在 Activity 的 attach 函式中,進行 Activity  attach 初始化
                 */
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);

               ...

        return activity;
    }
複製程式碼

通過上面程式碼,我們只關注註釋 4 跟註釋 7,我們看下注釋 4 的實現,程式碼如下:

//ActivityThread.java
    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
...

        //1.建立 Activity 的 Context 物件
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

...
        return appContext;
    }
複製程式碼
//ContextImpl.java
    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        ...
				//2. 例項化 Context 的實現類 ContextImpl
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null, 0, classLoader);

...
        return context;
    }
複製程式碼

通過上面程式碼我們知道,最後在 createActivityContext 函式中例項化了 ContextImpl 物件,現在我們回到 ActivityThread performLaunchActivity 函式的註釋 7 ,程式碼如下

//ActivityThread.java
                /**
                 * 7. 將 appContext 等物件依附在 Activity 的 attach 函式中,進行 Activity  attach 初始化
                 */
activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
複製程式碼
//Activity.java
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
      	//呼叫父類函式
        attachBaseContext(context);
      
      ....
        
    }
複製程式碼

attachBaseContext 是父類函式,程式碼如下:

//ContextThemeWrapper
  public class ContextThemeWrapper extends ContextWrapper {
    ...
	@Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }
    
    ...
      
  }
複製程式碼

繼續呼叫父類,程式碼如下:

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    

    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
      	//賦值給 Context
        mBase = base;
    }
複製程式碼

到最後我們看到 Context 的實現類 ContextImpl 賦值給了 ContextWrapper 的成員變數 mBase,現在我們直接去 ContextImpl 的 startService 看它具體實現,程式碼如下:

//ContextImpl
class ContextImpl extends Context {
  ...
    @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        /**
         * 呼叫內部 startServiceCommon 函式
         */
        return startServiceCommon(service, false, mUser);
    }
 ...
   
}
複製程式碼

繼續呼叫內部函式,程式碼如下:

//ContextImpl
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            /**
             * 呼叫 AMS 代理的 IActivityManager  的 startService 函式
             */
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
      ...
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
複製程式碼

在啟動 Activity 原始碼講解中,我們已經見過了這種呼叫方式,其內部就是通過 aidl 來進行應用程式與 AMS(SystemServer) 程式通訊,最後回撥到 AMS 的 startService 函式

ActivityThread 啟動 Service

呼叫過程按照老規矩,看看總體時序圖:

Koj07d.png

通過上一小節我們知道最後會回到 AMS 的 startService 函式中,那麼我們直接看 AMS 中的具體實現,程式碼如下:

//AMS.java

   /**
     * 這裡是 ContextImpl 通過程式間通訊呼叫的
     * @param caller
     * @param service
     * @param resolvedType
     * @param requireForeground
     * @param callingPackage
     * @param userId
     * @return
     * @throws TransactionTooLargeException
     */
    @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {

      ...
            try {
                /**
                 * 1. 呼叫 ActiveService 的 startServiceLocked 函式
                 */
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }
複製程式碼

註釋 1 的程式碼具體實現是通過 ActiveService 類呼叫 startServiceLocked 函式,程式碼如下:

//ActiveService.java

    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
            throws TransactionTooLargeException {
			...

        /**
         * 1. 查詢是否有與引數 Service 對應的 ServiceRecord
         */
        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg, false);

        if (res == null) {
            return null;
        }
        if (res.record == null) {
            return new ComponentName("!", res.permission != null
                    ? res.permission : "private to package");
        }

        /**
         * 2. 通過 ServiceLookupResult 得到引數 Service 對應的 ServiceRecord ,並傳入到註釋 3 處的 startServiceInnerLocked 中
         */
        ServiceRecord r = res.record;

        ...
        /**
         * 3. 呼叫內部 startServiceInnerLocked 函式
         */
        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
        return cmp;
    }
複製程式碼

在註釋 1 處的 retrieveServiceLocked 函式會查詢是否有與引數 service 對應的 ServiceRecord, 如果沒有找到,就會呼叫 PMS 去獲取引數 service 對應的 Service 資訊,並封裝到 ServiceRecord 中,最後將 ServiceRecord 封裝為 ServiceLookupResult 返回。其中 ServiceRecord 用於描述一個 Service, 和此前講過的 ActivityRecord 類似,在註釋 2 中將得到的 ServiceRecord 賦值給註釋 3 ,最後呼叫內部 startServiceInnerLocked 函式,程式碼如下:

//ActiveServices.java


    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
			 ...
        /**
         * 繼續呼叫內部函式
         */
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
       ...

        return r.name;
    }
複製程式碼

我們繼續看 bringUpServiceLocked 函式

//ActiveServices.java

    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
        ....
        /**
         * 1. 獲取 Service 想要在哪個程式中執行
         */
        final String procName = r.processName;
        String hostingType = "service";
        ProcessRecord app;

        if (!isolated) {
            /**
             * 2. 將 procname 和 Service 的 uid 傳入到 AMS 的getProcessRecordLocked 函式,查詢是否已經存在了當前啟動的 Service
             */
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
      

            /**
             * 3. 如果執行 Service 的應用程式程式已經存在
             */
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    /**
                     * 3.1 啟動 Service
                     */
                    realStartServiceLocked(r, app, execInFg);
                    return null;
...
            }
        } else {
...


        /**
         * 4. 如果用來執行的 Service 的應用程式程式不存在
         */
        if (app == null && !permissionsReviewRequired) {
            /**
             * 5. 建立應用程式程式
             */
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    hostingType, r.name, false, isolated, false)) == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r);
                return msg;
            }
....

        return null;
    }
複製程式碼

上面程式碼主要邏輯就是通過在 AMS 查詢需要啟動的 Service 所在的程式是否已經存在,如果存在則啟動 Service, 如果不存在就呼叫 註釋 5 內部函式,建立一個應用程式程式,那麼我們直接看註釋 3.1 啟動 Service 函式,程式碼如下:

//ActiveService.java

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        ...
            /**
             * 1. 呼叫 app.thread 函式,其實就是呼叫 IApplicationThread 的 scheduleCreateService 函式
             */
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
         ...
    }
複製程式碼

這裡的函式如果呼叫了 scheduleCreateService 說明已經離開了 AMS 程式,那麼 app.thread 到底是什麼了?它其實就是跟上一篇我們們介紹 Activity 啟動模式一樣,app.thread 就是 IApplicationThread 它是一個 aidl 檔案,具體實現在 ActivityThread 的非靜態內部類,具體看如下程式碼吧:

//ActivityThread.java
public final class ActivityThread {
...
private class ApplicationThread extends IApplicationThread.Stub {
 ...
   
        public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
            s.compatInfo = compatInfo;
						//呼叫內部過載函式
            sendMessage(H.CREATE_SERVICE, s);
        }   
 ...
  
  
}
複製程式碼

這裡呼叫內部過載函式這裡只需記住 H.CREATE_SERVIC 標記,具體實現程式碼如下:

  //ActivityThread.java 
	private void sendMessage(int what, Object obj) {
        /**
         * 1. 繼續呼叫內部過載函式 what = H.CREATE_SERVIC
         */

        sendMessage(what, obj, 0, 0, false);
    }

....

    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        /**
         * 2. 通過 H 的 Handler 將 what = H.CREATE_SERVIC 訊息 what 傳送出去
         */
        mH.sendMessage(msg);
    }
複製程式碼

這裡直接看註釋 2 通過 H 類的 sendMessage 將標誌為 H.CREATE_SERVIC 傳送出去。這裡的 H 類就是繼承的 Handler ,具體實現程式碼如下:

//ActivityThread.java
public final class ActivityThread {
//初始化 H 類
final H mH = new H();
  
    /**
     * H 繼承自 handler 是應用程式程式中主執行緒的訊息管理類
     */
    private class H extends Handler {
      ...
        
      public static final int CREATE_SERVICE  = 114;
      
     .... 
  		public void handleMessage(Message msg) {
       ...
                
      case CREATE_SERVICE:
       //呼叫 handleCreateService
       handleCreateService((CreateServiceData)msg.obj);
       Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
       break;
       ...
     }
    }
  
}
複製程式碼

H 的 handleMessage 接收到傳送過來的訊息,呼叫 handleCreateService 函式,我們來看下具體實現:

//ActivityService.java


    private void handleCreateService(CreateServiceData data) {

        /**
         * 1. 拿到要啟動 Service 的應用程式的 LoadApk
         */
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            /**
             * 2. 拿到類載入器
             */
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            /**
             * 3. 建立 Servcie 例項
             */
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            ...
        }

        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
            /**
             * 4. 建立 Context 例項
             */
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            /**
             * 5. 拿到快取中的 Application
             */
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            /**
             * 6. 執行 Service onCreate 生命週期
             */
            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();
            }
        } catch (Exception e) {
            ...
        }
    }
複製程式碼

這個函式裡面的程式碼,幾乎每行都很重要,我這裡簡單概括下,主要通過 ClassLoader 載入 Service 類,與 service 繫結 Application 和 Context 一個是應用級別,一個是服務級別,最後執行 Service 生命週期 onCreate 函式,這樣服務就啟動了,那麼剩下的生命週期就都大同小異,小夥伴感興趣的話,可以自己翻閱原始碼進行查閱,下一小節帶給大家是另一種方式啟動服務。

bindService 繫結服務過程

ContextImpl 到 AMS 的呼叫過程

下面還是以一張時序圖先看下 ContextImpl 到 AMS 呼叫過程:

KTeLJf.png

ContextWrapper 的關係這裡不再過多講解,如果忘記了可以看我文章開頭的介紹,我們直接看 ContextWrapper 類中的 bindService 函式,程式碼如下:

//ContextWrapper.java
public class ContextWrapper extends Context {
    Context mBase;
  ...
    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }
  
  ...
}
複製程式碼

這裡呼叫方式跟 startService 相似,都是呼叫 ContextImpl 內部函式,直接看 ContextImpl 的 bindService 函式

//ContextImpl.java
    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }
複製程式碼

呼叫內部過載函式 bindServiceCommon,程式碼如下:

//ContextImp.java
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) {
...
        /**
         * 1. 呼叫 LoadApk 型別的物件 mPackageInfo 的 getServiceDispatcher 函式,它的主要作用就是將 ServiceConnection 封裝為 IServiceConncet 型別的物件 sd, 從 IServiceConnection 的名字
         * 我們就能得知它實現了 Binder 機制,這樣 Service 的繫結就支援了跨程式。
         */
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            service.prepareToLeaveProcess(this);
            /**
             * 2. 獲得 AMS 的代理,呼叫 bindService 函式,將會傳遞到 AMS 中
             */
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
複製程式碼

這裡我們直接看核心程式碼 呼叫 IActivityManager 的 bindService 函式,最終也還是會回到 AMS 中 bindService 函式,下一小節將詳細介紹:

Service 繫結過程

收到 ContextImp 向 AMS 傳送過來的 bindService 請求,具體程式碼如下:

//AMS.java

    /**
     * 通過 ContextImpl 呼叫,中間程式間通訊傳遞過來的
     * @param caller
     * @param token
     * @param service
     * @param resolvedType
     * @param connection
     * @param flags
     * @param callingPackage
     * @param userId
     * @return
     * @throws TransactionTooLargeException
     */
    public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException {
			...
        synchronized(this) {
            //呼叫 ActiveService 的 bindServiceLocked 函式 
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
        }
    }
複製程式碼

回撥到 AMS 的 bindService 函式中,內部又呼叫 ActiveService 的 bindServiceLocked 函式進行傳遞,這裡可以看到 ActiveService 不管是 start 還是 bind 最後都是在 ActiveService 中處理 Service 的啟動邏輯。下面呼叫程式碼如下:

//ActiveServie.java

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {
						....

            /**
             * 1. 呼叫 ServiceRecord 的 retrieveAppBindingLocked 函式來獲取 AppBindRecord ,
             *    retrieveAppBindingLocked 函式內部建立 IntentBindRecord ,並對 IntentBindRecord 的成員變數進行賦值。
             */
            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);

            IBinder binder = connection.asBinder();
					...

            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                /**
                 * 2. 在 bringUpServiceLocked 函式的內部中呼叫了 realStartServiceLocked 函式,最終由 ActivityThread 來呼叫 Service 的 onCreate 函式啟動 Service.
                 */
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                        permissionsReviewRequired) != null) {
                    return 0;
                }
            }

          ....

            /**
             * 3. 表示 Server 已經執行,其中 s 是 ServiceRecord 物件,app 是 ProcessRecord 型別物件,b.intent.received 表示當前應用程式程式已經接收到繫結服務的 Service 是返回回來的 Binder,
             */
            if (s.app != null && b.intent.received) {
                // Service is already running, so we can immediately
                // publish the connection.
                try {
                    /**
                     * 4. 呼叫 IServiceConnect 的connected 函式,具體實現在 @see ServiceDispatcher.InnerConnection,
                     */
                    c.conn.connected(s.name, b.intent.binder, false);
                } catch (Exception e) {
                  ...
                }

                /**
                 * 5. 如果當前應用程式程式是第一個與 Service 進行繫結的,並且 Service 已經呼叫過 onUnBind 函式,則需要呼叫註釋 6 
                 */
                if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                    /**
                     * 6. 呼叫內部 requestServiceBindingLocked 函式
                     */
                    requestServiceBindingLocked(s, b.intent, callerFg, true);
                }
            } else if (!b.intent.requested) {//7. 如果應用程式程式的 Client 端沒有傳送過繫結 Service 的請求,則會呼叫註釋 8 
                /**
                 * 8.表示不是重新繫結
                 */
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }
            getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
        return 1;
    }

複製程式碼

當前函式中的處理邏輯比較多,涉及到了與 Service 相關的幾個物件,這裡先說明下;

  • ServiceRecord:用於描述一個 Service
  • ProcessRecord: 一個程式的資訊
  • ConnectionRecord:用於描述應用程式程式和 Service 建立的一次通訊
  • AppBindRecord: 應用程式程式通過 Intent 繫結 Service 時,會通過 AppBindRecord 來維護 Service 與應用程式程式之間的關聯。其內部儲存了誰繫結的 Service(ProcessRecord)、被繫結的 Service (AppBindRecord)、繫結 Service 的 Intent (IntentBindRecord) 和所有繫結通訊記錄的資訊(ArraySet)。
  • IntentBindRecord: 用於描述繫結 Service 的 Intent。

這裡就不在介紹上面程式碼具體意思了,每一步都標註了詳細註釋,這裡提一下上面註釋 2 ,這裡內部會呼叫 onCreate 步驟跟 startService 一樣,然後我們看註釋 6 如果當前應用程式程式是第一個與 Service 進行繫結的,並且 Service 已經呼叫過 onUnBind 函式。程式碼如下:

//ActiveServices.java
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                + " rebind=" + rebind);
        /**
         * 1. i.requested 表示是否傳送過繫結的請求,這裡上上一步知道是傳送過的,因為 為 false, rebind 為true ,所以前面為 true,
         *    i.apps.size() 其中 i 是 IntentBindRecord 物件,AMS 會為每個繫結 Service 的Intent 分配一個 IntentBindRecord 型別的物件。
         */
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
              	//2. 呼叫 IApplicationThread 物件
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {
             		...
                throw e;
            } catch (RemoteException e) {
              ...
                return false;
            }
        }
        return true;
    }
複製程式碼

根據上面程式碼,最後會呼叫註釋 2 ,通過 IApplicationThread 與 ActivityThread 應用程式程式通訊,程式碼如下:

//ActivityThread#ApplicationThread.java
 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;
...
  					//1. 
            sendMessage(H.BIND_SERVICE, s);
        }
複製程式碼

呼叫內部函式:

//ActivityThread.java
    private void sendMessage(int what, Object obj) {
        /**
         * 繼續呼叫內部過載函式 what = H.LAUNCH_ACTIVITY
         */

        sendMessage(what, obj, 0, 0, false);
    }

    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        /**
         * 2. 通過 H 的 Handler 將 what = H.LAUNCH_ACTIVITY 訊息 what 傳送出去
         */
        mH.sendMessage(msg);
    }
複製程式碼

這裡跟 startService 一樣最後都是 通過 H 來通知bindService,下面程式碼如下:

//ActivityThread.java
//ActivityThread.java
public final class ActivityThread {
//初始化 H 類
final H mH = new H();
  
    /**
     * H 繼承自 handler 是應用程式程式中主執行緒的訊息管理類
     */
    private class H extends Handler {
      ...
        
      public static final int CREATE_SERVICE  = 114;
      
     .... 
  		public void handleMessage(Message msg) {
       ...
                
      case BIND_SERVICE:
       //呼叫 handleBindService
				handleBindService((BindServiceData)msg.obj);
       break;
       ...
     }
    }
  
}
複製程式碼

接下來我們看 handleUnbindService 函式具體實現:

//ActivityThread.java
    private void handleBindService(BindServiceData data) {
        /**
         * 1. 獲取需要繫結的 Service
         */
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    /**
                     * 2.沒有經過繫結
                     */
                    if (!data.rebind) {
                        /**
                         * 3. 調動 Service 生命週期 onBind 函式
                         */
                        IBinder binder = s.onBind(data.intent);
                        /**
                         * 4. 呼叫 AMS 的 publishService 函式
                         */
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        /**
                         * 5.如果已經經過繫結呼叫 onRebind 生命週期函式
                         */
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                ...
            }
        }
    }
複製程式碼

這一步算是對 Service 進行開始執行生命週期函式了,如果 data.rebind 為 false 那麼說明沒有繫結,執行 s.onBind(data.intent); 生命週期函式,下面我們來看註釋 4 呼叫 AMS 的 publishService ,程式碼如下:

//AMS.java
    public void publishService(IBinder token, Intent intent, IBinder service) {
        // Refuse possible leaked file descriptors
        if (intent != null && intent.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        synchronized(this) {
            if (!(token instanceof ServiceRecord)) {
                throw new IllegalArgumentException("Invalid service token");
            }
            mServices.publishServiceLocked((ServiceRecord)token, intent, service);
        }
    }
複製程式碼

內部呼叫 ActiveServices 的 publishServiceLocked 函式

//ActiveServices.java

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try {
         ...
            if (r != null) {
                Intent.FilterComparison filter
                        = new Intent.FilterComparison(intent);
                IntentBindRecord b = r.bindings.get(filter);
              ...
                    for (int conni=r.connections.size()-1; conni>=0; conni--) {
                        ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                        for (int i=0; i<clist.size(); i++) {
                            ConnectionRecord c = clist.get(i);
                           .....
                            try {
                              
                              //1. 
                                c.conn.connected(r.name, service, false);
                            } catch (Exception e) {
                                ...
                            }
                        }
                    }
                }
                serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
複製程式碼

在註釋1 處呼叫了 ServiceDispatcher 型別的 sd 物件的 connected 函式,程式碼如下:

//LoadedApk.java
        private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

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

        public void connected(ComponentName name, IBinder service, boolean dead) {
            if (mActivityThread != null) {
              //2. 
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else {
                doConnected(name, service, dead);
            }
        }
複製程式碼

這裡首先通過程式間通訊 AMS 發到 InnerConnection 的 connected 函式,然後呼叫 LoadedApk 的 connected 函式,最後執行 註釋 2 的 post 函式, 程式碼如下:

//LoadedApk.java

        private final class RunConnection implements Runnable {
            RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
                mName = name;
						...
            }

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

呼叫 doConnected 函式,程式碼如下:

        public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            synchronized (this) {
            ...

            // If there was an old service, it is now disconnected.
            if (old != null) {
                mConnection.onServiceDisconnected(name);
            }
            if (dead) {
                mConnection.onBindingDied(name);
            }
            // If there is a new service, it is now connected.
            if (service != null) {
                /**
                 * 1. 呼叫了 ServiceConnection 型別的物件 mConnection 的 onServiceConnected 函式,這樣在客戶端實現了 ServiceConnection 介面,那麼就會被執行。
                 */
                mConnection.onServiceConnected(name, service);
            }
        }
複製程式碼

通過上面程式碼的註釋 1 我們知道,只要客戶端通過如下程式碼繫結,那麼就會回撥到 onServiceConnected 回撥,

        bindService(new Intent(XXXX,XXXX), new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG,"繫結成功");
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
 								Log.d(TAG,"繫結失敗,或者取消繫結");
            }
        })
複製程式碼

至此,整個 bindService 過程就分析完成。最後給出一個 AMS 到 ServiceConnection 時序圖。

KTwFtf.png

總結

這裡我們可以分為 3 個階段來進行服務的繫結,下面還是以一張圖來表述(ps: 這裡給處較難的 bindService 大概流程)。

KTwo8S.png

參考

  • 《Android 進階解密》

相關文章