Android?Service完整實(shí)現(xiàn)流程分析
前言
一開始的目標(biāo)是解決各種各樣的ANR問題的,我們知道,ANR總體上分有四種類型,這四種類型有三種是和四大組件相對應(yīng)的,所以,如果想了解ANR發(fā)生的根因,對安卓四大組件的實(shí)現(xiàn)流程是必須要了解的,都不明白ANR如何觸發(fā)的,怎么能完美的解決ANR的問題呢?
所以會寫一系列的文章,來分析四大組建的實(shí)現(xiàn)原理,同時也順帶講解四種類型的ANR是如何發(fā)生的。
本篇主要介紹service的完整實(shí)現(xiàn)流程,下一篇文章介紹Service中的ANR是如何產(chǎn)生的。
一.APP側(cè)啟動Service
其實(shí)啟動service和啟動Activity是很相似的,都是APP通知系統(tǒng)側(cè),由系統(tǒng)側(cè)完成的整個流程。
1.1前臺和后臺啟動
無論是Activity,還是service,還是Application,都繼承自Context的抽象類,所以可以使用Context的各種功能,就比如這了要介紹的啟動前臺/后臺service。
Context在安卓中,使用了一種典型的代理模式,我們調(diào)用的startService或者startForegroundService方法,最終都會委托給ContextImpl中的startService和startForegroundService來處理的。我們就來看下ContextImpl中的這兩個方法:
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
@Override
public ComponentName startForegroundService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, true, mUser);
}果然和我猜測的差不多,無論前臺還是后臺啟動,其實(shí)最終都會走到一個方法中,只是配置參數(shù)的區(qū)別而已。最終都會走執(zhí)行startServiceCommon方法。
1.2startServiceCommon
該方法中,通過binder通知系統(tǒng)的AMS完成對應(yīng)的service的啟動操作:
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
getOpPackageName(), getAttributionTag(), user.getIdentifier());接下來,我們就看下系統(tǒng)側(cè)是如何處理Service啟動流程的。
二.系統(tǒng)側(cè)分發(fā)處理Service的啟動邏輯
系統(tǒng)側(cè)的處理我主要分為3塊來講:
1.系統(tǒng)接受APP側(cè)的通知并轉(zhuǎn)發(fā)
2.系統(tǒng)側(cè)委托ActiveServices負(fù)責(zé)完成的處理流程
3.收到APP側(cè)執(zhí)行完成的回調(diào),進(jìn)行收尾操作
2.1AMS接受啟動service的通知
APP側(cè)持有system_server進(jìn)程的binder,上面講到,它會通過binder方法startService完成對系統(tǒng)側(cè)的通知。所以AMS的startService會收到這個通知。
我們看下代碼,發(fā)現(xiàn)AMS會把整個service的邏輯全部交由ActiveServices來處理,代碼如下:
try {
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, callingFeatureId, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}系統(tǒng)代碼startServiceLocked方法中,代碼雖然很長,但是卻遵循著一個不變的宗旨:位語句,即前面處理各種異常的分支邏輯,把核心流程留到方法的最終來處理。
所以我們直接看startServiceLocked方法的最后一部分即可:
final ComponentName realResult =
startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg,
allowBackgroundActivityStarts, backgroundActivityStartsToken);startServiceInnerLocked方法中,處理邏輯也是比較簡單的,最終會交給bringUpServiceLocked方法來進(jìn)行處理。而bringUpServiceLocked方法中則最終會交給realStartServiceLocked完成整個流程。好像系統(tǒng)代碼都喜喜歡用realStart,Activity啟動的流程中也有一個方法叫realStartActivity。
2.2realStartServiceLocked流程
realStartServiceLocked方法中,我們總結(jié)為三個流程:
1.bumpServiceExecutingLocked,啟動超時檢查。
2.thread.scheduleCreateService通知APP一側(cè)去創(chuàng)建Service。
3.sendServiceArgsLocked通知APP執(zhí)行Service的生命流程。
private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
boolean enqueueOomAdj) throws RemoteException {
//1.啟動超時檢查
bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
...
//2.通知APP創(chuàng)建service
thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.mState.getReportedProcState());
r.postNotification();
created = true;
...
//3.通知執(zhí)行service生命流程
sendServiceArgsLocked(r, execInFg, true);
...
}三.系統(tǒng)側(cè)通知APP啟動Service
一般情況下,APP側(cè)會收到系統(tǒng)側(cè)發(fā)過來兩種類型的通知,
第一種:創(chuàng)建Service的任務(wù)通知
第二種:執(zhí)行Service生命流程的通知,通知Service執(zhí)行onStartCommand方法。
ApplicationThread接受通知并創(chuàng)建Service
系統(tǒng)側(cè)持有APP側(cè)的binder,會通過scheduleCreateService這個binder方法通知APP一側(cè)進(jìn)行相應(yīng)的操作。而APP側(cè),完成這個工作接收的就是ApplicationThread中的scheduleCreateService方法。該方法收到通知后,通過handler切換到主線程處理:
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);
}handle中,會切換到主線程執(zhí)行ActivityThread的handleCreateService方法。
主要執(zhí)行了如下的幾段邏輯:
1.如果是首次創(chuàng)建App進(jìn)程的話,則需要重新創(chuàng)建Application;
2.創(chuàng)建Service對象;
3.調(diào)用service的attach方法進(jìn)行關(guān)聯(lián);
4.調(diào)用service的onCreate生命周期方法;
5.創(chuàng)建完成后,通過serviceDoneExecuting通知系統(tǒng)側(cè)創(chuàng)建完成。
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
Application app = packageInfo.makeApplication(false, mInstrumentation);
final java.lang.ClassLoader cl;
if (data.info.splitName != null) {
cl = packageInfo.getSplitClassLoader(data.info.splitName);
} else {
cl = packageInfo.getClassLoader();
}
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
ContextImpl context = ContextImpl.getImpl(service
.createServiceBaseContext(this, packageInfo));
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
}
if (data.info.attributionTags != null && data.info.attributionTags.length > 0) {
final String attributionTag = data.info.attributionTags[0];
context = (ContextImpl) context.createAttributionContext(attributionTag);
}
// Service resources must be initialized with the same loaders as the application
// context.
context.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
mServicesData.put(data.token, data);
mServices.put(data.token, service);
try {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}ApplicationThread接受通知并執(zhí)行Service的生命流程
同樣的,這里完成接受的是,仍然是ApplicationThread中的方法。這個流程中的接受方法是scheduleServiceArgs方法。
ApplicationThread中,收到通知后,通過handler把任務(wù)轉(zhuǎn)交到主線程。
public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
List<ServiceStartArgs> list = args.getList();
for (int i = 0; i < list.size(); i++) {
ServiceStartArgs ssa = list.get(i);
ServiceArgsData s = new ServiceArgsData();
s.token = token;
s.taskRemoved = ssa.taskRemoved;
s.startId = ssa.startId;
s.flags = ssa.flags;
s.args = ssa.args;
sendMessage(H.SERVICE_ARGS, s);
}
}接下來handler中切換到主線程會執(zhí)行ActivityThread的handleServiceArgs方法。
handleServiceArgs方法主要會完成以下幾件事:
1.找到對應(yīng)的service,調(diào)用起onStartCommand方法;
2.通知系統(tǒng)側(cè)回調(diào)完成。
private void handleServiceArgs(ServiceArgsData data) {
CreateServiceData createData = mServicesData.get(data.token);
Service s = mServices.get(data.token);
if (s != null) {
try {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
data.args.prepareToEnterProcess(isProtectedComponent(createData.info),
s.getAttributionSource());
}
int res;
if (!data.taskRemoved) {
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
QueuedWork.waitToFinish();
try {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to start service " + s
+ " with " + data.args + ": " + e.toString(), e);
}
}
}
}發(fā)我們發(fā)現(xiàn),不論是創(chuàng)建service,還是通知執(zhí)行service的生命流程,最終都執(zhí)行了一個完成的通知,這有何意圖呢?是的,這個意圖就是和ANR相關(guān)的,我們下一章來講了。
四.總結(jié)
前面一一講了實(shí)現(xiàn)的原理,我們最后再來做一個總結(jié),盡量用一張圖+幾句話的方式來概括。

