日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android Service演义

發布時間:2025/3/15 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android Service演义 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

(本文以Android 5.1為準)


1.概述


在Android平臺上,那種持續性工作一般都是由service來執行的。不少初學者總是搞不清service和線程、進程之間的關系,這當然會影響到他們開展具體的開發工作。

其實,簡單說起來,service和線程、進程是沒什么關系的。我們知道,在Android平臺上已經大幅度地弱化了進程的概念,取而代之的是一個個有意義的邏輯實體,比如activity、service等。Service實體必然要寄身到某個進程里才行,它也可以再啟動幾個線程來幫它干活兒。但是,說到底service只是一個邏輯實體、一個運行期上下文而已。

相比activity這種“操控UI界面的運行期上下文”,service這種上下文一般是沒有界面部分的。當然這里說的只是一般情況,有些特殊的service還是可以創建自己的界面的,比如當一個service需要顯現某種浮動面板時,就必須自己創建、銷毀界面了。

在Android系統內部的AMS里,是利用各種類型的Record節點來管理不同的運行期上下文的。比如以ActivityRecord來管理activity,以ServiceRecord來管理service。可是,線程這種東東可沒有對應的Record節點喔。一些初學者常常會在activity里啟動一個線程,從事某種耗時費力的工作,可是一旦activity被遮擋住,天知道它會在什么時候被系統砍掉,進而導致連應用進程也退出。從AMS的角度來看,它壓根就不知道用戶進程里還搞了個工作線程在干活兒,所以當它要干掉用戶進程時,是不會考慮用戶進程里還有沒有工作沒干完。

但如果是在service里啟動了工作線程,那么AMS一般是不會隨便砍掉service所在的進程的,所以耗時的工作也就可以順利進行了。

可是,我們也常常遇到那種“一次性處理”的工作,難道就不能臨時創建個線程來干活嗎?關于這一點,請大家留意,在Android平臺上應對這種情況的較好做法是創建一個IntentService派生類,而后覆蓋其onHandleIntent()成員函數。IntentService內部會自動為你啟動一個工作線程,并在工作線程里回調onHandleIntent()。當onHandleIntent()返回后,IntentService還會自動執行stopSelf()關閉自己。瞧瞧,多么自動化。

關于service,有兩個動作是誰都繞不開的,那就是startService()和bindService()。我們想知道,它們的內部機制到底是怎樣的?雖然網上已經有不少文章講述過這兩個動作,但是不同人理解技術的視角往往是不一樣的,本文我將以自己的視角來闡述它們。

?

2.Service機制


我們先來看一下Service類的代碼截選:
【frameworks/base/core/java/android/app/Service.java】

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {............private ActivityThread mThread = null;private String mClassName = null;private IBinder mToken = null;private Application mApplication = null;private IActivityManager mActivityManager = null;private boolean mStartCompatibility = false; }

Service是個抽象類,它間接繼承于Context,其繼承關系如下圖所示:

看了這張圖,請大家務必理解,Service只是個“上下文”(Context)對象而已,它和進程、線程是沒什么關系的。

在AMS中,負責管理service的ServiceRecord節點本身就是個binder實體。當AMS向應用進程發出語義,要求其創建service對象時,會把ServiceRecord通過binder機制“傳遞”給應用進程。這樣,應用進程的ActivityThread在處理AMS發來的語義時,就可以得到一個合法的binder代理,這個binder代理最終會被記錄在Service對象中,如此一來,Service實體就和系統里的ServiceRecord關聯起來了。我們畫個圖來說明,假如一個應用進程里啟動了兩個不同的Service,那么當service創建成功之后,AMS和用戶進程之間就會形成如下關系示意圖:

當然,如果用戶進程和service再多一點兒也完全沒問題,此時會形成下圖:

我們對圖中的ApplicationThread并不陌生,它記錄在ActivityThread中mAppThread域中。每當系統新fork一個用戶進程后,就會自動執行ActivityThread的attach()動作,里面會調用:

final IActivityManager mgr = ActivityManagerNative.getDefault(); . . . . . .mgr.attachApplication(mAppThread); . . . . . .

