Service 出現(xiàn)ANR的原因

Android知識(shí)總結(jié)

一、ANR出現(xiàn)原因

Android 系統(tǒng)中, ActivityManagerService( 簡(jiǎn)稱 AMS)和WindowManagerService( 簡(jiǎn) 稱 WMS)會(huì)檢測(cè)App的響應(yīng)時(shí)間,如果App在特定時(shí)間無(wú)法相應(yīng)屏幕觸摸或鍵盤(pán)輸入時(shí)間,或者特定事件沒(méi)有處理完畢,就會(huì)出現(xiàn) ANR。

1、以下四個(gè)條件都可以造成 ANR 發(fā)生:

  • InputDispatching Timeout:5 秒內(nèi)無(wú)法響應(yīng)屏幕觸摸事件或鍵盤(pán)輸入事件
  • BroadcastQueue Timeout :在執(zhí)行前臺(tái)廣播(BroadcastReceiver)的 onReceive()函數(shù)時(shí) 10 秒沒(méi)有處理完成,后臺(tái)為 60 秒。
  • Service Timeout :前臺(tái)服務(wù) 20 秒內(nèi),后臺(tái)服務(wù)在 200 秒內(nèi)沒(méi)有執(zhí)行完畢。
  • ContentProvider Timeout :ContentProvider 的 publish 在 10s 內(nèi)沒(méi)進(jìn)行完。

二、Service ANR 源碼分析

1、Service 造成的 e Service Timeout

Service Timeout 是位于 ActivityManager 線程中的 AMS.MainHandler 收到 SERVICE_TIMEOUT_MSG 消息時(shí)觸發(fā)。

2、發(fā)送延時(shí)消息

Service 進(jìn)程 attach 到 system_server 進(jìn)程的過(guò)程中會(huì)調(diào)用realStartServiceLocked ,緊接著mAm.mHandler.sendMessageAtTime() 來(lái)發(fā)送一個(gè)延時(shí)消息,延時(shí)的時(shí)常是定義好的,如前臺(tái) Service 的 20 秒。ActivityManager 線程中的 AMS.MainHandler 收到 SERVICE_TIMEOUT_MSG 消息時(shí)會(huì)觸發(fā)。

  • Service 是在AMS 中交給 ActiveServices 進(jìn)行管理的。

調(diào)用 ActiveServices.realStartServiceLocked 方法

IApplicationThread thread; //ActivityThread 的內(nèi)部類

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
    ...
    //發(fā)送 delay 消息(SERVICE_TIMEOUT_MSG
    bumpServiceExecutingLocked(r, execInFg, "create");
    
    try {
        if (LOG_SERVICE_START_STOP) {
        ...
        //最終執(zhí)行服務(wù)的 onCreate()方法
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                app.getReportedProcState());
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        Slog.w(TAG, "Application dead when creating service " + r);
        mAm.appDiedLocked(app, "Died when creating service");
        throw e;
    } finally {
        ..
    }
    ...
}
  • 發(fā)送延時(shí)消息

ActiveServices.java

private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    ...
    long now = SystemClock.uptimeMillis();
    if (r.executeNesting == 0) {
        r.executeFg = fg;
        ServiceState stracker = r.getTracker();
        if (stracker != null) {
            stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
        }
        if (r.app != null) {
            r.app.executingServices.add(r);
            r.app.execServicesFg |= fg;
            if (timeoutNeeded && r.app.executingServices.size() == 1) {
                scheduleServiceTimeoutLocked(r.app);
            }
        }
    } else if (r.app != null && fg && !r.app.execServicesFg) {
        r.app.execServicesFg = true;
        if (timeoutNeeded) {
            scheduleServiceTimeoutLocked(r.app);
        }
    }
    r.executeFg |= fg;
    r.executeNesting++;
    r.executingStart = now;
}
static final int SERVICE_TIMEOUT = 20*1000; //前臺(tái)服務(wù) 20 秒內(nèi)
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10; //后臺(tái)服務(wù)在 200 秒
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;
    //發(fā)送延時(shí)消息
    //當(dāng)超時(shí)后仍沒(méi)有 remove 該SERVICE_TIMEOUT_MSG 消息,則執(zhí)行 service Timeout 流程
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

