1. 程式人生 > >Android 外掛化原理解析——Service的外掛化

Android 外掛化原理解析——Service的外掛化

在 Activity生命週期管理 以及 廣播的管理 中我們詳細探討了Android系統中的Activity、BroadcastReceiver元件的工作原理以及它們的外掛化方案,相信讀者已經對Android Framework和外掛化技術有了一定的瞭解;本文將探討Android四大元件之一——Service元件的外掛化方式。

與Activity, BroadcastReceiver相比,Service元件的不同點在哪裡呢?我們能否用與之相同的方式實現Service的外掛化?如果不行,它們的差別在哪裡,應該如何實現Service的外掛化?

我們接下來將圍繞這幾個問題展開,最終給出Service元件的外掛化方式;閱讀本文之前,可以先clone一份 

understand-plugin-framework,參考此專案的 service-management 模組。另外,外掛框架原理解析系列文章見索引

Service工作原理

連Service的工作原理都不瞭解,談何外掛化?知己知彼。

Service分為兩種形式:以startService啟動的服務和用bindService繫結的服務;由於這兩個過程大體相似,這裡以稍複雜的bindService為例分析Service元件的工作原理。

繫結Service的過程是通過Context類的bindService完成的,這個方法需要三個引數:第一個引數代表想要繫結的Service的Intent,第二個引數是一個ServiceConnetion,我們可以通過這個物件接收到Service繫結成功或者失敗的回撥;第三個引數則是繫結時候的一些FLAG;關於服務的基本概念,可以參閱 

官方文件。(現在漢化了哦,E文不好童鞋的福音)

Context的具體實現在ContextImpl類,ContextImpl中的bindService方法直接呼叫了bindServiceCommon方法,此方法原始碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
UserHandle user)
{
IServiceConnection sd; if (conn == null) { throw new IllegalArgumentException("connection is null"); } if (mPackageInfo != null) { // important sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), mMainThread.getHandler(), 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(); int res = ActivityManagerNative.getDefault().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 new RuntimeException("Failure from system", e); } }

大致觀察就能發現這個方法最終通過ActivityManagerNative藉助AMS進而完成Service的繫結過程,在跟蹤AMS的bindService原始碼之前,我們關注一下這個方法開始處建立的sd變數。這個變數的型別是IServiceConnection,如果讀者還有印象,我們在 廣播的管理 一文中也遇到過類似的處理方式——IIntentReceiver;所以,這個IServiceConnection與IApplicationThread以及IIntentReceiver相同,都是ActivityThread給AMS提供的用來與之進行通訊的Binder物件;這個介面的實現類為LoadedApk.ServiceDispatcher。

這個方法最終呼叫了ActivityManagerNative的bindService,而這個方法的真正實現在AMS裡面,原始碼如下:

1
2
3
4
5
6
7
8
9
10
public int bindService(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags, String callingPackage,
        int userId) throws TransactionTooLargeException {
    enforceNotIsolatedCaller("bindService");
    // 略去引數校檢
    synchronized(this) {
        return mServices.bindServiceLocked(caller, token, service,
                resolvedType, connection, flags, callingPackage, userId);
    }
}

bindService這個方法相當簡單,只是做了一些引數校檢之後直接呼叫了ActivityServices類的bindServiceLocked方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags,
        String callingPackage, int userId) throws TransactionTooLargeException {
    final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
    // 引數校檢,略

    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage,
                Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
    // 結果校檢, 略
    ServiceRecord s = res.record;

    final long origId = Binder.clearCallingIdentity();

    try {
        // ... 不關心, 略

        mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
                s.appInfo.uid, s.name, s.processName);

        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent);
        IBinder binder = connection.asBinder();
        ArrayList<ConnectionRecord> clist = s.connections.get(binder);

        // 對connection進行處理, 方便存取,略
        clist.add(c);

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

        // 與BIND_AUTO_CREATE不同的啟動FLAG,原理與後續相同,略

    } finally {
        Binder.restoreCallingIdentity(origId);
    }

    return 1;
}