將ApplicationThread對象遠程“傳遞”給AMS,從而讓AMS得到一個合法的代理端。而當系統要求用戶進程創建service時,就會通過這個合法的代理端向用戶進程傳遞明確的語義。現在我們就從這里開始講解細節吧。

3.先說startService()

我們先來看啟動service的流程。要啟動一個service,我們一般是調用startService()。說穿了只是向AMS發起一個請求,導致AMS執行如下的startService()動作:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】

public ComponentName startService(IApplicationThread caller, Intent service, ? ? ? ? String resolvedType, int userId) {. . . . . .ComponentName res = mServices.startServiceLocked(caller, service,resolvedType, callingPid, callingUid, userId);. . . . . .return res;} }

其中的mServices域是ActiveServices類型的,其startServiceLocked()函數的代碼截選如下:
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】

ComponentName startServiceLocked(IApplicationThread caller, ? ? ? ? Intent service, String resolvedType, ? ? ? ? int callingPid, int callingUid, int userId) { . . . . . .// 必須先通過retrieveServiceLocked()找到(或創建)一個ServiceRecord節點ServiceLookupResult res = retrieveServiceLocked(service, resolvedType,callingPid, callingUid, userId, true, callerFg);. . . . . .ServiceRecord r = res.record;. . . . . .r.lastActivity = SystemClock.uptimeMillis();r.startRequested = true;r.delayedStop = false;r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),service, neededGrants));final ServiceMap smap = getServiceMap(r.userId);. . . . . .. . . . . .return startServiceInnerLocked(smap, service, r, callerFg, addToStarting); }

看到了嗎?必須先通過retrieveServiceLocked()找到(或創建)一個ServiceRecord節點,而后才能執行后續的啟動service的動作。請大家注意,在Android frameworks里有不少這樣的動作,基本上都是先創建xxxRecord節點,而后再向目標用戶進程傳遞語義,創建具體的對象。

retrieveServiceLocked()的代碼截選如下:

private ServiceLookupResult retrieveServiceLocked(Intent service, ? ? ? ? String resolvedType, int callingPid, int callingUid, int userId, ? ? ? ? boolean createIfNeeded, boolean callingFromFg) {ServiceRecord r = null;. . . . . .ServiceMap smap = getServiceMap(userId);final ComponentName comp = service.getComponent();if (comp != null) {r = smap.mServicesByName.get(comp);}if (r == null) {Intent.FilterComparison filter = new Intent.FilterComparison(service);r = smap.mServicesByIntent.get(filter);}if (r == null) {. . . . . .// 從PKMS處查到ServiceInfo. . . . . .ComponentName name = new ComponentName(sInfo.applicationInfo.packageName, sInfo.name);. . . . . .r = smap.mServicesByName.get(name);if (r == null && createIfNeeded) {. . . . . .r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);. . . . . .smap.mServicesByName.put(name, r);smap.mServicesByIntent.put(filter, r);. . . . . .}. . . . . .}if (r != null) {. . . . . .return new ServiceLookupResult(r, null);}return null; }

總之就是希望在AMS內部的相關表格里找到對應的ServiceRecord節點,如果找不到,就創建一個新節點,并插入到相應的表格中。

我手頭參考的是Android5.1的代碼,可以說這張表格大體上就是ServiceMap里的mServicesByName啦。當然,ServiceRecord節點一般還會同時記錄到其他表格里,比如mServicesByIntent表格,但現在我們不妨先只考慮mServicesByName,一切以便于理解為上。

事實上,在早期的Android代碼(比如Android 2.3版的代碼)中,是沒有那個ServiceMap的,那時的mServicesByName表格直接位于ActivityManagerService里,而且對應的域名叫作mServices。后來隨著Android的發展,需要支持多用戶以及其他一些概念,于是就搞出了個ServiceMap,并把原來的這張ServiceRecord表格搬到ServiceMap里了。我們可以畫一張示意圖,來說明Android代碼的變遷:

(Android 2.3版示意圖)


(Android 5.1版示意圖)

但是,不管ServiceRecord表格被放到哪里,其本質都是一致的。而AMS必須保證在實際啟動一個Service之前查到或創建對應的ServiceRecord節點。

在ServiceRecord類中有不少成員變量,其中有一個app域,專門用來記錄Service對應的用戶進程的ProcessRecord。當然,一開始這個app域是為null的,也就是說ServiceRecord節點還沒有和用戶進程關聯起來,待Service真正啟動之后,ServiceRecord的app域就有實際的值了。

現在我們繼續看startServiceLocked()函數,它在找到ServiceRecord節點之后,開始調用startServiceInnerLocked()。我們可以繪制一下相關的調用關系:

請注意上圖中bringUpServiceLocked()里的內容。此時大體上分三種情況:
1)如果ServiceRecord已經和Service寄身的用戶進程關聯起來了,此時ServiceRecord的app域以及app.thread域都是有值的,那么只調用 sendServiceArgsLocked()即可。這一步會讓Service間接走到大家熟悉的onStartCommand()。
2)如果尚未啟動Service寄身的用戶進程,那么需要調用mAm.startProcessLocked()啟動用戶進程。
3)如果Service寄身的用戶進程已經啟動,但尚未和ServiceRecord關聯起來,那么調用realStartServiceLocked(r, app, execInFg);這種情況下,會讓Service先走到onCreate(),而后再走到onStartCommand()。

