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

news2024/11/26 20:29:22

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


文章目录

  • 系列文章目录
  • 前言
  • 一、Service超时机制
    • 1.1 埋炸弹
      • 1.1.1 AS.realStartServiceLocked
      • 1.1.2 AS.bumpServiceExecutingLocked
    • 1.2 拆炸弹
      • 1.2.1 AT.handleCreateService
      • 1.2.2 AS.serviceDoneExecutingLocked
    • 1.3 引爆炸弹
      • 1.3.1 MainHandler.handleMessage
      • 1.3.2 AS.serviceTimeout
  • 二、BroadcastReceiver超时机制
    • 2.1 埋炸弹
      • 2.1.1 processNextBroadcast
      • 2.1.2 setBroadcastTimeoutLocked
    • 2.2 拆炸弹
      • 2.2.1 sendFinished
      • 2.2.2 cancelBroadcastTimeoutLocked
    • 2.3 引爆炸弹
      • 2.3.1 BroadcastHandler.handleMessage
      • 2.3.2 broadcastTimeoutLocked
      • 2.3.3 AppNotResponding
  • 三、ContentProvider超时机制
    • 3.1 埋炸弹
      • 3.1.1 AMS.attachApplicationLocked
    • 3.2 拆炸弹
      • 3.2.1 AMS.publishContentProviders
    • 3.3 引爆炸弹
      • 3.3.1 MainHandler.handleMessage
      • 3.3.2 AMS.processContentProviderPublishTimedOutLocked
      • 3.3.3 AMS.cleanupAppInLaunchingProvidersLocked
      • 3.3.4 AMS.removeProcessLocked
  • 小节
  • 总结


前言

我们开发中常见ANR场景如下:

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

以上分类详情可见Google官方文档ANRs定义:
在这里插入图片描述

以上4个场景中,service、broadcast、provider的超时机制较为相似,可以类比为一个引爆炸弹的过程,
整个流程包含三部分组成: 埋炸弹, 拆炸弹, 引爆炸弹,后续章节详细分析。

Input的超时检测机则截然不同,更像一个扫雷的过程,详细可见ANR原理篇 - Input超时机制。


提示:以下篇章,我们详细分析下service、broadcast、provider的超时机制

一、Service超时机制

Service Timeout是位于”ActivityManager”线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时触发。
对于Service有两类:

  • 对于前台服务,则超时为SERVICE_TIMEOUT = 20s;
  • 对于后台服务,则超时为SERVICE_BACKGROUND_TIMEOUT = 200s

由变量ProcessRecord.execServicesFg来决定是否前台启动

1.1 埋炸弹

分析前需熟悉Service启动流程:
在这里插入图片描述
在第5步,Service进程attach到system_server进程的过程中会调用realStartServiceLocked()方法来埋下炸弹.

1.1.1 AS.realStartServiceLocked

ActiveServices.java

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
    ...
    //发送delay消息(SERVICE_TIMEOUT_MSG),【见小节1.1.2】
    bumpServiceExecutingLocked(r, execInFg, "create");
    try {
        ...
        //最终执行服务的onCreate()方法
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
    } catch (DeadObjectException e) {
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        ...
    }
}

1.1.2 AS.bumpServiceExecutingLocked

private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    ... 
    scheduleServiceTimeoutLocked(r.app);
}

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    long now = SystemClock.uptimeMillis();
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    
    //当超时后仍没有remove该SERVICE_TIMEOUT_MSG消息,则执行service Timeout流程【见1.3.1】
    mAm.mHandler.sendMessageAtTime(msg,
        proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}

该方法的主要工作发送delay消息(SERVICE_TIMEOUT_MSG). 炸弹已埋下, 我们并不希望炸弹被引爆, 那么就需要在炸弹爆炸之前拆除炸弹.

1.2 拆炸弹

在system_server进程AS.realStartServiceLocked()调用的过程会埋下一颗炸弹, 超时没有启动完成则会爆炸. 那么什么时候会拆除这颗炸弹的引线呢? 经过Binder等层层调用进入目标进程的主线程handleCreateService()的过程.