1.無論前臺啟動還是后臺啟動,最終都會走到ContextImpl這個最終實(shí)現(xiàn)類中的方法,完成和AMS的交互。
2.AMS中主要是ActiveServices完成的整個流程。其核心方法是realStartServiceLocked。
他首先啟動一個延時消息,通過延時消息進(jìn)行超時的監(jiān)測。
然后通知APP去生成Service。
通知APP側(cè)去完成Service的生命周期流程onStartCommand。
3.收到APP側(cè)執(zhí)行完成的通知后,則取消注冊延時消息。
到此這篇關(guān)于Android Service完整實(shí)現(xiàn)流程分析的文章就介紹到這了,更多相關(guān)Android Service內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解關(guān)于MIUI 9沉浸式狀態(tài)欄的最新適配
由于各系統(tǒng)版本的限制,沉浸式狀態(tài)欄對系統(tǒng)有要求,本篇文章主要介紹了詳解關(guān)于MIUI 9沉浸式狀態(tài)欄的最新適配,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-05-05
Android自定義SwipeRefreshLayout高仿微信朋友圈下拉刷新
這篇文章主要以社交APP的BOSS微信為例,介紹了Android自定義SwipeRefreshLayout高仿微信朋友圈下拉刷新,感興趣的小伙伴們可以參考一下2016-07-07
Android創(chuàng)建文件時出現(xiàn)java.io.IOException:?Operation?not?permitte
最近使用android10創(chuàng)建文件失敗,并拋出權(quán)限異常,這篇文章主要給大家介紹了Android創(chuàng)建文件時出現(xiàn)java.io.IOException:?Operation?not?permitted異常的解決方法,需要的朋友可以參考下2023-05-05
Android實(shí)現(xiàn)一個帶粘連效果的LoadingBar
Loading效果相信大家應(yīng)該都實(shí)現(xiàn)過,最近發(fā)現(xiàn)了一個不錯的效果,決定分享給大家,所以下面這篇文章主要給大家介紹了關(guān)于利用Android實(shí)現(xiàn)一個帶粘連效果的LoadingBar的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-12-12
Android開發(fā)之登錄驗(yàn)證實(shí)例教程
這篇文章主要介紹了Android開發(fā)之登錄驗(yàn)證實(shí)現(xiàn)方法,包括發(fā)送數(shù)據(jù)、服務(wù)器端驗(yàn)證、配置文件等,需要的朋友可以參考下2014-08-08
Android實(shí)戰(zhàn)RecyclerView頭部尾部添加方法示例
本篇文章主要介紹了Android實(shí)戰(zhàn)RecyclerView頭部尾部添加方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
從0快速搭建一個實(shí)用的MVVM框架(超詳細(xì))
這篇文章主要介紹了從0搭建一個實(shí)用的MVVM框架,結(jié)合Jetpack,構(gòu)建快速開發(fā)的MVVM框架,支持快速生成ListActivity、ListFragment,主要是基于MVVM進(jìn)行快速開發(fā)上手即用,需要的朋友可以參考下2022-03-03
Android開發(fā)中一個簡單實(shí)用的調(diào)試應(yīng)用技巧分享
這篇文章主要跟大家分享了一個簡單實(shí)用的Android調(diào)試應(yīng)用技巧,文中介紹的非常詳細(xì),相信對大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友下面來一起看看吧。2017-05-05

