ANR源碼分析之Service Timeout

在前面的一篇文章中,分析了Broadcast Timeout的流程,接下來繼續分析Service Timeout的流程。Service默認不會運行在子線程中,它也不會運行在一個獨立的進程中,它一樣執行在UI線程中,所以也不能在Service中執行耗時操做,不然也會產生的ANR。java

 Service Timeout總體流程以下圖所示:app

1.startService(ContextImpl.java)post

/*
    * 啓動服務
    */
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, mUser);
    }
 
 
    private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
            validateServiceIntent(service);//驗證Service的Intent
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), getOpPackageName(), user.getIdentifier());//調用AMS的start方法
 
 
            ........
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
 2.startService(ActivityManagerService.java)
 在ActivityManagerService.java類中
 public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId)
            throws TransactionTooLargeException {
        enforceNotIsolatedCaller("startService");
        ......
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, callingPackage, userId);//調用ActiveServices的startServiceLocked方法
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }
3.startServiceLocked(ActiveServices.java)
    在ActiveServices.java類中
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, String callingPackage, final int userId)
            throws TransactionTooLargeException {
 
 
        final boolean callerFg;
        if (caller != null) {
         final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
         .........
         //調用者不是處於後臺線程組中
         callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
       } else {
          callerFg = true;
        }
     .......
        ServiceRecord r = res.record;
        ......
        //準備startService參數
        r.lastActivity = SystemClock.uptimeMillis();
        r.startRequested = true;
        r.delayedStop = false;
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                service, neededGrants));
        ......
        return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    }
 
 
    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        ServiceState stracker = r.getTracker();
        if (stracker != null) {
            stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
        }
        r.callStart = false;
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startRunningLocked();
        }
        //調用bringUpServiceLocked方法將服務調起
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        .......
        return r.name;
    }
 
 
    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
 
 
         ......
 
 
        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
        ......
        if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    //調用realStartServiceLocked方法啓動服務
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                }
 
 
            }
        } else {
           ........
        }
 
 
        ........
    }
 
 
    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        .......
        r.app = app;
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
 
 
        final boolean newService = app.services.add(r);
        //開始監控onCreate方法執行時長
        bumpServiceExecutingLocked(r, execInFg, "create");
        mAm.updateLruProcessLocked(app, false, null);
        mAm.updateOomAdjLocked();
 
 
        boolean created = false;
        try {
            ......
            mAm.notifyPackageUse(r.serviceInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
           //開始Service的onCreate流程
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
           ......
        } finally {
            ......
        }
 
 
        ......
        //開始Service的onBind流程
        requestServiceBindingsLocked(r, execInFg);
 
 
        ......
       //開始Service的onStart流程
        sendServiceArgsLocked(r, execInFg, true);
        ......
    }
    4.bumpServiceExecutingLocked(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 (r.app.executingServices.size() == 1) {
                    //服務方法首次執行時,調用該方法
                    scheduleServiceTimeoutLocked(r.app);
                }
            }
        } else if (r.app != null && fg && !r.app.execServicesFg) {
            r.app.execServicesFg = true;
            //服務方法非首次執行時,調用該方法
            scheduleServiceTimeoutLocked(r.app);
        }
        r.executeFg |= fg;//記錄服務是否在前臺執行
        r.executeNesting++;//記錄服務執行方法的次數
        r.executingStart = now;//記錄服務方法執行的開始時間
    }
    在執行服務的生命週期方法時,會調用bumpServiceExecutingLocked來監控服務方法執行是否超時,該方法最終調用scheduleServiceTimeoutLocked方法來發送SERVICE_TIMEOUT_MSG消息到ActivityManagerService中。
 void scheduleServiceTimeoutLocked(ProcessRecord proc) {
        ......
        long now = SystemClock.uptimeMillis();
        Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_TIMEOUT_MSG);
        msg.obj = proc;
        //發送消息到AMS中,並添加延遲時間,若是是前臺服務,則超時時間爲20s,若是是後臺服務,則超時時間爲200s
        mAm.mHandler.sendMessageAtTime(msg,
                proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
    }
     //服務超時時間爲20s
    static final int SERVICE_TIMEOUT = 20*1000;
    //在後臺線程組中服務超時時間爲200s
    static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