1.2.1 AT.handleCreateService

ActivityThread.java

private void handleCreateService(CreateServiceData data) {
        ...
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        Service service = (Service) cl.loadClass(data.info.name).newInstance();
        ...

        try {
            //创建ContextImpl对象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            //创建Application对象
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            //调用服务onCreate()方法 
            service.onCreate();
            
            //拆除炸弹引线[见小节1.2.2]
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (Exception e) {
            ...
        }
    }

在这个过程会创建目标服务对象,以及回调onCreate()方法, 紧接再次经过多次调用回到system_server来执行serviceDoneExecuting.

1.2.2 AS.serviceDoneExecutingLocked

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) {
    ...
    if (r.executeNesting <= 0) {
        if (r.app != null) {
            r.app.execServicesFg = false;
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                //当前服务所在进程中没有正在执行的service
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
        ...
    }
    ...
}

该方法的主要工作是当service启动完成,则移除服务超时消息SERVICE_TIMEOUT_MSG。

1.3 引爆炸弹

前面介绍了埋炸弹和拆炸弹的过程, 如果在炸弹倒计时结束之前成功拆卸炸弹,那么就没有爆炸的机会, 但总有些极端情况下无法即时拆除炸弹,导致炸弹爆炸, 其结果就是App发生ANR. 接下来,来看看炸弹爆炸的现场:

在system_server进程中有一个Handler线程, 名叫”ActivityManager”.当倒计时结束便会向该Handler线程发送 一条信息SERVICE_TIMEOUT_MSG.

1.3.1 MainHandler.handleMessage

ActivityManagerService.java ::MainHandler

final class MainHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case SERVICE_TIMEOUT_MSG: {
                ...
                //【见小节1.3.2】
                mServices.serviceTimeout((ProcessRecord)msg.obj);
            } break;
            ...
        }
        ...
    }
}

1.3.2 AS.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);
            anrMessage = "executing service " + timeout.shortName;
        }
    }

    if (anrMessage != null) {
        //当存在timeout的service,则执行appNotResponding
        mAm.appNotResponding(proc, null, null, false, anrMessage);
    }
}

其中anrMessage的内容为”executing service [发送超时serviceRecord信息]”.

二、BroadcastReceiver超时机制

BroadcastReceiver Timeout是位于”ActivityManager”线程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息时触发。

对于广播队列有两个: foreground队列和background队列:

  • 前台广播,则超时为BROADCAST_FG_TIMEOUT = 10s;
  • 后台广播,则超时为BROADCAST_BG_TIMEOUT = 60s

2.1 埋炸弹

分析前需熟悉广播启动流程.
在这里插入图片描述

如上图流程8通过调用 processNextBroadcast来处理广播.其流程为先处理并行广播,再处理当前有序广播,最后获取并处理下条有序广播.

2.1.1 processNextBroadcast

BroadcastQueue.java

final void processNextBroadcast(boolean fromMsg) {
    synchronized(mService) {
        ...
        //part 2: 处理当前有序广播
        do {
            r = mOrderedBroadcasts.get(0);
            //获取所有该广播所有的接收者
            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
            if (mService.mProcessesReady && r.dispatchTime > 0) {
                long now = SystemClock.uptimeMillis();
                if ((numReceivers > 0) &&
                        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                    //当广播处理时间超时,则强制结束这条广播【见小节2.3.2】
                    broadcastTimeoutLocked(false);
                    ...
                }
            }
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                if (r.resultTo != null) {
                    //处理广播消息消息
                    performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
                    r.resultTo = null;
                }
                //拆炸弹【见小节2.2.2】
                cancelBroadcastTimeoutLocked();
            }
        } while (r == null);
        ...

        //part 3: 获取下条有序广播
        r.receiverTime = SystemClock.uptimeMillis();
        if (!mPendingBroadcastTimeoutMessage) {
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            //埋炸弹【见小节2.1.2】
            setBroadcastTimeoutLocked(timeoutTime);
        }
        ...
    }
}