進(jìn)入 ActivityManagerService#MainHandler處理消息

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    final MainHandler mHandler; 
    final ActiveServices mServices;

    final class MainHandler extends Handler {
        public MainHandler(Looper looper) {
            super(looper, null, true);
        }
        
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case GC_BACKGROUND_PROCESSES_MSG: {
                synchronized (ActivityManagerService.this) {
                    performAppGcsIfAppropriateLocked();
                }
            } break;
            case SERVICE_TIMEOUT_MSG: { //處理 service 的超時(shí)時(shí)間 ANR
                mServices.serviceTimeout((ProcessRecord)msg.obj);
            } break;
            ...
        
        }
    }
}

進(jìn)入ActiveServices#serviceTimeout 進(jìn)行處理

void serviceTimeout(ProcessRecord proc) {
    String anrMessage = null;
    synchronized(mAm) {
        if (proc.isDebugging()) {
            // The app's being debugged, ignore timeout.
            return;
        }
        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.mProcessList.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.shortInstanceName;
        } 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));
        }
    }

    if (anrMessage != null) {
        //處理 NAR
        mAm.mAnrHelper.appNotResponding(proc, anrMessage);
    }
}

無(wú)論是四大組件或者進(jìn)程等只要發(fā)生ANR,最終都會(huì)調(diào)用 AMS.appNotResponding() 方法。

class AnrHelper {
    //保存 ANR 信息
    private final ArrayList<AnrRecord> mAnrRecords = new ArrayList<>();
    private final AtomicBoolean mRunning = new AtomicBoolean(false);

    void appNotResponding(ProcessRecord anrProcess, String annotation) {
        appNotResponding(anrProcess, null /* activityShortComponentName */, null /* aInfo */,
                null /* parentShortComponentName */, null /* parentProcess */,
                false /* aboveSystem */, annotation);
    }

    void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,
            ApplicationInfo aInfo, String parentShortComponentName,
            WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
        synchronized (mAnrRecords) {
            mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
                    parentShortComponentName, parentProcess, aboveSystem, annotation));
        }
        startAnrConsumerIfNeeded();
    }
    
    private void startAnrConsumerIfNeeded() {
        if (mRunning.compareAndSet(false, true)) {
            new AnrConsumerThread().start();
        }
    }

    //執(zhí)行 ANR 的線程
    private class AnrConsumerThread extends Thread {
        AnrConsumerThread() {
            super("AnrConsumer");
        }

        private AnrRecord next() {
            synchronized (mAnrRecords) {
                return mAnrRecords.isEmpty() ? null : mAnrRecords.remove(0);
            }
        }

        @Override
        public void run() {
            AnrRecord r;
            while ((r = next()) != null) {
                final long startTime = SystemClock.uptimeMillis();
                // If there are many ANR at the same time, the latency may be larger. If the latency
                // is too large, the stack trace might not be meaningful.
                final long reportLatency = startTime - r.mTimestamp;
                final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS;
                r.appNotResponding(onlyDumpSelf);
                final long endTime = SystemClock.uptimeMillis();
                Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in "
                        + (endTime - startTime) + "ms, latency " + reportLatency
                        + (onlyDumpSelf ? "ms (expired, only dump ANR app)" : "ms"));
            }

            mRunning.set(false);
            synchronized (mAnrRecords) {
                // The race should be unlikely to happen. Just to make sure we don't miss.
                if (!mAnrRecords.isEmpty()) {
                    startAnrConsumerIfNeeded();
                }
            }
        }
    }

    //存儲(chǔ) ANR 信息類
    private static class AnrRecord {
        final ProcessRecord mApp;
        final String mActivityShortComponentName;
        final String mParentShortComponentName;
        final String mAnnotation;
        final ApplicationInfo mAppInfo;
        final WindowProcessController mParentProcess;
        final boolean mAboveSystem;
        final long mTimestamp = SystemClock.uptimeMillis();

        AnrRecord(ProcessRecord anrProcess, String activityShortComponentName,
                ApplicationInfo aInfo, String parentShortComponentName,
                WindowProcessController parentProcess, boolean aboveSystem, String annotation) {
            mApp = anrProcess;
            mActivityShortComponentName = activityShortComponentName;
            mParentShortComponentName = parentShortComponentName;
            mAnnotation = annotation;
            mAppInfo = aInfo;
            mParentProcess = parentProcess;
            mAboveSystem = aboveSystem;
        }

        void appNotResponding(boolean onlyDumpSelf) {
            mApp.appNotResponding(mActivityShortComponentName, mAppInfo,
                    mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,
                    onlyDumpSelf);
        }
    }
}
  • 進(jìn)入目標(biāo)進(jìn)程的主線程創(chuàng)建 Service

