一、前言
在Service组件StartService()方式启动流程分析文章中,针对Context#startService()启动Service流程分析了源码,其实关于Service启动还有一个比较重要的点是Service启动的ANR,因为因为线上出现了上百例的"executing service " + service.shortName的异常。
本文中所有源码都只粘贴关键代码,无关紧要的代码已忽略。
二、Service-ANR原理
2.1 Service启动ANR原理简述
Service的ANR触发原理,是在启动Service前使用Handler发送一个延时的Message(埋炸弹过程),然后在Service启动完成后remove掉这个Message(拆炸弹过程)。如果在指定的延迟时间内没有remove掉这个Message,那么就会触发ANR(没有在炸弹爆炸前拆掉就会爆炸),弹出AppNotResponding的弹窗。
其实这个机制跟Windows/MacOS的应用程序无响应,是类似的交互设计。
2.2 前台Service VS 后台Service的区别
2.2.1 前台Service
前台Service是一种在通知栏中显示持续通知的服务,它通常用于执行用户明确知晓的任务,比如音乐播放器、定位服务等。前台Service在系统内部被视为用户正在主动使用的组件,因此它具有更高的优先级和较低的系统资源限制。在使用前台Service时,必须在通知栏中显示一个通知,以告知用户有一个正在运行的Service,并且通常还应该提供一些与该Service相关的有用信息。
2.2.3 后台Service
后台Service是一种不会在通知栏中显示通知的服务。它用于执行一些不需要用户直接交互或注意的任务,例如数据同步、网络请求等。后台Service具有较低的系统优先级,系统可能会在资源紧张的情况下终止这些服务,以释放资源。
系统默认service都是后台的,可以通过startForeground()把该service提升到foreground优先级,那么adj便会成为PERCEPTIBLE_APP_ADJ(可感知的级别),这个级别的app一般不会轻易被杀。
- 从Android O开始提供startForegroundService开启前台Service:
Intent intent = new Intent(MainActivity.this, ANRService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
startService(intent);
2.3 Service启动ANR源码执行过程
ps: 还是基于Android SDK28源码分析
基于文章:Service组件StartService()方式启动流程分析的总结,我们已经很清楚,通过startService的方式启动Service的源码过程。因此,本文直接从com.android.server.am.ActiveServices#bringUpServiceLocked方法的源码开始分析,如有不清楚前置的启动流程的同学,可以参考我之前的文章,然后打开AS对照看下这部分的代码。
2.3.1 ActiveServices#bringUpServiceLocked
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
realStartServiceLocked(r, app, execInFg);
}
2.3.2 ActiveServices#realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
// ...
// 会转调到scheduleServiceTimeoutLocked(r.app)方法进行埋炸弹操作
bumpServiceExecutingLocked(r, execInFg, "create");
// 炸弹埋下了,开始引爆计时
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
// 调整oomAdj优先级
mAm.updateOomAdjLocked();
boolean created = false;
try {
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
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) {
// 进程已经kill了,或者binder通信失败了,直接kill掉进程,
// 通过app.thread向ActivityThread.H发消息报告程序奔溃了
mAm.appDiedLocked(app);
throw e;
} finally {
if (!created) {
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
// serviceDoneExecutingLocked方法内会拆除炸弹
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
// Cleanup.
if (newService) {
app.services.remove(r);
r.app = null;
}
// Retry.
if (!inDestroying) {
scheduleServiceRestartLocked(r, false);
}
}
// ...
}
}
- 从如上realStartSreviceLocked()实现逻辑看,如果是新创建(非首次启动的Serive,冷启动)Service,ANR超时时间的计算是从创建Service实例->回调Service#onCreate结束;
- app.thread.scheduleCreateService:切到主线程执行创建Service实例,并回调Service的attach、onCreate方法。这里面我们能控制耗时的只有onCreate方法,所以发现Service ANR了,都会去看主线程是否有Blocked、Service的onCreate方法是否有耗时操作。
- 实际验证,在Serice的onCreate方法中Thread.sleep(20_000),查看确实会ANR
2.3.3 埋炸弹过程:ActiveServices#bumpServiceExecutingLocked
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
// ...
scheduleServiceTimeoutLocked(r.app);
// ...
}
- com.android.server.am.ActiveServices#scheduleServiceTimeoutLocked
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
- 这里需要知道,炸弹的爆炸时间在ActiveServices中定义了三个:
// 前台Service的超时时间是20s
static final int SERVICE_TIMEOUT = 20*1000;
// 后台Service的超时时间是200s,是前台超时时间的10倍
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
// How long the startForegroundService() grace period is to get around to
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;
2.3.4 拆炸弹过程:ActiveServices#serviceDoneExecutingLocked
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
// ...
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
// ...
}
- 通过以上源码分析过程,我们可以简单总结成如下时序图,供理解整体过程:
2.3.5 炸弹爆炸出发ANR弹窗过程
如上文分析的,ANR弹窗其实就是一个sendMessageDelayed()方式发送的一个Message,想要了解ANR炸弹这么爆炸的,其实检索这个what值为ActivityManagerService.SERVICE_TIMEOUT_MSG的消息处理过程即可。
这个消息的Handler对应的handleMessage方法实现代码在AMS.java中。
- com.android.server.am.ActivityManagerService.MainHandler#handleMessage
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
// ...
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
case SERVICE_FOREGROUND_TIMEOUT_MSG: {
mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
} break;
case SERVICE_FOREGROUND_CRASH_MSG: {
mServices.serviceForegroundCrash(
(ProcessRecord) msg.obj, msg.getData().getCharSequence(SERVICE_RECORD_KEY));
} break;
// ...
}
}
- com.android.server.am.ActiveServices#serviceTimeout
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
final long now = SystemClock.uptimeMillis();
final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
for (int i=proc.executingServices.size()-1; i>=0; i--) {
ServiceRecord sr = proc.executingServices.valueAt(i);
if (sr.executingStart < maxTime) {
timeout = sr;
break;
}
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(timeout);
timeout.dump(pw, " ");
pw.close();
mLastAnrDump = sw.toString();
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
// 这一句对于我们分析问题比较关键,如果有Service的ANR,
// 就会在log中有这样的前缀打印:executing service Service.shortName
anrMessage = "executing service " + timeout.shortName;
} else {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
}
}
// 真正触发ANR弹窗的位置
if (anrMessage != null) {
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
这里记住anrMessage的格式是:executing service Service.shortName,代表的是Service的启动超时。
- com.android.server.am.AppErrors#appNotResponding
final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) {
if (mService.mController != null) {
try {
// 0 == continue, -1 = kill process immediately
// !!关键:mService.mController的实现类是:
// com.android.server.am.ActivityManagerShellCommand.MyActivityController
int res = mService.mController.appEarlyNotResponding(
app.processName, app.pid, annotation);
if (res < 0 && app.pid != MY_PID) {
app.kill("anr", true);
}
} catch (RemoteException e) {
mService.mController = null;
Watchdog.getInstance().setActivityController(null);
}
long anrTime = SystemClock.uptimeMillis();
if (ActivityManagerService.MONITOR_CPU_USAGE) {
mService.updateCpuStatsNow();
}
// Unless configured otherwise, swallow ANRs in background processes
// & kill the process.
// 读取开发者选项中的“显示后台ANR”开关
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
boolean isSilentANR;
// Don't dump other PIDs if it's a background ANR
isSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);
// Log the ANR to the main log.
StringBuilder info = new StringBuilder();
info.setLength(0);
// 这里可以看出写入到日志文件中的格式是:ANR in processName,
// 比如:我们应用包名是com.techmix.myapp,
// 那可在搜索时,直接输入ANR in com.techminx.myapp搜索,可更高效定位到ANR的trace位置
info.append("ANR in ").append(app.processName);
if (activity != null && activity.shortComponentName != null) {
info.append(" (").append(activity.shortComponentName).append(")");
}
info.append("\n");
info.append("PID: ").append(app.pid).append("\n");
if (annotation != null) {
// 这里的reason还是serviceTimeout中定义的Service启动的anrMessage字符串:
// "executing service Service.shortName",没有多余的更明细的分类了,具体是哪一步ANR了。
info.append("Reason: ").append(annotation).append("\n");
}
if (parent != null && parent != activity) {
info.append("Parent: ").append(parent.shortComponentName).append("\n");
}
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
// For background ANRs, don't pass the ProcessCpuTracker to
// avoid spending 1/2 second collecting stats to rank lastPids.
File tracesFile = ActivityManagerService.dumpStackTraces(true, firstPids,
(isSilentANR) ? null : processCpuTracker,
(isSilentANR) ? null : lastPids, nativePids);
// 写入cpu占用信息到anr log中
String cpuInfo = null;
if (ActivityManagerService.MONITOR_CPU_USAGE) {
mService.updateCpuStatsNow();
synchronized (mService.mProcessCpuTracker) {
cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
}
info.append(processCpuTracker.printCurrentLoad());
info.append(cpuInfo);
}
info.append(processCpuTracker.printCurrentState(anrTime));
// ANR log写入到dropbox文件夹中,annotation变量就是
// com.android.server.am.ActiveServices#serviceTimeout中传入的anrMessage变量
mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
cpuInfo, tracesFile, null);
synchronized (mService) {
// 静默ANR的定义?
if (isSilentANR) {
app.kill("bg anr", true);
return;
}
// 通过Handler发送ANR弹窗的dialog,这里直接跟进
// ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG这个消息的handleMessage处理逻辑即可
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
// 注意这里的mService是AMS
mService.mUiHandler.sendMessage(msg);
}
}
- 静默ANR的定义?
- 弹出ANR弹窗的逻辑代码:com.android.server.am.ActivityManagerService.UiHandler#handleMessage
case SHOW_NOT_RESPONDING_UI_MSG: {
// 还是转调到AppErrors中的方法去实现了,所以这里只是用AMS中的UiHandler切换了一下线程而已
// 最开始的startService方法,从应用主线程
// ContextImpl#startService->AMS#startService(),后者其实是执行在binder线程池的线程里
// 面的,是子线程。所以这里通过消息的方式切到主线程
mAppErrors.handleShowAnrUi(msg);
ensureBootCompleted();
} break;
- com.android.server.am.AppErrors#handleShowAnrUi
// 跟ANR相关的变量直接存储在了ProcessRecord.java类中,每个进程单独维护一个
boolean notResponding; // does the app have a not responding dialog?
Dialog anrDialog; // dialog being displayed due to app not resp.
void handleShowAnrUi(Message msg) {
Dialog dialogToShow = null;
synchronized (mService) {
AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
final ProcessRecord proc = data.proc;
if (proc == null) {
Slog.e(TAG, "handleShowAnrUi: proc is null");
return;
}
if (proc.anrDialog != null) {
Slog.e(TAG, "App already has anr dialog: " + proc);
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
AppNotRespondingDialog.ALREADY_SHOWING);
return;
}
// 这个ANR的广播,应用进程如果注册了能接收到吗?
Intent intent = new Intent("android.intent.action.ANR");
if (!mService.mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
}
mService.broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
if (mService.canShowErrorDialogs() || showBackground) {
dialogToShow = new AppNotRespondingDialog(mService, mContext, data);
proc.anrDialog = dialogToShow;
} else {
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
AppNotRespondingDialog.CANT_SHOW);
// 如果ANR弹窗是关闭状态下,直接kill当前应用进程了
// 跟ANR弹窗中点关闭应用一样,多是调用AMS#killAppAtUsersRequest方法
// 关闭当前进程
mService.killAppAtUsersRequest(proc, null);
}
}
// If we've created a crash dialog, show it without the lock held
if (dialogToShow != null) {
dialogToShow.show();
}
}
- com.android.server.am.AppErrors#killAppAtUserRequestLocked
void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) {
app.crashing = false;
app.crashingReport = null;
app.notResponding = false;
app.notRespondingReport = null;
if (app.anrDialog == fromDialog) {
app.anrDialog = null;
}
if (app.waitDialog == fromDialog) {
app.waitDialog = null;
}
// 这里的MY_PID是定义在AMS中的:static final int MY_PID = myPid();
// 所以这里只要是有效的应用pid,都是能进入if逻辑分支中的
if (app.pid > 0 && app.pid != MY_PID) {
handleAppCrashLocked(app, "user-terminated" /*reason*/,
null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
app.kill("user request after error", true);
}
}
app.kill()调用的是ProcessRecord#kill(),最终转调到AMS#killProcessGroup()方法了。这里AMS方式kill掉进程的,在Android的logcat中其实都能搜索到,Activity Manager killing的字样的。
2.4 Service的哪些相关方法是有ANR埋炸弹——计时引爆逻辑的?
需要注意:Service对于ANR的定义其实不止onCreate中,onBind、onStartCommand耗时超过20s其实都会触发ANR,下面进行详细分析。
2.4.1 Service#onCreate
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
// ...
// 会转调到scheduleServiceTimeoutLocked(r.app)方法进行埋炸弹操作
bumpServiceExecutingLocked(r, execInFg, "create");
// 炸弹埋下了,开始引爆计时,拆炸弹是在ActivityThread中执行完创建Servie实例、
// 回调onCreate方法之后进行
// (1)
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
}
- (1)ActivityThread#scheduleCreateService()方法源码
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
java.lang.ClassLoader cl = packageInfo.getClassLoader();
// (1) 反射创建Service实例,直接Clazz.newInstance(),由AppComponentFactory实现
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
// (2)创建ApplicationContext做为Service中的mBase变量,
// 也就是实际的ContextImpl实例
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
//(3)创建Application实例,也是通过反射创建,也是由AppComponentFactory实现
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
mServices.put(data.token, service);
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
2.4.2 Service#onStartCommand
- Service#onStartCommand()方法埋炸弹代码
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
boolean oomAdjusted) throws TransactionTooLargeException {
// ...
// 开始埋炸弹
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
mAm.updateOomAdjLocked(r.app, true);
}
Exception caughtException = null;
try {
// (2) 转调到ActivityThread#scheduleServiceArgs()
r.app.thread.scheduleServiceArgs(r, slice);
} catch (TransactionTooLargeException e) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
+ " args, first: " + args.get(0).args);
Slog.w(TAG, "Failed delivering service starts", e);
caughtException = e;
} catch (RemoteException e) {
// Remote process gone... we'll let the normal cleanup take care of this.
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
Slog.w(TAG, "Failed delivering service starts", e);
caughtException = e;
} catch (Exception e) {
Slog.w(TAG, "Unexpected exception", e);
caughtException = e;
}
if (caughtException != null) {
// Keep nesting count correct
final boolean inDestroying = mDestroyingServices.contains(r);
for (int i = 0; i < args.size(); i++) {
// 拆炸弹
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
}
if (caughtException instanceof TransactionTooLargeException) {
throw (TransactionTooLargeException)caughtException;
}
}
}
- app.thread.scheduleServiceArg会转到ActivityThread中,通过H.sendMessage(),最后调用Service的onStartComman方法传入intent中的参数
2.4.3 Service#onBind/onRebind
- Service#onBind()方法埋炸弹代码
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManager.getService().publishService(data.token,
data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
}
2.4.4 Service#onUnbind
- Service#unBind()方法埋炸弹代码
private final void bringDownServiceLocked(ServiceRecord r) {
// Tell the service that it has been unbound.
if (r.app != null && r.app.thread != null) {
for (int i=r.bindings.size()-1; i>=0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing down binding " + ibr
+ ": hasBound=" + ibr.hasBound);
if (ibr.hasBound) {
try {
bumpServiceExecutingLocked(r, false, "bring down unbind");
mAm.updateOomAdjLocked(r.app, true);
ibr.hasBound = false;
ibr.requested = false;
r.app.thread.scheduleUnbindService(r,
ibr.intent.getIntent());
} catch (Exception e) {
Slog.w(TAG, "Exception when unbinding service "
+ r.shortName, e);
serviceProcessGoneLocked(r);
}
}
}
}
}
app.thread.scheduleUnbindService方法中,会给Service#onUnbind进行埋炸弹:
private void handleUnbindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
// onUnbind方法耗时是应用程序能控制的
boolean doRebind = s.onUnbind(data.intent);
try {
if (doRebind) {
ActivityManager.getService().unbindFinished(
data.token, data.intent, doRebind);
} else {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to unbind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
2.4.5 Service#onDestroy
- Service#onDestroy()方法埋炸弹
这块的炸弹计时包含了两个可控的方法,Service#onDestroy()和QueuedWork.waitToFinish(),后者是我们平时通过apply方法修改SP时加入的Runnable任务,会在onDestroy执行后进行同步阻塞写入。
埋炸弹源码:
private void handleStopService(IBinder token) {
Service s = mServices.remove(token);
// (1)
s.onDestroy();
s.detachAndCleanUp();
Context context = s.getBaseContext();
if (context instanceof ContextImpl) {
final String who = s.getClassName();
((ContextImpl) context).scheduleFinalCleanup(who, "Service");
}
// (2)
QueuedWork.waitToFinish();
ActivityManager.getService().serviceDoneExecuting(token, SERVICE_DONE_EXECUTING_STOP, 0, 0);
}
通过分析实现代码,上述就(1)s.onDestroy()和(2)QueuedWork.waitToFinish()是我们可控的。文章来源:https://www.toymoban.com/news/detail-605379.html
通过上述分析,我们得出结论,Service可重写方法中,共有5个有炸弹引爆计时。在ActivityThread$H中对应的Message为:文章来源地址https://www.toymoban.com/news/detail-605379.html
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115; // 包含onStartCommand和onTaskRemoved方法
public static final int STOP_SERVICE = 116;
public static final int BIND_SERVICE = 121; // 包含onBind和onReBind方法
public static final int UNBIND_SERVICE = 122;
- 总结Service中除了dump、onTrimMemory、onConfigurationChanged是没有埋炸弹的,其余可重写代码均有炸弹计时,超时都会触发ANR,并且在写代码时需要注意回调方法的执行顺序问题,比如:onCreate->onStartCommand这条执行路径上,onCreate阻塞10s,onStartCommand阻塞10s,那也会触发ANR。另外,从源码中我们也不难看出,其实ANR的计时范围还包含了我们无法控制的Framework层的逻辑代码,所以我在试测时,发现根本不用20s就会ANR!!
2.4.6 总结
- 实际写代码验证结果如下:
/**
* @author TechMix
* @date 2023年7月31日 23:00
* @description 验证Service哪些回调方法会有ANR埋炸弹——爆炸计时逻辑
*/
class ANRService : Service() {
companion object {
private const val TAG = "yyg_ANRService"
}
/**
* 无ANR计时炸弹
*/
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
Log.d(TAG, "onConfigurationChanged: ")
}
/**
* 无ANR计时炸弹
*/
override fun onLowMemory() {
super.onLowMemory()
Log.d(TAG, "onLowMemory: ")
}
/**
* 无ANR计时炸弹
*/
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
Log.d(TAG, "onTrimMemory: ")
}
/**
* 有ANR计时炸弹,同onStartCommand()方法,是二选一的分支代码
* if (!data.taskRemoved) {
* res = s.onStartCommand(data.args, data.flags, data.startId);
* } else {
* s.onTaskRemoved(data.args);
* res = Service.START_TASK_REMOVED_COMPLETE;
* }
*/
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
Log.d(TAG, "onTaskRemoved: ")
}
/**
* 无ANR计时炸弹
*/
override fun dump(fd: FileDescriptor?, writer: PrintWriter?, args: Array<out String>?) {
super.dump(fd, writer, args)
Log.d(TAG, "dump: ")
}
/**
* 有埋ANR炸弹
*/
override fun onCreate() {
super.onCreate()
// Thread.sleep(19_000)
Log.d(TAG, "onCreate: ")
}
/**
* 有埋ANR炸弹,是在onStartCommand方法最前面回调的。
*/
override fun onStart(intent: Intent?, startId: Int) {
super.onStart(intent, startId)
Thread.sleep(19_000)
Log.d(TAG, "onStart: ")
}
/**
* 有埋ANR炸弹
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// Thread.sleep(18_000)
Log.d(TAG, "onStartCommand: ")
return super.onStartCommand(intent, flags, startId)
}
/**
* 有埋ANR炸弹
*/
override fun onBind(intent: Intent): IBinder? {
Log.d(TAG, "onBind: ")
return null
}
/**
* 有埋ANR炸弹,同onBind()方法,是二选一的分支代码
*/
override fun onRebind(intent: Intent?) {
Log.d(TAG, "onRebind: ")
super.onRebind(intent)
Thread.sleep(19_000)
}
/**
* 有埋ANR炸弹
*/
override fun onUnbind(intent: Intent?): Boolean {
Log.d(TAG, "onUnbind: ")
return super.onUnbind(intent)
}
/**
* 有埋ANR炸弹
* !!注意:ANR计时包含了QueuedWork.waitToFinish()的耗时(SP操作apply方法修改后,还没写入的任务,
* 会在调用这个方法时同步阻塞执行文件写入。)
*/
override fun onDestroy() {
Log.d(TAG, "onDestroy: ")
super.onDestroy()
}
}
- 阅读Android源码的感受:
google工程师的代码,其实业务逻辑代码并不见得比我们写的优雅,技术上还是跟我们一样的就是用那些东西,Handler、各种成员变量。但是整体看起来是比较清晰的,不过跟我们写代码一样,方法多了,像ActiveServices那一堆什么startXxxLocked、brindDownXx x之类的,太多了就看的一脸懵逼,只能跟进关键的代码片段了。但是对于异常的处理是比较到位的,关键位置都有相应的log,并且能够控制打开/关闭,能给调试带来很大便捷,这点我们是值得借鉴学习的。
另外一个就是业务代码味道没有那么重,技术转化成产品能力比较强,感觉就像是实现了某些技术,然后技术之上赋能了产品需要的能力的感觉。
到了这里,关于Android Service启动ANR原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!