我們重點看第三種情況,realStartServiceLocked()的調用示意圖如下:

看到了吧,無非是利用scheduleXXX()這樣的函數,來通知用戶進程去做什么事。以后大家看到這種以schedule打頭的函數,可以直接打開ActivityThread.java文件去查找其對應的實現函數。比如scheduleCreateService()的代碼如下:
【frameworks/base/core/java/android/app/ActivityThread.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); }

它發出的CREATE_SERVICE消息,會由ActivityThread的內嵌類H處理,H繼承于Handler,而且是在UI主線程里處理消息的。可以看到,處理CREATE_SERVICE消息的函數是handleCreateService():

case CREATE_SERVICE:Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");handleCreateService((CreateServiceData)msg.obj);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);break;

另外,請大家注意realStartServiceLocked()傳給scheduleCreateService()函數的第一個參數就是ServiceRecord類型的r,而ServiceRecord本身是個Binder實體噢。待“傳到”應用進程時,這個Binder實體對應的Binder代理被稱作token,記在了CreateServiceData對象的token域中。現在CreateServiceData對象又經由msg.obj傳遞到消息處理函數里,并進一步作為參數傳遞給handleCreateService()函數。

handleCreateService()的代碼如下:

private void handleCreateService(CreateServiceData data) {. . . . . .LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);Service service = null;. . . . . .java.lang.ClassLoader cl = packageInfo.getClassLoader();service = (Service) cl.loadClass(data.info.name).newInstance();. . . . . .ContextImpl context = ContextImpl.createAppContext(this, packageInfo);context.setOuterContext(service);Application app = packageInfo.makeApplication(false, mInstrumentation);// 注意,ServiceRecord實體對應的代理端,就是此處的data.token。service.attach(context, this, data.info.name, data.token, app,ActivityManagerNative.getDefault());service.onCreate();mServices.put(data.token, service);. . . . . . }

簡單地說就是,先利用反射機制創建出Service對象:

service = (Service) cl.loadClass(data.info.name).newInstance();

然后再調用該對象的onCreate()成員函數:

service.onCreate();

而因為handleCreateService()函數本身是在用戶進程的UI主線程里執行的,所以service的onCreate()函數也就是在UI主線程里執行的。常常會有人告誡新手,不要在onCreate()、onStart()里執行耗時的操作,現在大家知道是為什么了吧,因為在UI主線程里執行耗時的操作不但會引起界面卡頓,嚴重的還會導致ANR報錯。

另外,在執行到上面的service.attach()時,那個和ServiceRecord對應的代理端token也傳進來了:

public final void attach( ? ? ? ? Context context, ? ? ? ? ActivityThread thread, String className, IBinder token, ? ? ? ? Application application, Object activityManager) {attachBaseContext(context);mThread = thread; ? ? ? ? ??mClassName = className;mToken = token; ? ? // 注意這個token噢,它的對端就是AMS里的ServiceRecord!mApplication = application;mActivityManager = (IActivityManager)activityManager;mStartCompatibility = getApplicationInfo().targetSdkVersion< Build.VERSION_CODES.ECLAIR; }

可以看到,token記錄到ServiceRecord的mToken域了。

最后,新創建出的Service對象還會記錄進ActivityThread的mServices表格去,于是我們可以在前文示意圖的基礎上,再畫一張新圖:

這就是startService()啟動服務的大體過程。上圖并沒有繪制“調用startService()的用戶進程”,因為當Service啟動后,它基本上就和發起方沒什么關系了。

?

4.再說bindService()

接下來我們來說說bindService(),它可比startService()要麻煩一些。當一個用戶進程bindService()時,它需要先準備好一個實現了ServiceConnection接口的對象。ServiceConnection的定義如下:

public interface ServiceConnection {public void onServiceConnected(ComponentName name, IBinder service);public void onServiceDisconnected(ComponentName name); }

在Android平臺上,每當用戶調用bindService(),Android都將之視作是要建立一個新的“邏輯連接”。而當連接建立起來時,系統會回調ServiceConnection接口的onServiceConnected()。另一方面,那個onServiceDisconnected()函數卻不是在unbindService()時發生的。一般來說,當目標service所在的進程意外掛掉或者被殺掉時,系統才會回調onServiceDisconnected(),而且,此時并不會銷毀之前的邏輯連接,也就是說,那個“邏輯連接”仍然處于激活狀態,一旦service后續再次運行,系統會再次回調onServiceConnected()。

在Android平臺上,要和其他進程建立邏輯連接往往都需要利用binder機制。那么,發起bindService()的用戶進程又是在哪里創建邏輯連接需要的binder實體呢?我們可以看看bindServiceCommon()函數的代碼截選:
【frameworks/base/core/java/android/app/ContextImpl.java】

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, ? ? ? ? UserHandle user) {IServiceConnection sd;. . . . . .sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),mMainThread.getHandler(), flags); ? // 請注意返回的sd!. . . . . .IBinder token = getActivityToken();. . . . . .?int res = ActivityManagerNative.getDefault().bindService(mMainThread.getApplicationThread(), getActivityToken(),service, service.resolveTypeIfNeeded(getContentResolver()),sd, flags, user.getIdentifier());. . . . . . }

請大家注意mPackageInfo.getServiceDispatcher()那一句,它返回的就是binder實體。此處的mPackageInfo是用戶進程里和apk對應的LoadedApk對象。getServiceDispatcher()的代碼如下:
【frameworks/base/core/java/android/app/LoadedApk.java】