对于广播超时处理时机:

  1. 首先在part3的过程中setBroadcastTimeoutLocked(timeoutTime) 设置超时广播消息;
  2. 然后在part2根据广播处理情况来处理:
    • 当广播接收者等待时间过长,则调用broadcastTimeoutLocked(false);
    • 当执行完广播,则调用cancelBroadcastTimeoutLocked;

2.1.2 setBroadcastTimeoutLocked

final void setBroadcastTimeoutLocked(long timeoutTime) {
    if (! mPendingBroadcastTimeoutMessage) {
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

设置定时广播BROADCAST_TIMEOUT_MSG,即当前往后推mTimeoutPeriod时间广播还没处理完毕,则进入广播超时流程。

2.2 拆炸弹

broadcast跟service超时机制大抵相同,但有一个非常隐蔽的技能点,那就是通过静态注册的广播超时会受SharedPreferences(简称SP)的影响。

2.2.1 sendFinished

关于广播是否考虑SP的情况取决于如下代码:

public final void finish() {
    if (mType == TYPE_COMPONENT) {
        final IActivityManager mgr = ActivityManager.getService();
        if (QueuedWork.hasPendingWork()) {
            //当SP有未同步到磁盘的工作,则需等待其完成,才告知系统已完成该广播
            QueuedWork.queue(new Runnable() {
                public void run() {
                    sendFinished(mgr);
                }
            }, false);
        } else {
            sendFinished(mgr);
        }
    } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
        final IActivityManager mgr = ActivityManager.getService();
        sendFinished(mgr);
    }
}

可见,只有XML静态注册的广播超时检测过程会考虑是否有SP尚未完成,动态广播并不受其影响。

2.2.2 cancelBroadcastTimeoutLocked

final void cancelBroadcastTimeoutLocked() {
    if (mPendingBroadcastTimeoutMessage) {
        mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
        mPendingBroadcastTimeoutMessage = false;
    }
}

移除广播超时消息BROADCAST_TIMEOUT_MSG

2.3 引爆炸弹

2.3.1 BroadcastHandler.handleMessage

BroadcastQueue.java ::BroadcastHandle

private final class BroadcastHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BROADCAST_TIMEOUT_MSG: {
                synchronized (mService) {
                    //【见小节2.3.2】
                    broadcastTimeoutLocked(true);
                }
            } break;
            ...
        }
        ...
    }
}

2.3.2 broadcastTimeoutLocked

BroadcastRecord.java

//fromMsg = true
final void broadcastTimeoutLocked(boolean fromMsg) {
    if (fromMsg) {
        mPendingBroadcastTimeoutMessage = false;
    }

    if (mOrderedBroadcasts.size() == 0) {
        return;
    }

    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mOrderedBroadcasts.get(0);
    if (fromMsg) {
        if (mService.mDidDexOpt) {
            mService.mDidDexOpt = false;
            long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }
        
        if (!mService.mProcessesReady) {
            return; //当系统还没有准备就绪时,广播处理流程中不存在广播超时
        }

        long timeoutTime = r.receiverTime + mTimeoutPeriod;
        if (timeoutTime > now) {
            //如果当前正在执行的receiver没有超时,则重新设置广播超时
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }
    }

    BroadcastRecord br = mOrderedBroadcasts.get(0);
    if (br.state == BroadcastRecord.WAITING_SERVICES) {
        //广播已经处理完成,但需要等待已启动service执行完成。当等待足够时间,则处理下一条广播。
        br.curComponent = null;
        br.state = BroadcastRecord.IDLE;
        processNextBroadcast(false);
        return;
    }

    r.receiverTime = now;
    //当前BroadcastRecord的anr次数执行加1操作
    r.anrCount++;

    if (r.nextReceiver <= 0) {
        return;
    }
    ...
    
    Object curReceiver = r.receivers.get(r.nextReceiver-1);
    //查询App进程
    if (curReceiver instanceof BroadcastFilter) {
        BroadcastFilter bf = (BroadcastFilter)curReceiver;
        if (bf.receiverList.pid != 0
                && bf.receiverList.pid != ActivityManagerService.MY_PID) {
            synchronized (mService.mPidsSelfLocked) {
                app = mService.mPidsSelfLocked.get(
                        bf.receiverList.pid);
            }
        }
    } else {
        app = r.curApp;
    }

    if (app != null) {
        anrMessage = "Broadcast of " + r.intent.toString();
    }

    if (mPendingBroadcast == r) {
        mPendingBroadcast = null;
    }

    //继续移动到下一个广播接收者
    finishReceiverLocked(r, r.resultCode, r.resultData,
            r.resultExtras, r.resultAbort, false);
    scheduleBroadcastsLocked();

    if (anrMessage != null) {
        // [见小节2.3.3]
        mHandler.post(new AppNotResponding(app, anrMessage));
    }
}
  1. mOrderedBroadcasts已处理完成,则不会anr;
  2. 正在执行dexopt,则不会anr;
  3. 系统还没有进入ready状态(mProcessesReady=false),则不会anr;
  4. 如果当前正在执行的receiver没有超时,则重新设置广播超时,不会anr;

