ANR系列(一)——ANR源码拆解之Service的触发

前言

关于Service的ANR场景还是比较少见的,它的ANR设计原理也是比较简单,在很多监控的方案中都可以看得到

ANR触发场景

通过查阅Android官方文档,我们知道出现以下任何情况,系统都会针对我们的应用触发ANR:

  • Service Timeout:比如前台服务在20s内未执行完成,后台服务Timeout时间是前台服务的10倍,200s
  • BroadcastQueue Timeout:比如前台广播在10s内未执行完成,后台60s
  • ContentProvider Timeout:内容提供者,在publish过超时10s
  • InputDispatching Timeout: 输入事件分发超时5s,包括按键和触摸事件

Service的工作机制

在了解Service触发ANR之前,要先了解Service的工作机制

ANR系列(一)——ANR源码拆解之Service的触发_第1张图片

  • ContextImpl:ContextImpl是Android一个很重要的数据结构,它是Context的具体实现,Context中的大部分逻辑都是由ContextImpl来完成
  • ActivityManagerService:简称AMS,继承自ActivityManagerNavite(简称AMN),AMN继承自Binder并实现了IActivityManager这个Binder接口,因此AMS也是一个Binder,它是IActivityManager的具体实现
  • ActiveServices:辅助AMS进行Service管理的的类,包括Service的启动,绑定和停止等
  • ServiceRecord:Service的记录类,可以简单理解为Service的一个栈
  • ActivityThread:ActivityThread并不是一个Thread类,但代表了Android的主线程,其中的main方法是整个APP的入口,主要在Application进程中管理执行主线程,以及调度和执行活动和广播,和活动管理请求的其它操作

Service的ANR机制

  1. 埋炸弹:指的是在启动Service前,发送延时消息,如果Service在指定的是时间没有拆炸弹,则会引爆ANR
  2. 拆炸弹:在指定时间内,顺利运行Service服务,运行完成之后会拆掉一开始预埋下来的炸弹
  3. 引爆炸弹:在指定的时间未成功顺利运行Service服务,则会处理延时消息,引发ANR弹窗,引爆炸弹

Service埋定时炸弹

Service的启动过程分为两种

  1. 通过StartService启动Service
  2. 通过bindService启动Service

两种启动方式最终的调用链都来到ActiveServices.realStartServiceLocked()

private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
        IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
        boolean enqueueOomAdj) throws RemoteException {
    
    ......
    
    // 1.发送delay消息(SERVICE_TIMEOUT_MSG)
    bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);

    try {
        // 2.创建Service对象,并且调用onCreate()
        thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                app.mState.getReportedProcState());
     
    } catch (DeadObjectException e) {
        Slog.w(TAG, "Application dead when creating service " + r);
        mAm.appDiedLocked(app, "Died when creating service");
        throw e;
    } finally {
        ......
    }
    ......
}

private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why,
        @Nullable String oomAdjReason) {
        ......
	    scheduleServiceTimeoutLocked(r.app);
	    ......
}

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
        return;
    }
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
            ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

ActiveServices.realStartServiceLocked()做了两个事情

  1. 通过mHandler发送延时消息,在这里将会埋下炸弹
  2. 创建Service正式执行Service的启动逻辑,并且调用ServiceonCreate()

从发送的延时时间可以看出,Service的炸弹是指前台服务Timeout时间为20s,后台服务Timeout时间是前台服务的10倍,200s

static final int SERVICE_TIMEOUT = 20*1000;
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

Service拆炸弹

Service正常运行情况下,最终会执行到ActivityThreadhandleCreateService方法

private void handleCreateService(CreateServiceData data) {
	......
	try {
        ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
    }
	......
}

ActiveServices我们找到了这个移除SERVICE_TIMEOUT_MSG消息的方法serviceDoneExecutingLocked

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing, boolean enqueueOomAdj) {
        ......
        if (psr.numberOfExecutingServices() == 0) {
            if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
                   "No more executingServices of " + r.shortInstanceName);
            mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
        } 
	    ......
}

Service引爆炸弹

在创建Service之前如果消息没被移除,也就代表着Service没有被顺利的执行下来,就会导致Handler去处理当前的Timeout消息

final class MainHandler extends Handler {

    public MainHandler(Looper looper) {
        super(looper, null , true);
    }

    @Override
    public void handleMessage(Message msg) {
        ...
        // 1.处理消息
        case SERVICE_TIMEOUT_MSG: {
            mServices.serviceTimeout((ProcessRecord) msg.obj);
        }
        ...
    }
}

void serviceTimeout(ProcessRecord proc) {
    String anrMessage = null;

    ......
    if (anrMessage != null) {
        // 2.触发ANR弹窗
        mAm.mAnrHelper.appNotResponding(proc, anrMessage);
    }
}

appNotResponding方法内会开始生成trace文件等信息,然后通过ActivityManagerServicemUiHandler发送SHOW_NOT_RESPONDING_UI_MSG展示ANR弹窗

你可能感兴趣的:(ANR合集,android,java,开发语言)