public final IServiceConnection getServiceDispatcher(ServiceConnection c,Context context, Handler handler, int flags) {synchronized (mServices) {LoadedApk.ServiceDispatcher sd = null;ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map =?mServices.get(context);if (map != null) {sd = map.get(c);}if (sd == null) {sd = new ServiceDispatcher(c, context, handler, flags);if (map == null) {map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();mServices.put(context, map);}map.put(c, sd);} else {sd.validate(context, handler);}return sd.getIServiceConnection(); ?// 注意,返回ServiceDispatcher內的binder實體} }

也就是說,先嘗試在LoadedApk的mServices表中查詢ServiceDispatcher對象,如果查不到,就重新創建一個。ServiceDispatcher對象會記錄下從用戶處傳來的ServiceConnection引用。而且,ServiceDispatcher對象內部還含有一個binder實體,現在我們可以通過調用sd.getIServiceConnection()一句,返回這個內部的binder實體。

現在我們畫一張示意圖,來說明發起bindService()動作的用戶進程中的樣子:

LoadedApk中的mServices是常見的表中表形式,定義如下:

private final ArrayMap<Context, ArrayMap<ServiceConnection,?LoadedApk.ServiceDispatcher>> mServices

這里比較有趣的是,第一層表的key類型為Context,大家不妨想一想,如果我們的應用里有兩個activity都去綁定同一個Service會怎么樣?很明顯,在mServices表里就會有兩個不同的子表,也會創建出兩個不同的ServiceDispatcher對象,而且即便我們在調用bindService()時傳入同一個ServiceConnection對象,依舊會有兩個ServiceDispatcher對象。示意圖如下:

說完了發起bindService()動作的用戶進程一側,接下來我們再來看AMS一側的代碼。對應的bindService()代碼如下:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】

public int bindService(IApplicationThread caller, IBinder token, ? ? ? ? Intent service, String resolvedType, ? ? ? ? IServiceConnection connection, int flags, int userId) {enforceNotIsolatedCaller("bindService");// Refuse possible leaked file descriptorsif (service != null && service.hasFileDescriptors() == true) {throw new IllegalArgumentException("File descriptors passed in Intent");}synchronized(this) {return mServices.bindServiceLocked(caller, token, service, resolvedType,connection, flags, userId);} }

主要工作集中在mServices.bindServiceLocked()一句。請注意bindService()的倒數第三個參數,它對應的就是上圖中ServiceDispatcher的mIServiceConnection域記錄的binder實體。至于bindService()的第二個參數IBinder token,其實記錄的是發起綁定動作的Activity的token,當然,如果發起者是其他非Activity型的對象,那么這個參數應該是null。

上面的代碼最終走到mServices.bindServiceLocked()一句。我們摘錄一下bindServiceLocked()的代碼:
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】

int bindServiceLocked(IApplicationThread caller, IBinder token,Intent service, String resolvedType,IServiceConnection connection, int flags, int userId) {. . . . . .final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);. . . . . .ActivityRecord activity = null;. . . . . .activity = ActivityRecord.isInStackLocked(token);. . . . . .ServiceLookupResult res = retrieveServiceLocked(service, resolvedType,Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);. . . . . .ServiceRecord s = res.record;. . . . . .?AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); ?// 注意這個b!ConnectionRecord c = new ConnectionRecord(b, activity,connection, flags, clientLabel, clientIntent);IBinder binder = connection.asBinder();ArrayList<ConnectionRecord> clist = s.connections.get(binder);. . . . . .clist.add(c);b.connections.add(c);. . . . . .b.client.connections.add(c);. . . . . .clist = mServiceConnections.get(binder);. . . . . .clist.add(c);if ((flags&Context.BIND_AUTO_CREATE) != 0) {. . . . . .if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {return 0;}}. . . . . .if (s.app != null && b.intent.received) {. . . . . .c.conn.connected(s.name, b.intent.binder); ?// 注意這個!. . . . . .if (b.intent.apps.size() == 1 && b.intent.doRebind) {requestServiceBindingLocked(s, b.intent, callerFg, true);}} else if (!b.intent.requested) {requestServiceBindingLocked(s, b.intent, callerFg, false); ?}. . . . . . }

盡管這個函數里有不少技術細節,而且涉及到多個映射表,但它的總體意思大概是這樣的。每當用戶調用bindService()時,Android都將之看作是要建立一個新的“邏輯連接”,而每個邏輯連接都對應一個ConnectionRecord節點,所以最終的表現肯定是向ServiceRecord內部的某張映射表里添加一個新的ConnectionRecord節點。當然,在實際運作時,這個節點還會記錄進其他幾個映射表里(比如系統總映射表),但這不影響我們理解問題。

那么為什么系統里會有那么多映射表呢?這大概是為了在復雜的網狀聯系中快速便捷地找到相關的節點。我們知道,一個用戶進程可以綁定多個Service,而一個Service也可以被多個用戶進程綁定,這就是網狀結構。示意圖如下:

前文我們已經說過,綁定時使用的ServiceConnection對象在發起端其實對應了一個ServiceDispatcher對象,現在,ServiceDispatcher的mIServiceConnection傳遞給bindServiceLocked(),也就是那個IServiceConnection connection參數。如果系統要建立“邏輯連接”,那么就需要把IServiceConnection代理端記錄到ConnectionRecord節點里。

ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags,?clientLabel, clientIntent);

另外,請大家注意bindServiceLocked()里那個重要的AppBindRecord節點。

AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); ?// 注意這個b!