2.3.3 AppNotResponding

BroadcastQueue.java

private final class AppNotResponding implements Runnable {
    ...
    public void run() {
        // 进入ANR处理流程
        mService.appNotResponding(mApp, null, null, false, mAnnotation);
    }
}

三、ContentProvider超时机制

ContentProvider Timeout是位于”ActivityManager”线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。

ContentProvider 超时为CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s. 这个跟前面的Service和BroadcastQueue不同, 与Provider进程启动过程相关.

3.1 埋炸弹

分析前需熟悉Provider启动流程:
在这里插入图片描述
埋炸弹的过程 其实是在进程创建的过程,进程创建后在startProcessLocked之后会调用AMS.attachApplicationLocked进入system_server进程.

3.1.1 AMS.attachApplicationLocked

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid) {
    ProcessRecord app;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid); // 根据pid获取ProcessRecord
        }
    } 
    ...
    
    //系统处于ready状态或者该app为FLAG_PERSISTENT进程则为true
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;

    //app进程存在正在启动中的provider,则超时10s后发送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息
    if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
        msg.obj = app;
        mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
    }
    
    thread.bindApplication(...);
    ...
}

10s之后引爆该炸弹

3.2 拆炸弹

当provider成功publish之后,便会拆除该炸弹.

3.2.1 AMS.publishContentProviders

public final void publishContentProviders(IApplicationThread caller,
       List<ContentProviderHolder> providers) {
   ...
   
   synchronized (this) {
       final ProcessRecord r = getRecordForAppLocked(caller);
       
       final int N = providers.size();
       for (int i = 0; i < N; i++) {
           ContentProviderHolder src = providers.get(i);
           ...
           ContentProviderRecord dst = r.pubProviders.get(src.info.name);
           if (dst != null) {
               ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
               
               mProviderMap.putProviderByClass(comp, dst); //将该provider添加到mProviderMap
               String names[] = dst.info.authority.split(";");
               for (int j = 0; j < names.length; j++) {
                   mProviderMap.putProviderByName(names[j], dst);
               }

               int launchingCount = mLaunchingProviders.size();
               int j;
               boolean wasInLaunchingProviders = false;
               for (j = 0; j < launchingCount; j++) {
                   if (mLaunchingProviders.get(j) == dst) {
                       //将该provider移除mLaunchingProviders队列
                       mLaunchingProviders.remove(j);
                       wasInLaunchingProviders = true;
                       j--;
                       launchingCount--;
                   }
               }
               //成功pubish则移除该消息
               if (wasInLaunchingProviders) {
                   mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
               }
               synchronized (dst) {
                   dst.provider = src.provider;
                   dst.proc = r;
                   //唤醒客户端的wait等待方法
                   dst.notifyAll();
               }
               ...
           }
       }
   }    
}

3.3 引爆炸弹