進(jìn)入 ActivityThread#ApplicationThread#scheduleCreateService 的方法。

public final void scheduleCreateService(IBinder token,
        ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
    updateProcessState(processState, false);
    CreateServiceData s = new CreateServiceData();
    s.token = token;
    s.info = info;
    s.compatInfo = compatInfo;
    //發(fā)送 CREATE_SERVICE 消息
    sendMessage(H.CREATE_SERVICE, s);
}

調(diào)用 ActivityThread#H 類發(fā)送消息

class H extends Handler {
    public void handleMessage(Message msg) 
        switch (msg.what) {
            case CREATE_SERVICE:
                if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                            ("serviceCreate: " + String.valueOf(msg.obj)));
                }
                handleCreateService((CreateServiceData)msg.obj);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
        }
}
private void handleCreateService(CreateServiceData data) {
    unscheduleGcIdler();

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
        //創(chuàng)建 ContextImpl 對(duì)象
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        //創(chuàng)建 Application 對(duì)象
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
        // Service resources must be initialized with the same loaders as the application
        // context.
        context.getResources().addLoaders(
                app.getResources().getLoaders().toArray(new ResourcesLoader[0]));

        context.setOuterContext(service);
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        //調(diào)用服務(wù) onCreate()方法
        service.onCreate();
        mServices.put(data.token, service);
        try {
            //取消 AMS.MainHandler 的延時(shí)消息
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw new RuntimeException(
                "Unable to create service " + data.info.name
                + ": " + e.toString(), e);
        }
    }
}

這個(gè)方法中會(huì)創(chuàng)建目標(biāo)服務(wù)對(duì)象,以及回調(diào)常用的 Service 的 onCreate() 方法,緊接著通過(guò) serviceDoneExecuting() 回到 system_server 執(zhí)行取消 AMS.MainHandler 的延時(shí)消息

3、回到 system_server 進(jìn)程執(zhí)行取消 AMS.MainHandler 的延時(shí)消息。

進(jìn)入ActivityManagerService#serviceDoneExecuting 方法

final ActiveServices mServices;

public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
    synchronized(this) {
        if (!(token instanceof ServiceRecord)) {
            Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
            throw new IllegalArgumentException("Invalid service token");
        }
        mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
    }
}

調(diào)用 ActiveServices#serviceDoneExecutingLocked 方法

