網站首頁 編程語言 正文
前言
一開始的目標是解決各種各樣的ANR問題的,我們知道,ANR總體上分有四種類型,這四種類型有三種是和四大組件相對應的,所以,如果想了解ANR發生的根因,對安卓四大組件的實現流程是必須要了解的,都不明白ANR如何觸發的,怎么能完美的解決ANR的問題呢?
所以會寫一系列的文章,來分析四大組建的實現原理,同時也順帶講解四種類型的ANR是如何發生的。
本篇主要介紹service的完整實現流程,下一篇文章介紹Service中的ANR是如何產生的。
一.APP側啟動Service
其實啟動service和啟動Activity是很相似的,都是APP通知系統側,由系統側完成的整個流程。
1.1前臺和后臺啟動
無論是Activity,還是service,還是Application,都繼承自Context的抽象類,所以可以使用Context的各種功能,就比如這了要介紹的啟動前臺/后臺service。
Context在安卓中,使用了一種典型的代理模式,我們調用的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);
}
果然和我猜測的差不多,無論前臺還是后臺啟動,其實最終都會走到一個方法中,只是配置參數的區別而已。最終都會走執行startServiceCommon方法。
1.2startServiceCommon
該方法中,通過binder通知系統的AMS完成對應的service的啟動操作:
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
getOpPackageName(), getAttributionTag(), user.getIdentifier());
接下來,我們就看下系統側是如何處理Service啟動流程的。
二.系統側分發處理Service的啟動邏輯
系統側的處理我主要分為3塊來講:
1.系統接受APP側的通知并轉發
2.系統側委托ActiveServices負責完成的處理流程
3.收到APP側執行完成的回調,進行收尾操作
2.1AMS接受啟動service的通知
APP側持有system_server進程的binder,上面講到,它會通過binder方法startService完成對系統側的通知。所以AMS的startService會收到這個通知。
我們看下代碼,發現AMS會把整個service的邏輯全部交由ActiveServices來處理,代碼如下:
try {
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, callingFeatureId, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
系統代碼startServiceLocked方法中,代碼雖然很長,但是卻遵循著一個不變的宗旨:位語句,即前面處理各種異常的分支邏輯,把核心流程留到方法的最終來處理。
所以我們直接看startServiceLocked方法的最后一部分即可:
final ComponentName realResult =
startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg,
allowBackgroundActivityStarts, backgroundActivityStartsToken);
startServiceInnerLocked方法中,處理邏輯也是比較簡單的,最終會交給bringUpServiceLocked方法來進行處理。而bringUpServiceLocked方法中則最終會交給realStartServiceLocked完成整個流程。好像系統代碼都喜喜歡用realStart,Activity啟動的流程中也有一個方法叫realStartActivity。
2.2realStartServiceLocked流程
realStartServiceLocked方法中,我們總結為三個流程:
1.bumpServiceExecutingLocked,啟動超時檢查。
2.thread.scheduleCreateService通知APP一側去創建Service。
3.sendServiceArgsLocked通知APP執行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創建service
thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.mState.getReportedProcState());
r.postNotification();
created = true;
...
//3.通知執行service生命流程
sendServiceArgsLocked(r, execInFg, true);
...
}
三.系統側通知APP啟動Service
一般情況下,APP側會收到系統側發過來兩種類型的通知,
第一種:創建Service的任務通知
第二種:執行Service生命流程的通知,通知Service執行onStartCommand方法。
ApplicationThread接受通知并創建Service
系統側持有APP側的binder,會通過scheduleCreateService這個binder方法通知APP一側進行相應的操作。而APP側,完成這個工作接收的就是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中,會切換到主線程執行ActivityThread的handleCreateService方法。
主要執行了如下的幾段邏輯:
1.如果是首次創建App進程的話,則需要重新創建Application;
2.創建Service對象;
3.調用service的attach方法進行關聯;
4.調用service的onCreate生命周期方法;
5.創建完成后,通過serviceDoneExecuting通知系統側創建完成。
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接受通知并執行Service的生命流程
同樣的,這里完成接受的是,仍然是ApplicationThread中的方法。這個流程中的接受方法是scheduleServiceArgs方法。
ApplicationThread中,收到通知后,通過handler把任務轉交到主線程。
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中切換到主線程會執行ActivityThread的handleServiceArgs方法。
handleServiceArgs方法主要會完成以下幾件事:
1.找到對應的service,調用起onStartCommand方法;
2.通知系統側回調完成。
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);
}
}
}
}
發我們發現,不論是創建service,還是通知執行service的生命流程,最終都執行了一個完成的通知,這有何意圖呢?是的,這個意圖就是和ANR相關的,我們下一章來講了。
四.總結
前面一一講了實現的原理,我們最后再來做一個總結,盡量用一張圖+幾句話的方式來概括。
1.無論前臺啟動還是后臺啟動,最終都會走到ContextImpl這個最終實現類中的方法,完成和AMS的交互。
2.AMS中主要是ActiveServices完成的整個流程。其核心方法是realStartServiceLocked。
他首先啟動一個延時消息,通過延時消息進行超時的監測。
然后通知APP去生成Service。
通知APP側去完成Service的生命周期流程onStartCommand。
3.收到APP側執行完成的通知后,則取消注冊延時消息。
原文鏈接:https://blog.csdn.net/rzleilei/article/details/128452528
相關推薦
- 2022-08-30 c語言學習——動態內存分配
- 2023-06-13 C語言中函數返回值不一致問題_C 語言
- 2022-11-19 Kotlin協程之Flow異常示例處理_Android
- 2022-11-28 Flutter?Widgets之標簽類控件Chip詳解_IOS
- 2022-12-05 一文教你如何優雅處理Golang中的異常_Golang
- 2022-05-22 python?使用tkinter與messagebox寫界面和彈窗_python
- 2022-03-25 Postman返回中文亂碼的解決方案(postman請求結果亂碼)
- 2023-05-15 使用Bash讀取和處理CSV文件的方法_linux shell
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支