在system_server进程中有一个Handler线程, 名叫”ActivityManager”.当倒计时结束便会向该Handler线程发送 一条信息CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG,

3.3.1 MainHandler.handleMessage

ActivityManagerService.java ::MainHandler

final class MainHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
                ...
                ProcessRecord app = (ProcessRecord)msg.obj;
                synchronized (ActivityManagerService.this) {
                    //【见小节4.3.2】
                    processContentProviderPublishTimedOutLocked(app);
                }
            } break;
            ...
        }
        ...
    }
}

3.3.2 AMS.processContentProviderPublishTimedOutLocked

private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
    //[见3.3.3]
    cleanupAppInLaunchingProvidersLocked(app, true); 
    //[见小节3.3.4]
    removeProcessLocked(app, false, true, "timeout publishing content providers");
}

3.3.3 AMS.cleanupAppInLaunchingProvidersLocked

boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
    boolean restart = false;
    for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
        ContentProviderRecord cpr = mLaunchingProviders.get(i);
        if (cpr.launchingApp == app) {
            if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
                restart = true;
            } else {
                //移除死亡的provider
                removeDyingProviderLocked(app, cpr, true);
            }
        }
    }
    return restart;
}

removeDyingProviderLocked()的功能跟进程的存活息息相关:

  • 对于stable类型的provider(即conn.stableCount > 0),则会杀掉所有跟该provider建立stable连接的非persistent进程.
  • 对于unstable类的provider(即conn.unstableCount > 0),并不会导致client进程被级联所杀.

3.3.4 AMS.removeProcessLocked

private final boolean removeProcessLocked(ProcessRecord app,
        boolean callerWillRestart, boolean allowRestart, String reason) {
    final String name = app.processName;
    final int uid = app.uid;

    //移除mProcessNames中的相应对象
    removeProcessNameLocked(name, uid);
    if (mHeavyWeightProcess == app) {
        mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
                mHeavyWeightProcess.userId, 0));
        mHeavyWeightProcess = null;
    }
    boolean needRestart = false;
    if (app.pid > 0 && app.pid != MY_PID) {
        int pid = app.pid;
        synchronized (mPidsSelfLocked) {
            mPidsSelfLocked.remove(pid);
            mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
        }
        
        ...
        boolean willRestart = false;
        if (app.persistent && !app.isolated) {
            if (!callerWillRestart) {
                willRestart = true;
            } else {
                needRestart = true;
            }
        }
        app.kill(reason, true); //杀进程
        handleAppDiedLocked(app, willRestart, allowRestart);
        if (willRestart) {
            removeLruProcessLocked(app);
            addAppLocked(app.info, false, null /* ABI override */);
        }
    } else {
        mRemovedProcesses.add(app);
    }
    return needRestart;
}

小节

Service超时检测机制:

  • 超过一定时间没有执行完相应操作来触发移除延时消息,则会触发anr;

BroadcastReceiver超时检测机制:

  • 有序广播的总执行时间超过 2* receiver个数 * timeout时长,则会触发anr;

另外:

  • 对于Service, Broadcast, Input发生ANR之后,最终都会调用AMS.appNotResponding;
  • 对于provider,在其进程启动时publish过程可能会出现ANR, 则会直接杀进程以及清理相应信息,而不会弹出ANR的对话框. appNotRespondingViaProvider()过程会走appNotResponding(), 这个就不介绍了,很少使用,由用户自定义超时时间.有序广播的某一个receiver执行过程超过 timeout时长,则会触发anr.

总结

service启动/广播发送消息/provider启动时候,会发送一个delay延时消息(SERVICE_TIMEOUT_MSG)埋下炸弹进入倒计时, 超时没有启动完成则会触发ANR流程(AMS中执行SERVICE_TIMEOUT_MSG消息处理),引爆炸弹;
启动完成,则移除服务超时消息SERVICE_TIMEOUT_MSG,不会引发ANR。

相关类:

  • AMS, 即ActivityManagerService