void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
    boolean inDestroying = mDestroyingServices.contains(r);
    if (r != null) {
        if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
            r.callStart = true;
            switch (res) {
                case Service.START_STICKY_COMPATIBILITY:
                case Service.START_STICKY: {
                    // We are done with the associated start arguments.
                    r.findDeliveredStart(startId, false, true);
                    // Don't stop if killed.
                    r.stopIfKilled = false;
                    break;
                }
                case Service.START_NOT_STICKY: {
                    // We are done with the associated start arguments.
                    r.findDeliveredStart(startId, false, true);
                    if (r.getLastStartId() == startId) {
                        r.stopIfKilled = true;
                    }
                    break;
                }
                case Service.START_REDELIVER_INTENT: {
                    ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
                    if (si != null) {
                        si.deliveryCount = 0;
                        si.doneExecutingCount++;
                        // Don't stop if killed.
                        r.stopIfKilled = true;
                    }
                    break;
                }
                case Service.START_TASK_REMOVED_COMPLETE: {
                    r.findDeliveredStart(startId, true, true);
                    break;
                }
                default:
                    throw new IllegalArgumentException(
                            "Unknown service start result: " + res);
            }
            if (res == Service.START_STICKY_COMPATIBILITY) {
                r.callStart = false;
            }
        } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) {
            if (!inDestroying) {
                if (r.app != null) {
                    Slog.w(TAG, "Service done with onDestroy, but not inDestroying: "
                            + r + ", app=" + r.app);
                }
            } else if (r.executeNesting != 1) {
                Slog.w(TAG, "Service done with onDestroy, but executeNesting="
                        + r.executeNesting + ": " + r);
                // Fake it to keep from ANR due to orphaned entry.
                r.executeNesting = 1;
            }
        }
        final long origId = Binder.clearCallingIdentity();
        serviceDoneExecutingLocked(r, inDestroying, inDestroying);
        Binder.restoreCallingIdentity(origId);
    } else {
        Slog.w(TAG, "Done executing unknown service from pid "
                + Binder.getCallingPid());
    }
}
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
        boolean finishing) {
    if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "<<< DONE EXECUTING " + r
            + ": nesting=" + r.executeNesting
            + ", inDestroying=" + inDestroying + ", app=" + r.app);
    else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
            "<<< DONE EXECUTING " + r.shortInstanceName);
    r.executeNesting--;
    if (r.executeNesting <= 0) {
        if (r.app != null) {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                    "Nesting at 0 of " + r.shortInstanceName);
            r.app.execServicesFg = false;
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
                        "No more executingServices of " + r.shortInstanceName);
                //移除當(dāng)前服務(wù)所在進(jìn)程中沒(méi)有正在執(zhí)行的 service
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
            } else if (r.executeFg) {
                // Need to re-evaluate whether the app still needs to be in the foreground.
                for (int i=r.app.executingServices.size()-1; i>=0; i--) {
                    if (r.app.executingServices.valueAt(i).executeFg) {
                        r.app.execServicesFg = true;
                        break;
                    }
                }
            }
            if (inDestroying) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                        "doneExecuting remove destroying " + r);
                mDestroyingServices.remove(r);
                r.bindings.clear();
            }
            mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
        }
        r.executeFg = false;
        if (r.tracker != null) {
            final int memFactor = mAm.mProcessStats.getMemFactorLocked();
            final long now = SystemClock.uptimeMillis();
            r.tracker.setExecuting(false, memFactor, now);
            r.tracker.setForeground(false, memFactor, now);
            if (finishing) {
                r.tracker.clearCurrentOwner(r, false);
                r.tracker = null;
            }
        }
        if (finishing) {
            if (r.app != null && !r.app.isPersistent()) {
                r.app.stopService(r);
                r.app.updateBoundClientUids();
                if (r.whitelistManager) {
                    updateWhitelistManagerLocked(r.app);
                }
            }
            r.setProcess(null);
        }
    }
}

此方法中 Service 邏輯處理完成則移除之前延時(shí)的消息SERVICE_TIMEOUT_MSG 。如果沒(méi)有執(zhí)行完畢不調(diào)用這個(gè)方法,則超時(shí)后會(huì)發(fā)出 SERVICE_TIMEOUT_MSG 來(lái)告知ANR 發(fā)生。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容