相傳對于一個Service而言,有多少應用和它建立了綁定關系,就會有多少個AppBindRecord節點,要不怎么叫App-Bind呢?當然,一個應用里可以有多個地方發起綁定動作,所以AppBindRecord里需要用一個ArraySet<ConnectionRecord>記錄下每個綁定動作對應的邏輯連接節點。

有了這些認識后,我們可以畫一張“發起端用戶進程”和“系統進程”之間的示意圖:


在ConnectionRecord被記錄進合適的表后,要開始和目標service建立連接了。我們可以看到,bindServiceLocked()會嘗試呼叫起目標service。

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

看到這句if語句,大家應該知道調用bindService()時為什么常常要加上BIND_AUTO_CREATE了吧:

bindService(intent, conn, Context.BIND_AUTO_CREATE);

?

假設目標Service之前已經啟動過,那么現在的綁定流程會走到if (s.app != null && b.intent.received)分支里,于是調用到c.conn.connected()。

? ? ? ? if (s.app != null && b.intent.received) {. . . . . .c.conn.connected(s.name, b.intent.binder); ?// 注意這個!. . . . . .if (b.intent.apps.size() == 1 && b.intent.doRebind) {requestServiceBindingLocked(s, b.intent, callerFg, true);}} else if (!b.intent.requested) {requestServiceBindingLocked(s, b.intent, callerFg, false); ?}

但是,如果Service之前并未啟動,而且這次是我們首次綁定service,那么不就到不了c.conn.connected()了嗎?此時應該會走到else if 分支,調用到requestServiceBindingLocked(),該函數主要是向目標service發起綁定的請求,但是現在連service寄身的進程可能都還沒有啟動,request又有什么意義呢?所以,此處調用requestServiceBindingLocked()也許不會有什么重大意義。這并不是說requestServiceBindingLocked()不重要,而是說它真正起作用的地方也許不在這里。為了說明問題,我們得看一下剛剛調用的bringUpServiceLocked()函數:

private final String bringUpServiceLocked(ServiceRecord r, ? ? ? ? int intentFlags, boolean execInFg, boolean whileRestarting) {. . . . . .ProcessRecord app;. . . . . .if (app == null) {if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,"service", r.name, false, isolated, false)) == null) {. . . . . .bringDownServiceLocked(r);return msg;}. . . . . .}// 注意此處的mPendingServices!if (!mPendingServices.contains(r)) {mPendingServices.add(r);}. . . . . . }

bringUpServiceLocked()通過調用mAm.startProcessLocked()啟動目標service寄身的進程,而后會把ServiceRecord節點記入mPendingServices數組列表中。

待后續service寄身的進程成功啟動后,會輾轉調用到attachApplicationLocked(),該函數的代碼截選如下:

boolean attachApplicationLocked(ProcessRecord proc, String processName) throws RemoteException {. . . . . .if (mPendingServices.size() > 0) {ServiceRecord sr = null;. . . . . .for (int i=0; i<mPendingServices.size(); i++) {sr = mPendingServices.get(i);. . . . . .mPendingServices.remove(i);i--;proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode, mAm.mProcessStats); realStartServiceLocked(sr, proc, sr.createdFromFg);. . . . . .}. . . . . . }

也就是說,當目標service寄身的進程啟動后,會從mPendingServices數組列表中把ServiceRecord節點刪除,并進一步調用realStartServiceLocked():

private final void realStartServiceLocked(ServiceRecord r, ? ? ? ? ProcessRecord app, boolean execInFg) throws RemoteException {. . . . . .r.app = app;. . . . . .app.services.add(r);. . . . . .app.thread.scheduleCreateService(r, r.serviceInfo,mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),app.repProcState);. . . . . .requestServiceBindingsLocked(r, execInFg);. . . . . .sendServiceArgsLocked(r, execInFg, true);. . . . . . }

此處調用的app.thread.scheduleCreateService()會間接導致目標service走到大家熟悉的onCreate()。而后還會調用requestServiceBindingsLocked()。這里大概才是requestServiceBindingLocked()真正起作用的地方。

requestServiceBindingsLocked()的代碼如下:

private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) {for (int i=r.bindings.size()-1; i>=0; i--) {IntentBindRecord ibr = r.bindings.valueAt(i);if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {break;}} } private final boolean requestServiceBindingLocked(ServiceRecord r, ? ? ? ? IntentBindRecord i, boolean execInFg, boolean rebind) {. . . . . .r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,r.app.repProcState);if (!rebind) {i.requested = true;}i.hasBound = true;i.doRebind = false;. . . . . . }

終于看到調用scheduleBindService()了。

到了“Service所屬的進程”里,scheduleBindService()執行的代碼如下:

public final void scheduleBindService(IBinder token, Intent intent, ? ? ? ? boolean rebind, int processState) {updateProcessState(processState, false);BindServiceData s = new BindServiceData();s.token = token;?? ??? ?// 對應AMS里的ServiceRecords.intent = intent;s.rebind = rebind;......sendMessage(H.BIND_SERVICE, s); }

?

所發出的BIND_SERVICE消息,會導致service寄身的進程走到handleBindService():
【frameworks/base/core/java/android/app/ActivityThread.java】

private void handleBindService(BindServiceData data) {Service s = mServices.get(data.token);. . . . . .if (!data.rebind) {IBinder binder = s.onBind(data.intent);ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);} else {s.onRebind(data.intent);ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);}. . . . . . }

回調了目標service的onBind(),而后向AMS發布自己,即調用publishService()。

publishService()的第一個參數指代AMS里的ServiceRecord節點,而最后一個參數是目標Service的onBind()函數返回的服務對象。因此publish函數其實起的是銜接的作用。