致谢:
Service类型ANR原理讲解
broadcast类型ANR产生原理讲解
ContentProvider类型ANR产生原理讲解
理解Android ANR的触发原理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/533807.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三大基础排序算法——我欲修仙(功法篇)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️我欲修仙】 学习名言&#xff1a;莫等闲、白了少年头&#xff0c;空悲切。——岳飞 系列文章目录 第一章 ❤️ 学习前的必知知识 第二章 ❤️ 二分查找 文章目录 系列文章目录前言&#x1f697;&…

Netty实战(三)

Netty的组件和设计 一、Channel、EventLoop 和 ChannelFuture1.1 Channel 接口1.2 EventLoop 接口1.3 ChannelFuture 接口 二、ChannelHandler 和 ChannelPipeline2.1 ChannelHandler 接口2.2 ChannelPipeline 接口2.3 编码器和解码器2.4 抽象类 SimpleChannelInboundHandler 三…

suricata中DPDK收发包源码分析2

《suricata中DPDK收发包源码分析1》中分析了整体的DPDK收发包框架代码&#xff0c;今天我们继续来深入了解一下一些细节方面的问题。 目录 Q1&#xff1a;收发包线程模式在代码中是怎样确定的&#xff1f; Q2: DPDK库的初始化rte_eal_init在哪里调用的&#xff1f; Q3: 对网…

Linux中LV Status的状态为NOT available

今天下午有现场反馈备份磁盘找不到了&#xff0c;使用lvm方式的。提供了todesk帮忙看下&#xff0c; 首先使用 blkid查看&#xff0c;确实看不到备份磁盘的UUID&#xff0c;使用lvdisplay查看状态&#xff0c;状态不对了 [rootdb1 ~]# lvdisplay --- Logical volume --- …

.Vue3项目初始化

文章目录 1.Vue3项目初始化1.1 创建vue项目1.2 vue 初始化1.3 git 项目管理1.4 配置iconfig.json1.5 element 按需引入1.6 element 主题色的定制1.7 axios的基础配置1.8 router路由的配置 1.Vue3项目初始化 1.1 创建vue项目 npm init vuelatest1.2 vue 初始化 npm install1.…

【2023/05/16】MonteCarlo

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第11天。 Share O Beauty,find theyself in love,not in the flattery of thymirror. 译文&#xff1a; 啊&#xff0c;美啊&#xff0c;在爱中找你自己吧&#xff0c;不要到你镜子的诌谀中去寻找。 M…

[遗传学]转座因子的结构与功能

本篇文章主要带你了解:转座因子的发现和分类;原核生物以及真核生物种的转座子;转座作用的分子机制以及转座因子的遗传学效应和应用. &#x1f9ec;转座因子的发现和分类 &#x1f9ec;转座因子的概念 转座因子(transposable element)是在转座酶&#xff08;transposase&#xf…

Class 03 - R语言的 Vectors(向量) 与 lists(列表)

Class 03 - R语言的 Vector与 列表 list R语言语法脚本文件的创建、保存、和修改名称第一个函数使用帮助功能查看函数详细说明语法问题变量与赋值定义变量名称格式调用变量 R中的数据结构Vectors (向量)创建向量查看向量的性质查看数据类型 typeof()查看数据长度 length()检查…

Elasticsearch 核心技术(十):GEO 地理查询(geo_bounding_box、geo_distance、geo_shape)

❤️ 博客主页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;大数据核心技术从入门到精通 文章目录 一、地理数据类型1.1、geo_point 地理点类型1.1.1、创建一个含有 geo_point 字…

opencv_c++学习(八)

一、两张图像的像素比较 比较最大最小 最小&#xff1a; min(lnputArray src1, InputArray src2, outputArray dst)最大&#xff1a; max(lnputArray src1, InputArray src2, outputArray dst)src1 :第一个图像矩阵&#xff0c;可以是任意通道数的矩阵。 src2:第二个图像矩…

电源电压过冲

