Android 外掛化原理解析——Service的外掛化
在 Activity生命週期管理 以及 廣播的管理 中我們詳細探討了Android系統中的Activity、BroadcastReceiver元件的工作原理以及它們的外掛化方案,相信讀者已經對Android Framework和外掛化技術有了一定的瞭解;本文將探討Android四大元件之一——Service元件的外掛化方式。
與Activity, BroadcastReceiver相比,Service元件的不同點在哪裡呢?我們能否用與之相同的方式實現Service的外掛化?如果不行,它們的差別在哪裡,應該如何實現Service的外掛化?
我們接下來將圍繞這幾個問題展開,最終給出Service元件的外掛化方式;閱讀本文之前,可以先clone一份
Service工作原理
連Service的工作原理都不瞭解,談何外掛化?知己知彼。
Service分為兩種形式:以startService啟動的服務和用bindService繫結的服務;由於這兩個過程大體相似,這裡以稍複雜的bindService
為例分析Service元件的工作原理。
繫結Service的過程是通過Context
類的bindService
完成的,這個方法需要三個引數:第一個引數代表想要繫結的Service的Intent,第二個引數是一個ServiceConnetion,我們可以通過這個物件接收到Service繫結成功或者失敗的回撥;第三個引數則是繫結時候的一些FLAG;關於服務的基本概念,可以參閱
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, |
大致觀察就能發現這個方法最終通過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) { |