美文网首页
ANR原理分析

ANR原理分析

作者: taoyyyy | 来源:发表于2024-03-15 18:01 被阅读0次

    四大组件的ANR触发机制

    Service、BroadcastReceiver和ContentProvider的ANR触发机制都可以简述为在AMS端的埋炸弹和拆炸弹过程。
    以Service为例
    AMS端启动service时往handler post一条超时消息,消息到了之后就触发anr,期间如果service正常起来,会回调AMS,remove这条消息。
    埋炸弹

    com/android/server/am/ActiveServices.java
    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流程    
        mAm.mHandler.sendMessageAtTime(msg,
            proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
    }
    

    拆炸弹

    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();
                
                //             ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (Exception e) {
                ...
            }
        }
    
    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();
                
                //             ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (Exception e) {
                ...
            }
        }
    

    引爆炸弹

    // ActivityManagerService.java ::MainHandler
    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;
         }
    }
    
    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--) {       // 从进程里面获取正在运行的 service
                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);
        }
    }
    

    注意ContentProvider埋炸弹的时机是在进程启动过程相关(因为ContentProvider是在进程启动时开启生命周期,并把自己publish到AMS)。

    input 触发anr机制

    input拥有不同的的超时机制,即在每次上报事件的时候检测一把是否爆炸。
    对于key事件,每次分发的时候检测一把是否有事件没有分发完或者没有收到app分发完成的回调;
    对于触摸事件,则检测一把是否距离上一次分发给app的事件是否超过500ms。
    上述原因满足一条则进入anr逻辑,在下一次事件分发时检测时间是否超过5s且期间没有收到app分发完成的回调,则正式触发anr。

    std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
            const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
            const char* targetType) {
        ...
    
        // Ensure that the dispatch queues aren't too far backed up for this event.
        if (eventEntry->type == EventEntry::TYPE_KEY) {
            // If the event is a key event, then we must wait for all previous events to
            // complete before delivering it because previous events may have the
            // side-effect of transferring focus to a different window and we want to
            // ensure that the following keys are sent to the new window.
            //
            // Suppose the user touches a button in a window then immediately presses "A".
            // If the button causes a pop-up window to appear then we want to ensure that
            // the "A" key is delivered to the new pop-up window.  This is because users
            // often anticipate pending UI changes when typing on a keyboard.
            // To obtain this behavior, we must serialize key events with respect to all
            // prior input events.
            if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) {
                return StringPrintf("Waiting to send key event because the %s window has not "
                        "finished processing all of the input events that were previously "
                        "delivered to it.  Outbound queue length: %d.  Wait queue length: %d.",
                        targetType, connection->outboundQueue.count(), connection->waitQueue.count());
            }
        } else {
            // Touch events can always be sent to a window immediately because the user intended
            // to touch whatever was visible at the time.  Even if focus changes or a new
            // window appears moments later, the touch event was meant to be delivered to
            // whatever window happened to be on screen at the time.
            //
            // Generic motion events, such as trackball or joystick events are a little trickier.
            // Like key events, generic motion events are delivered to the focused window.
            // Unlike key events, generic motion events don't tend to transfer focus to other
            // windows and it is not important for them to be serialized.  So we prefer to deliver
            // generic motion events as soon as possible to improve efficiency and reduce lag
            // through batching.
            //
            // The one case where we pause input event delivery is when the wait queue is piling
            // up with lots of events because the application is not responding.
            // This condition ensures that ANRs are detected reliably.
            if (!connection->waitQueue.isEmpty()
                    && currentTime >= connection->waitQueue.head->deliveryTime
                            + STREAM_AHEAD_EVENT_TIMEOUT) {
                return StringPrintf("Waiting to send non-key event because the %s window has not "
                        "finished processing certain input events that were delivered to it over "
                        "%0.1fms ago.  Wait queue length: %d.  Wait queue head age: %0.1fms.",
                        targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
                        connection->waitQueue.count(),
                        (currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f);
            }
        }
        return "";
    }
    int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime){
         // 如果失败的原因是因为上一个任务未处理完,则不需要给超时时间重新赋值
         if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
                // 设置InputTargetWaitCause
                mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
                //这里的currentTime是指执行dispatchOnceInnerLocked方法体的起点
                mInputTargetWaitStartTime = currentTime; 
                // timeout为5s
                mInputTargetWaitTimeoutTime = currentTime + timeout;
                mInputTargetWaitTimeoutExpired = false;
                mInputTargetWaitApplicationHandle.clear();
          }
        //当超时5s,则进入ANR流程
        if (currentTime >= mInputTargetWaitTimeoutTime) {
            onANRLocked(currentTime, applicationHandle, windowHandle,
                    entry->eventTime, mInputTargetWaitStartTime, reason);
            *nextWakeupTime = LONG_LONG_MIN; //强制立刻执行轮询来执行ANR策略
            return INPUT_EVENT_INJECTION_PENDING;
        }
    }
    
    

    其他

    anr触发后通过发信号的方式触发堆栈搜集。

        final void appNotResponding(ProcessRecord app, ActivityRecord activity,
                ActivityRecord parent, boolean aboveSystem, final String annotation) {
                    ...
            if (tracesFile == null) {
                // There is no trace file, so dump (only) the alleged culprit's threads to the log
                Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
            }
                    ...
        }
    
    Image.png

    No Window Focus Timeout,Window Timeout,Window Monitor Timeout。

    相关文章

      网友评论

          本文标题:ANR原理分析

          本文链接:https://www.haomeiwen.com/subject/dvcwzdtx.html