在AMS一側,publish動作最終會走到publishServiceLocked():
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {. . . . . .Intent.FilterComparison filter?= new Intent.FilterComparison(intent);IntentBindRecord b = r.bindings.get(filter);if (b != null && !b.received) {b.binder = service; ? // 記錄下目標進程對應的binder代理b.requested = true;b.received = true;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);. . . . . .?c.conn.connected(r.name, service); ?// 通告bindService發起方. . . . . .}}}. . . . . . }

這里又牽扯到ServiceRecord的connections映射表,在介紹bindServiceLocked()函數時,我們其實已經看到過幾句相關的代碼了,現在再列舉一下:

? ? ? ? IBinder binder = connection.asBinder();ArrayList<ConnectionRecord> clist = s.connections.get(binder);. . . . . .clist.add(c);

也就是說,我們在綁定服務時創建的那個ConnectionRecord節點,會同時將該節點記錄進ServiceRecord的connections映射表,而映射表的key值就是邏輯上指向發起端的IServiceConnection代理。

為什么要有這個映射表呢?很簡單,就是為了快速地找出所有使用相同IServiceConnection.Stub對象,完成綁定動作的ConnectionRecord節點。請大家設想,我們完全可以在一個Activity里,使用同一個ServiceConnection對象發起多次綁定同一個service的動作,發起綁定時使用的intent也許會不同,但最終有可能綁定到同一個service,此時,上面代碼中s.connections.get(binder)得到的ArrayList就會含有多個元素啦。

好,介紹完connections映射表,我們繼續看publishServiceLocked()里的那句c.conn.connected()。ConnectionRecord節點的conn成員也是IServiceConnection類型的代理端,所以這一句調用最終是遠程調用到發起端的ServiceDispatcher里的mIServiceConnection對象,該對象是InnerConnection類型的。

InnerConnection的connected()函數如下:
【frameworks/base/core/java/android/app/LoadedApk.java】

private static class InnerConnection extends IServiceConnection.Stub {final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;. . . . . .public void connected(ComponentName name, IBinder service) throws RemoteException {LoadedApk.ServiceDispatcher sd = mDispatcher.get();if (sd != null) {sd.connected(name, service);}} }

而ServiceDispatcher的connected()函數如下:

public void connected(ComponentName name, IBinder service) {if (mActivityThread != null) {mActivityThread.post(new RunConnection(name, service, 0));} else {doConnected(name, service);} } private final class RunConnection implements Runnable {. . . . . .public void run() {if (mCommand == 0) {doConnected(mName, mService);} else if (mCommand == 1) {doDeath(mName, mService);}}. . . . . . }

?

【frameworks/base/core/java/android/app/LoadedApk.java】

public void doConnected(ComponentName name, IBinder service) {. . . . . .info = new ConnectionInfo();info.binder = service;info.deathMonitor = new DeathMonitor(name, service);try {service.linkToDeath(info.deathMonitor, 0);mActiveConnections.put(name, info);} catch (RemoteException e) {. . . . . .}. . . . . .// If there is a new service, it is now connected.if (service != null) {mConnection.onServiceConnected(name, service); ? // 終于看到onServiceConnected了!} }


至此,我們終于看到大家熟悉的onServiceConnected()回調啦!而傳來的service參數,就是我們希望綁定的那個service提供的binder代理。現在我們可以說已經打通了bindService()動作涉及的三方關系:發起方、AMS、目標Service。我們不妨再畫一張圖看看:

有關Service的基本機制和啟動流程,我們就先說這么多吧。以后我們再補充其他方面的內容。

原文地址:?https://my.oschina.net/youranhongcha/blog/710046

總結

以上是生活随笔為你收集整理的Android Service演义的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲一区二区网站 | 污视频网站免费在线观看 | 女人做爰全过程免费观看美女 | 亚洲人成人一区二区在线观看 | 一区二区三区四区日韩 | 人人妻人人澡人人爽国产一区 | 欧美30p | 国产妇女视频 | 在线播放你懂得 | 日批在线视频 | 激情综合啪啪 | 最新在线视频 | 吞精囗交69激情欧美 | 亚洲性欧美色 | 天堂网在线观看 | 亚洲国产大片 | 欧美a网| 亚洲一区二区三区四区五区午夜 | 国产毛片18 | 日本久久中文字幕 | 亚洲国产精品欧美久久 | 日韩一级片网站 | 熟女人妻aⅴ一区二区三区60路 | 一本大道av伊人久久综合 | 天堂久久网 | 欧美午夜精品久久久久免费视 | 日本免费色 | 亚洲免费一区二区 | 亚洲成人免费av | 宅男噜噜噜 | 亚洲色图36p | 亚洲狼人在线 | 天天色综合1 | 黑人vs日本人ⅹxxxhd | 日本中文字幕有码 | 清纯唯美亚洲综合 | 亚洲专区在线播放 | 国精产品99永久一区一区 | 色呦呦网站在线观看 | 中文字幕看片 | 捆绑最紧bdsm视频 | 最近中文字幕在线观看 | 日韩欧美激情视频 | 午夜a视频| 黄色特级毛片 | 青草久久网 | 自拍毛片| 天堂一区在线观看 | 日韩在线第二页 | 国产无遮挡免费视频 | 在线观看国产亚洲 | 精品国产精品三级精品av网址 | 久久黄色录像 | 国产乱码久久久久 | 诱惑av| 涩涩视频软件 | 天天摸夜夜 | 国产午夜无码视频在线观看 | 日韩色在线观看 | 天天艹天天爽 | 美女综合网 | 无码视频一区二区三区 | 熟妇高潮一区二区三区 | www.在线看 | 永久免费视频网站直接看 | 黄色91免费版 | 欧美一区二区成人 | 欧美精品日韩在线 | 91精品国产99久久久久久 | 手机福利视频 | 恶虐女帝安卓汉化版最新版本 | 国产污视频在线播放 | av国产免费| 日本在线看 | 久久婷婷一区 | 亚洲免费不卡视频 | 亚洲精品乱码久久久久久写真 | 国产美女流白浆 | 最新av网站在线观看 | 伊人色网站| 日韩成人小视频 | 伊人久久一区二区 | 国产精品一区免费 | 57pao国产成永久免费视频 | 熟睡人妻被讨厌的公侵犯 | 男人插女人的网站 | 伊人久久成人网 | 成人www | 国产片淫乱18一级毛片动态图 | 国产情侣呻吟对白高潮 | 国产嫩草在线 | 国产精品成人久久久久久久 | www射| www一级片| 美女被猛网站 | 日韩高清在线一区 | 手机在线看片福利 | 91久久精品国产91性色tv | 激情网五月天 |