service兩種啟動方式(電腦service怎麼開啟)
前言
這幾天分析了一下的啟動過程,於是乎,今天寫一下Service是如何啟動的; 給我的感覺是這些啟動過程並不複雜,千萬不要被一坨一坨的程式碼嚇住了,雖然彎彎繞繞不少,過載函式一個接著一個,就向走迷宮一樣,但只要抓住主線閱讀,很快就能找到出口; 強調一下閱讀系統原始碼,起碼要對程序間通訊要了解,對binder機制非常非常清楚,binder就是指南針,要不然你會暈頭轉向;強行閱讀,就容易睡著。
Service啟動先來一張圖感受一下
這張圖能夠說明一個大致的流程,但是服務的啟動肯定不是這麼簡單,但是我們先簡單的總結一下,逐漸深入。服務的啟動形式有兩種,startService()和 binderService(),我們看startService()這一種。startService是ContextWrapper裡面的方法。
ContextWrapper.java
@Override public ComponentName startService(Intent service) { return mBase.startService(service);//mBase這裡指的是ContextImpl類 }
ContextImpl.java
@Override public ComponentName startService(Intent service) { warnIfCallingFromSystemProcess(); return startServiceCommon(service, mUser); }
private ComponentName startServiceCommon(Intent service, UserHandle user) { try { //檢驗Intent validateServiceIntent(service); ...... ComponentName cn = ActivityManagerNative.getDefault().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( getContentResolver()), getOpPackageName(), user.getIdentifier()); ...... return cn; } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } }
校驗完Intent後,就呼叫
ActivityManagerNative.getDefault(),獲取一個IActivityManager物件,將啟動Service這件事情交給了IActivityManager。我們看一下ActivityManagerNative的類定義
public abstract class ActivityManagerNative extends Binder implements IActivityManager
這種模式是不是非常熟悉啊? 繼承了Binder,實現了一個IActivityManager介面,這個跟我們生成了遠端服務通訊生成的AIDL的java檔案怎麼那麼像,現在告訴你,這就是為了遠端服務通訊做準備的,只是一般這種類我們都是自動生成的,ActivityManagerNative 是谷歌的人自己寫
一個完整的AID L有兩部分,一個是個跟服務端通訊的Stub,一個是跟客戶端通訊的Proxy; ActivityManagerNative就是Stub,閱讀原始碼發現在ActivityManagerNative 檔案中還有個ActivityManagerProxy,那麼跟客戶端通訊的Proxy也有了。先看IActivityManager怎麼獲取的
ActivityManagerNative.java
static public IActivityManager getDefault() { return gDefault.get(); }
private static final SingletongDefault = new Singleton() { protected IActivityManager create() { //獲取名為"activity"的服務,服務都註冊到ServiceManager來統一管理 IBinder b = ServiceManager.getService("activity"); if (false) { Log.v("ActivityManager", "default service binder = " b); } IActivityManager am = asInterface(b); if (false) { Log.v("ActivityManager", "default service = " am); } return am; } };
就是一個單例設計模式,獲取到服務物件IBinder,把這個IBinder轉換成IActivityManager返回了。現在由IActivityManager啟動服務。
public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, String callingPackage, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); service.writeToParcel(data, 0); data.writeString(resolvedType); data.writeString(callingPackage); data.writeInt(userId); mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0); reply.readException(); ComponentName res = ComponentName.readFromParcel(reply); data.recycle(); reply.recycle(); return res; }
上面說了ActivityManagerProxy作為binder通訊的客戶端,ActivityManagerNative 作為binder通訊的服務端; mRemote.transact()是binder通訊的客戶端發起方法,經過binder驅動,最後回到binder服務端ActivityManagerNative的onTransact()方法。
@Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { ....... switch (code) { case START_SERVICE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); IApplicationThread app = ApplicationThreadNative.asInterface(b); Intent service = Intent.CREATOR.createFromParcel(data); String resolvedType = data.readString(); String callingPackage = data.readString(); int userId = data.readInt(); ComponentName cn = startService(app, service, resolvedType, callingPackage, userId); reply.writeNoException(); ComponentName.writeToParcel(cn, reply); return true; } } ....... }
ActivityManagerNative的真正實現是ActivityManagerService,所以binder通訊的服務端的ActivityManagerService,
ActivityManagerProxy.startService()最終呼叫
ActivityManagerService.startService()。注意這就跨程序了,ActivityManagerService是一個服務端的程序。看ActivityManagerService中的startService方法。
ActivityManagerService.java
public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, String callingPackage, int userId) throws TransactionTooLargeException { ...... synchronized(this) { ....... ComponentName res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, callingPackage, userId); Binder.restoreCallingIdentity(origId); return res; } }
ActivityManagerService沒有直接幹這個活,而是把這個任務交給了mService, mService 是一個 ActiveServices 物件。在早期的安卓版本中並沒有這個類,後來重構時抽出這個類專門用來管理Service.
ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, String callingPackage, int userId) throws TransactionTooLargeException { ........ return startServiceInnerLocked(smap, service, r, callerFg, addToStarting); }
tartServiceInnerLocked呼叫了 bringUpServiceLocked(),bringUpServiceLocked()內部呼叫了realStartServiceLocked(),我們看realStartServiceLocked()方法。
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { ....... try { ....... app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); r.postNotification(); created = true; } catch (DeadObjectException e) { .... } finally { .... } requestServiceBindingsLocked(r, execInFg); updateServiceClientActivitiesLocked(app, null, true); // If the service is in the started state, and there are no // pending arguments, then fake up one so its onStartCommand() will // be called. if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) { r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), null, null)); } // 進入onStartCommand() sendServiceArgsLocked(r, execInFg, true); .... }
這裡的關鍵是
app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app 是要執行 Service 的程序對應的 ProcessRecord 物件,代表一個應用程序; 要區分一下,一般我們都是單方向通訊,客戶端將處理請求傳送給服務端,服務端處理後返回,如果要服務端向客戶端傳送一個“請求”呢?這裡的thread 是一個 ApplicationThreadProxy 物件,它是應用程序的 ApplicatonThread 物件在 AMS 端的代理,AMS 靠它來和應用程序進行通訊。所以AMS和應用程序可以雙向通訊了。
ApplicationThreadProxy.java
public final void scheduleCreateService(IBinder token, ServiceInfo info, CompatibilityInfo compatInfo, int processState) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeStrongBinder(token); info.writeToParcel(data, 0); compatInfo.writeToParcel(data, 0); data.writeInt(processState); try { mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); } catch (TransactionTooLargeException e) { throw e; } data.recycle(); }
執行mRemote.transact後,就會回撥ApplicationThreadNative的onTransact,這是Binder的套路。
ApplicationThreadNative.java
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case SCHEDULE_CREATE_SERVICE_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); IBinder token = data.readStrongBinder(); ServiceInfo info = ServiceInfo.CREATOR.createFromParcel(data); CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data); int processState = data.readInt(); scheduleCreateService(token, info, compatInfo, processState); return true; } ... }
內部呼叫scheduleCreateService,看上面的圖,可以知道,scheduleCreateService是屬於ApplicatonThread的。
ApplicatonThread.java
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類處理的,H類就是系統Hander,專門處理系統請求的; 比如一些Activity的生命週期等全在這裡面。這個 H物件是在應用程序的主執行緒中建立的,所以最終的結果是把建立 Service 的訊息傳到了主執行緒,因此Service是執行在主執行緒中的。
H.java
private class H extends Handler { ......... public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " codeToString(msg.what)); switch (msg.what) { case CREATE_SERVICE: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate"); handleCreateService((CreateServiceData)msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } }
ActivityThread.java
private void handleCreateService(CreateServiceData data) { ....... LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { // 反射載入Service java.lang.ClassLoader cl = packageInfo.getClassLoader(); service = (Service) cl.loadClass(data.info.name).newInstance(); } catch (Exception e) { ....... } try { if (localLOGV) Slog.v(TAG, "Creating service " data.info.name); //建立ContextImpl物件 ContextImpl context = ContextImpl.createAppContext(this, packageInfo); context.setOuterContext(service); //建立Application物件 Application app = packageInfo.makeApplication(false, mInstrumentation); service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); //回撥onCreate方法 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) { ....... } }
到此Service的onCreate就回撥了,那麼onStartCommand()何時回撥呢?在realStartServiceLocked中呼叫了sendServiceArgsLocked(r, execInFg, true),sendServiceArgsLocked與上面類似,最終也是傳送了一個(SERVICE_ARGS)訊息。
ApplicationThread.java
public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId, int flags ,Intent args) { ServiceArgsData s = new ServiceArgsData(); s.token = token; s.taskRemoved = taskRemoved; s.startId = startId; s.flags = flags; s.args = args; sendMessage(H.SERVICE_ARGS, s); }
ActivityThread.java
private void handleServiceArgs(ServiceArgsData data) { Service s = mServices.get(data.token); if (s != null) { try { if (data.args != null) { data.args.setExtrasClassLoader(s.getClassLoader()); data.args.prepareToEnterProcess(); } int res; if (!data.taskRemoved) { //onStartCommand回撥 res = s.onStartCommand(data.args, data.flags, data.startId); } else { s.onTaskRemoved(data.args); res = Service.START_TASK_REMOVED_COMPLETE; } QueuedWork.waitToFinish(); try { ActivityManagerNative.getDefault().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_START, data.startId, res); } catch (RemoteException e) { // nothing to do. } ensureJitEnabled(); } catch (Exception e) { ...... } } }
Service的onCreate的回撥和onStartCommand的回撥套路是完全一樣的,朋友們可以自己體會,onCreate的回撥先執行scheduleCreateService()方法,最終回撥Service.onCreate(); onStartCommand的回撥先執行scheduleServiceArgs()方法,最終回撥Service.onStartCommand()
總結一下:
IActivityManager介面中定義了AMS嚮應用程式(本例中即Service)提供的多種API,Activity通過ActivityManagerProxy就可以使用這些API,向AMS發出請求
所以是通過ActivityManagerProxy,呼叫ActivityManagerProxy的startService方法; 在內部呼叫transact,然後會呼叫ActivityManagerNative中的onTransact()方法,在該方法中,將會r完成AMS與Activity的連線並呼叫AMS的startService()方法,那麼AMS是如何Service所在的應用程式呢?比如scheduleCreateService
原來ApplicationThreadProxy 是應用程序的 ApplicatonThread 物件在 AMS 端的代理,AMS 靠它來和應用程序進行通訊。這就是Activity與AMS之間的雙向Binder連線;Activity用IActivityManager提供的APIActivityManagerService提出執行某個動作的請求(本例中是啟動RemoteService),ActivityManagerService通過IApplicationThread提供的API來控制Activity所在的應用程式
上面的分析省去了很多的內容,如果從程序角度看服務啟動過程。
- Process A程序:是指呼叫startService命令所在的程序,也就是啟動服務的發起端程序
- system_server程序:系統程序,是java framework框架的核心載體,裡面執行了大量的系統服務,比如這裡提供ApplicationThreadProxy,ActivityManagerService,這個兩個服務都執行在system_server程序的不同執行緒中
- Zygote程序:是由init程序孵化而來的,用於建立Java層程序的母體,所有的Java層程序都是由Zygote程序孵化而來
- Remote Service程序:遠端服務所在程序,是由Zygote程序孵化而來的用於執行Remote服務的程序。主執行緒主要負責Activity/Service等元件的生命週期以及UI相關操作都執行在這個執行緒; 另外,每個App程序中至少會有兩個binder執行緒 ApplicationThread和ActivityManagerProxy
啟動流程:
- Process A程序採用Binder IPC向system_server程序發起startService請求;
- system_server程序接收到請求後,向zygote程序傳送建立程序的請求;
- zygote程序fork出新的子程序Remote Service程序;
- Remote Service程序,通過Binder IPC向sytem_server程序發起attachApplication請求;
- system_server程序在收到請求後,進行一系列準備工作後,再通過binder IPC向remote Service程序傳送scheduleCreateService請求;
- Remote Service程序的binder執行緒在收到請求後,通過handler向主執行緒傳送CREATE_SERVICE訊息;
- 主執行緒在收到Message後,通過發射機制建立目標Service,並回撥Service.onCreate()方法。 到此,服務便正式啟動完成。當建立的是本地服務或者服務所屬程序已建立時,則無需經過上述步驟2、3,直接建立服務即可
有需要文章中完整程式碼的同學 : 現在私信傳送 “底層原始碼” 即可免費獲取
現在私信傳送 “進階” 還可以獲取《更多 Android 原始碼解析 核心筆記 面試真題》
最後我想說:
學習沒有捷徑可言,我們要注意記學習,不僅要記,還要寫心得體會,文字筆記、畫圖、總結等,方式很多,但是一定要自己認真去做,不要太相信自己的記憶,只有反覆記憶,加深理解才行
同時,對於程式設計師而言,不單單是死記硬背,我們有更好的方式去學習,比如寫demo去驗證。複習知識點時,要及時跟你做過的專案結合起來,這樣在面試時就知道怎麼聊了,由專案講到知識點,由一個知識點串聯到另一個知識點。複習到一定階段,可以嘗試著去把這些東西串聯起來,由點及面,形成知識體系
對於程式設計師來說,要學習的知識內容、技術有太多太多,要想不被環境淘汰就只有不斷提升自己,從來都是我們去適應環境,而不是環境來適應我們
技術是無止境的,你需要對自己提交的每一行程式碼、使用的每一個工具負責,不斷挖掘其底層原理,才能使自己的技術昇華到更高的層面
Android 架構師之路還很漫長,與君共勉