Android Service启动ANR原理

这篇具有很好参考价值的文章主要介绍了Android Service启动ANR原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

    在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的弹窗。
Android Service启动ANR原理,Android四大组件相关,Android Framework,开发经验总结,android
    其实这个机制跟Windows/MacOS的应用程序无响应,是类似的交互设计。
Android Service启动ANR原理,Android四大组件相关,Android Framework,开发经验总结,android
Android Service启动ANR原理,Android四大组件相关,Android Framework,开发经验总结,android

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); 
	// ...       
}
  • 通过以上源码分析过程,我们可以简单总结成如下时序图,供理解整体过程:
    Android Service启动ANR原理,Android四大组件相关,Android Framework,开发经验总结,android
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()是我们可控的。

通过上述分析,我们得出结论,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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ANR原理篇 - service/broadcast/provider超时机制

    ANR概述 ANR基础篇 - Trace.txt文件分析 ANR基础篇 - Input系统 ANR基础篇 - 相关系统知识简介 ANR原理篇 - ANR原理总览 ANR原理篇 - ANR弹框是如何显示出来的 ANR原理篇 - service/broadcast/provider超时机制 ANR原理篇 - Input超时机制 ANR原理篇 - ANR信息收集过程

    2024年02月05日
    浏览(38)
  • Android Framework学习之Activity启动原理

    Android 13.0 Activity启动原理逻辑流程图如下:

    2024年02月05日
    浏览(54)
  • Android framework学习指南之Launcher启动过程原理分析

    Launcher是一个用来显示系统中已经安装的应用程序的应用程序,Launcher 在启动过程中会请求PackageManagerService 返回系统中已经安装的应用程序的信息,并将这些信息封装成一个快捷图标列表显示在系统屏幕上,这样用户可以通过点击这些快捷图标来启动相应的应用程序,它的作

    2024年02月03日
    浏览(50)
  • 安卓四大组件——Service服务(内含Binder的简单解释)

    对于Service这个组件很多同学都会把它跟线程搞混,所以我们有必要先缕清一下基础知识。 线程:比进程更小的执行单元,每个进程可能有多条线程,线程需要放在一个 进程中才能执行,线程由程序负责管理,而进程则由系统进行调度! 多线程的理解:并行执行多个条指令,

    2024年02月07日
    浏览(49)
  • 【Android Framework (八) 】- Service

    1,挂载文件系统,创建文件目录 调用selinux_setup权限安全相关 2,初始化内存空间 初始化属性服务 创建Epoll 注册监听子进程重启异常操作等,对子进程进行线程守护 3,startPropertyServic 开启属性服务 进行监听 4,LoadBootScripts 加载init.rc文件 进行解析 调用do_class_start 文件开启

    2024年02月01日
    浏览(37)
  • Android四大组件之服务

    为什么要使用服务呢? 从上面的文字说,我们知道这个服务是用于执行长期后台运行的操作。有些时候,我们没有界面,但是程序仍然需要工作。比如说,我们播放音乐,在后台播放音乐。比如说,我们下载任务,在后台下载文件。这些都是没有界面 的后台运行程序,这些

    2024年02月14日
    浏览(80)
  • Android 四大组件之广播

    在Android应用开发中,广播组件是一项关键技术,被广泛用于各种任务和场景。无论是发送系统级广播,还是在应用内部实现组件间的通信,了解和掌握广播组件的工作原理和使用方法对于每个Android开发者来说都是至关重要的。本文旨在帮助你深入了解Android广播组件,并给出

    2024年02月06日
    浏览(47)
  • [Android 四大组件] --- Activity

    ​​Activity​​是一个Android的应用组件,它提供屏幕进行交互。每个Activity都会获得一个用于绘制其用户界面的窗口,窗口可以充满哦屏幕也可以小于屏幕并浮动在其他窗口之上。 一个应用通常是由多个彼此松散联系的Activity组成,一般会指定应用中的某个Activity为主活动,也

    2024年02月10日
    浏览(44)
  • [Android 四大组件] --- BroadcastReceiver

    BroadcastReceiver(广播接收器)即广播,是一个全局的监听器。 Android 广播分为两个角色:广播发送者、广播接受者。 广播按照类型分为两种,一种是全局广播,另一种是本地广播 全局广播:就是发出的广播被其他任意应用程序接收,或者可以接收来自其他任意应用程序的广播

    2024年02月10日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包