前言&#xff1a; 前段时间突然想起来以前的一个问题&#xff0c;这个问题相信大家也都遇到过&#xff0c;甚至是解决过&#xff0c;或者没解决&#xff0c;也就不了了之&#xff0c;今天这篇文章&#xff0c;主要来讲下这个问题&#xff0c;看完喜欢的欢迎给我留言或者点赞&a…

【Linux】常见指令

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;别人可以拷贝我的模式&#xff0c;但不能拷贝我不断往前的激情 &#x1f6f8;C专栏&#xff1a;Linux修炼内功基地 家人们更新不易&#xff0c;你们的&#x1f44d;点赞&#x1f44d;和⭐关注⭐…

EasyYapi插件—快速生成API接口文档

EasyYapi插件—快速生成API接口文档 1. 功能 导出http到&#xff08;Controller注解类&#xff09; YapiPostmanmarkdown导出RPC到 YapimarkdownCall api调用API接口 注意点&#xff1a; 注释中可以使用module标注模块所属的模块。表示接口会发布到yapi模块下&#xff1b;只…

生存分析利器:Python 中的 Kaplan-Meier Fitter 类详解

KaplanMeierFitter是lifelines库中用于计算生存分析的一个类。使用KaplanMeierFitter类&#xff0c;我们可以对我们的数据进行不同组之间的生存分析&#xff0c;比如根据年龄、性别、治疗手段等分类变量进行分组分析。 该类包含许多方法和属性&#xff0c;其中最常用的是fit()…

FPGA复位信号设计讨论

复位概述 复位作为电子系统中最常见的信号同时也是最重要的信号&#xff0c;它对工程师整体的设计表现有着极大的影响。复位信号可能深刻影响设计的性能表现&#xff0c;功耗&#xff0c;面积等等。对于一个优秀的系统设计&#xff0c;很难不把复位信号当成一个关键信号来设计。…

【车载基础软件 |ASF中间件 系列二】— 国产汽车生态平台ASF的生态框架

本文主要介绍国产基础软件开发平台架构下基于ASF的生态框架。 背景信息 随着E/E架构演进,从最初传统的分布式架构,由独立功能的ECU通过CANLIN等高实时性 总线通讯。诼渐演进到当前分域集中式结合车联网功能的结构,车载以大网逐渐步入了整车电子电器架构之中,除了高实时性…

【AIGC使用教程】论文阅读神器 SciSpace 从注册到体验

欢迎关注【AIGC使用教程】 专栏 【AIGC使用教程】论文阅读神器 SciSpace 从注册到体验 【AIGC使用教程】Microsoft Edge/Bing Chat 注册使用完全指南 【AIGC使用教程】GitHub Copilot 免费注册及在 VS Code 中的安装使用 【AIGC使用教程】GitHub Copilot 免费注册及在 PyCharm …

golang第一个简单项目实战+源码(gin+gorm)

创建项目并新建包 点击file->settings->GOPATH&#xff0c;把当前项目加进GOPATH目录中&#xff0c;然后点击apply&#xff0c; 点击加号完成之后&#xff0c;在左边选择go build 新建数据库 编写配置信息 这里将数据的信息都写在了配置文件里面 dns的内容为&#x…

二叉树概念结构,以及画图加代码分析二叉树遍历,创建,销毁,层序遍历,判断是否完全二叉树等等

二叉树 树的概念及结构树的概念树的相关概念树的表示 二叉树的概念及结构概念特殊的二叉树二叉树性质二叉树的存储结构 二叉树的实现二叉树顺序结构的实现二叉树链式结构的实现二叉树的遍历前序遍历中序遍历后序遍历 二叉树结点数量叶子结点数量求树高求k层结点数量 二叉树创建…

SpringMVC第一阶段:SpringMVC的介绍和使用

1、SpringMVC的概述 Spring MVC框架是一个开源的Java平台&#xff0c;为开发强大的基于JavaWeb应用程序提供全面的基础架构支持&#xff0c;并且使用起来非常简单容易。 Spring web MVC框架提供了MVC(模型 - 视图 - 控制器)架构&#xff0c;用于开发灵活和松散耦合的Web应用程…