5.handleMessage方法(ActivityManagerService.java)
    ActivityManagerService接收到scheduleServiceTimeoutLocked方法發送過來的SERVICE_TIMEOUT_MSG消息後,調用ActiveServices的serviceTimeout方法發送ANR消息
 public void handleMessage(Message msg) {
         case SERVICE_TIMEOUT_MSG: {
                ......
                mServices.serviceTimeout((ProcessRecord)msg.obj);//調用ActiveServices的serviceTimeOut()方法
            } break;
     }
6.serviceTimeout方法(ActiveServices.java)
void serviceTimeout(ProcessRecord proc) {
        String anrMessage = null;
 
 
        synchronized(mAm) {
            ......
            final long now = SystemClock.uptimeMillis();
            //計算當前時間減去服務超時時間
            final long maxTime =  now -
                    (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
            ServiceRecord timeout = null;
            long nextTime = 0;
            //遍歷服務執行方法列表,若是有方法執行時間超過服務執行超時最大時間,則發送ANR消息
            for (int i=proc.executingServices.size()-1; i>=0; i--) {
                ServiceRecord sr = proc.executingServices.valueAt(i);
                //服務執行開始時間小於maxTime,則說明服務已經超時了
                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);
                ......
                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消息給AppErrors給用戶顯示
        if (anrMessage != null) {
            mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
        }
    }
    在serviceTimeout方法中判斷服務方法執行是否超時,若是執行超時則發送ANR消息給AppErrors,不然繼續監控下一個服務方法執行是否超時。ui


    在服務方法執行完成後,將取消服務超時監控。在realStartServiceLocked方法中,執行完bumpServiceExecutingLocked方法後,接着執行ActivityThread的scheduleCreateService方法。
1.scheduleCreateService方法(ActivityThread.java)this

    /*
    * createService方法來建立服務
    */
    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;
 
 
            sendMessage(H.CREATE_SERVICE, s);
    }
 
 
    public void handleMessage(Message msg) {
        case CREATE_SERVICE:
            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) {
        .....
        // 獲取服務的package信息
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            // 經過反射建立服務類
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            ......
        }
 
 
        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
 
 
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
 
 
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
//回調服務的onCreate方法
            service.onCreate();
            mServices.put(data.token, service);
            try {
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);//調用AMS的serviceDoneExecuting方法
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
           ......
        }
    }
  2.serviceDoneExecuting方法(ActivityManagerService.java)
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);
        }
    }
3.serviceDoneExecutingLocked方法(ActiveServices.java)
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
        boolean inDestroying = mDestroyingServices.contains(r);
        if (r != null) {
            .......
            final long origId = Binder.clearCallingIdentity();
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
            Binder.restoreCallingIdentity(origId);
        } else {
            ........
        }
    }
 
 
    private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing) {
        ......
        r.executeNesting--;
        if (r.executeNesting <= 0) {
            if (r.app != null) {
                .....
                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.shortName);
                    //移除SERVICE_TIMEOUT_MSG消息,取消對服務方法執行時間的監控
                    mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
                } else if (r.executeFg) {
                   ......
                }
                .......
            }
            r.executeFg = false;
            .......
        }
    }
    在serviceDoneExecutingLocked方法中,若是執行完了服務的方法,就移除SERVICE_TIMEOUT_MSG消息,取消對服務方法執行時間的監控。
    至此,完整的介紹了在執行服務方法的時候,設置監聽服務超時的過程以及移除監聽服務超時的過程。默認狀況下,服務執行的超時時間爲20s,在後臺線程組中服務執行超時時間爲200s。
 線程

相關文章
相關標籤/搜索