這個方法比較長,我這裡省去了很多無關程式碼,只列出關鍵邏輯;首先它通過retrieveServiceLocked方法獲取到了intent匹配到的需要bind到的Service元件res;然後把ActivityThread傳遞過來的IServiceConnection使用ConnectionRecord進行了包裝,方便接下來使用;最後如果啟動的FLAG為BIND_AUTO_CREATE,那麼呼叫bringUpServiceLocked開始建立Service,我們跟蹤這個方法:(非這種FLAG的程式碼已經省略,可以自行跟蹤)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting) throws TransactionTooLargeException {

    // 略。。

    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
    final String procName = r.processName;
    ProcessRecord app;

    if (!isolated) {
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                // 1. important !!!
                realStartServiceLocked(r, app, execInFg);
                return null;
            } catch (TransactionTooLargeException e) {
                throw e;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
            }

        }
    } else {
        app = r.isolatedProc;
    }

    // Not running -- get it started, and enqueue this service record
    // to be executed when the app comes up.
    if (app == null) {
        // 2. important !!!
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                "service", r.name, false, isolated, false)) == null) {
            bringDownServiceLocked(r);
            return msg;
        }
        if (isolated) {
            r.isolatedProc = app;
        }
    }
    // 略。。
    return null;
}

這個方案同樣也很長,但是實際上非常簡單:注意我註釋的兩個important的地方,如果Service所在的程序已經啟動,那麼直接呼叫realStartServiceLocked方法來真正啟動Service元件;如果Service所在的程序還沒有啟動,那麼先在AMS中記下這個要啟動的Service元件,然後通過startProcessLocked啟動新的程序。

我們先看Service程序已經啟動的情況,也即realStartServiceLocked分支:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {

    // 略。。

    boolean created = false;
    try {
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startLaunchedLocked();
        }
        mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        // 略。。
    }

    requestServiceBindingsLocked(r, execInFg);

    // 不關心,略。。
}

這個方法首先呼叫了app.thread的scheduleCreateService方法,我們知道,這是一個IApplicationThread物件,它是App所在程序提供給AMS的用來與App程序進行通訊的Binder物件,這個Binder的Server端在ActivityThread的ApplicationThread類,因此,我們跟蹤ActivityThread類,這個方法的實現如下:

1
2
3
4
5
6
7
8
9
10
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);
}

它不過是轉發了一個訊息給ActivityThread的H這個Handler,H類收到這個訊息之後,直接呼叫了ActivityThread類的handleCreateService方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void handleCreateService(CreateServiceData data) {
    unscheduleGcIdler();

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
    }

    try {
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service);

        Application app = packageInfo.makeApplication(false, mInstrumentation);
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManagerNative.getDefault());
        service.onCreate();
        mServices.put(data.token, service);
        try {
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            // nothing to do.
        }
    } catch (Exception e) {
    }
}

看到這段程式碼,是不是似曾相識?!沒錯,這裡與Activity元件的建立過程如出一轍!所以這裡就不贅述了,可以參閱 Activity生命週期管理

需要注意的是,這裡Service類的建立過程與Activity是略微有點不同的,雖然都是通過ClassLoader通過反射建立,但是Activity卻把建立過程委託給了Instrumentation類,而Service則是直接進行。

OK,現在ActivityThread裡面的handleCreateService方法成功創建出了Service物件,並且呼叫了它的onCreate方法;到這裡我們的Service已經啟動成功。scheduleCreateService這個Binder呼叫過程結束,程式碼又回到了AMS程序的realStartServiceLocked方法。這裡我們不得不感嘆Binder機制的精妙,如此簡潔方便高效的跨程序呼叫,在程序之間來回穿梭,遊刃有餘。

realStartServiceLocked方法的程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {

    // 略。。

    boolean created = false;
    try {
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startLaunchedLocked();
        }
        mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        // 略。。
    }

    requestServiceBindingsLocked(r, execInFg);

    // 不關心,略。。
}

這個方法在完成scheduleCreateService這個binder呼叫之後,執行了一個requestServiceBindingsLocked方法;看方法名好像於「繫結服務」有關,它簡單地執行了一個遍歷然後呼叫了另外一個方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
        boolean execInFg, boolean rebind) throws TransactionTooLargeException {
    if (r.app == null || r.app.thread == null) {
        return false;
    }
    if ((!i.requested || rebind) && i.apps.size() > 0) {