西部数码主机 | 阿里云主机| 虚拟主机 | 服务器 | 返回乐道官网

理解 Android Crash处理流程(2)

时间:2016-10-05 06:29来源:未知 作者:好模板 点击:
9.2.1 app.kill [- ProcessRecord.java] void kill(String reason, boolean noisy) {if (!killedByAm) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, kill);if (noisy) {Slog.i(TAG, Killing + toShortStrin

9.2.1 app.kill

[-> ProcessRecord.java]

void kill(String reason, boolean noisy) {
    if (!killedByAm) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
        if (noisy) {
            Slog.i(TAG, "Killing " + toShortString() + " (adj " + setAdj + "): " + reason);
        }
        EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
        Process.killProcessQuiet(pid); //杀进程
        Process.killProcessGroup(info.uid, pid); //杀进程组,包括native进程
        if (!persistent) {
            killed = true;
            killedByAm = true;
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }
}

此处reason为“crash”,关于杀进程的过程见我的另一篇文章 理解杀进程的实现原理.

9.2.2 handleAppDiedLocked

[-> ActivityManagerService.java]

private final void handleAppDiedLocked(ProcessRecordapp,
        boolean restarting, boolean allowRestart) {
    int pid = app.pid;
    //清除应用中service/receiver/ContentProvider信息
    boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
    if (!kept && !restarting) {
        removeLruProcessLocked(app);
        if (pid > 0) {
            ProcessList.remove(pid);
        }
    }
 
    if (mProfileProc == app) {
        clearProfilerLocked();
    }
 
    //清除应用中activity相关信息
    boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
 
    app.activities.clear();
    ...
    if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) {
        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
    }
}

9.3 ASS.resumeTopActivitiesLocked

[-> ActivityStackSupervisor.java]

boolean resumeTopActivitiesLocked() {
    return resumeTopActivitiesLocked(null, null, null);
}
 
boolean resumeTopActivitiesLocked(ActivityStacktargetStack, ActivityRecordtarget,
        BundletargetOptions) {
    if (targetStack == null) {
        targetStack = mFocusedStack;
    }
    boolean result = false;
    if (isFrontStack(targetStack)) {
        //【见小节9.3.1】
        result = targetStack.resumeTopActivityLocked(target, targetOptions);
    }
 
    for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
        final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
            final ActivityStackstack = stacks.get(stackNdx);
            if (stack == targetStack) {
                continue; //已经启动
            }
            if (isFrontStack(stack)) {
                stack.resumeTopActivityLocked(null);
            }
        }
    }
    return result;
}

此处 mFocusedStack 是当前正在等待接收input事件或者正在启动下一个activity的 ActivityStack 。

9.3.1 AS.resumeTopActivityLocked

[-> ActivityStack.java]

final boolean .resumeTopActivityLocked(ActivityRecordprev, Bundleoptions) {
    ...
    result = resumeTopActivityInnerLocked(prev, options);//【见小节9.3.2】
    return result;
}

9.3.2 AS.resumeTopActivityInnerLocked

[-> ActivityStack.java]

private boolean resumeTopActivityInnerLocked(ActivityRecordprev, Bundleoptions) {
    //找到mTaskHistory栈中第一个未处于finishing状态的Activity
    final ActivityRecordnext = topRunningActivityLocked(null);
 
    if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
                        mStackSupervisor.allResumedActivitiesComplete()) {
        //当top activity已经处于resume,则无需操作;
        return false;
    }
 
    if (mService.isSleepingOrShuttingDown()
            && mLastPausedActivity == next
            && mStackSupervisor.allPausedActivitiesComplete()) {
        //当正处于sleeping状态,top activity处于paused,则无需操作
        return false;
    }
 
    //正在启动app的activity,确保app不会被设置为stopped
    AppGlobals.getPackageManager().setPackageStoppedState(
                next.packageName, false, next.userId);
    //回调应用onResume方法
    next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                            mService.isNextTransitionForward(), resumeAnimOptions);
    ...
}

该方法代码比较长,这里就简单列举几条比较重要的代码。执行完该方法,应用也便完成了activity的resume过程。

9.4 finishTopRunningActivityLocked

9.4.1 ASS.finishTopRunningActivityLocked

[-> ActivityStackSupervisor.java]

void finishTopRunningActivityLocked(ProcessRecordapp, String reason) {
    for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
        final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
        final int numStacks = stacks.size();
        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
            final ActivityStackstack = stacks.get(stackNdx);
            //此处reason= "force-crash"【见小节9.4.2】
            stack.finishTopRunningActivityLocked(app, reason);
        }
    }
}

9.4.2 AS.finishTopRunningActivityLocked

final void finishTopRunningActivityLocked(ProcessRecordapp, String reason) {
    //找到栈顶第一个不处于finishing状态的activity
    ActivityRecord r = topRunningActivityLocked(null);
    if (r != null && r.app == app) {
        int taskNdx = mTaskHistory.indexOf(r.task);
        int activityNdx = r.task.mActivities.indexOf(r);
        //【见小节9.4.3】
        finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
        --activityNdx;
        if (activityNdx < 0) {
            do {
                --taskNdx;
                if (taskNdx < 0) {
                    break;
                }
                activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
            } while (activityNdx < 0);
        }
        if (activityNdx >= 0) {
            r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
            if (r.state == ActivityState.RESUMED
                    || r.state == ActivityState.PAUSING
                    || r.state == ActivityState.PAUSED) {
                if (!r.isHomeActivity() || mService.mHomeProcess != r.app) {
                    //【见小节9.4.3】
                    finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
                }
            }
        }
    }
}

9.4.3 AS.finishActivityLocked

final boolean finishActivityLocked(ActivityRecord r, int resultCode, IntentresultData,
          String reason, boolean oomAdj) {
    if (r.finishing) {
        return false; //正在finishing则返回
    }
    //设置finish状态的activity不可见
    r.makeFinishingLocked();
    //暂停key的分发事件
    r.pauseKeyDispatchingLocked();
 
    mWindowManager.prepareAppTransition(endTask
            ? AppTransition.TRANSIT_TASK_CLOSE
            : AppTransition.TRANSIT_ACTIVITY_CLOSE, false);
 
    mWindowManager.setAppVisibility(r.appToken, false);
    //回调activity的onPause方法
    startPausingLocked(false, false, false, false);
    ...
}

该方法最终会回调到activity的pause方法。

执行到这,我们还回过来看小节 5.crashApplication 中,处理完makeAppCrashingLocked,则会再发送消息SHOW_ERROR_MSG,弹出提示crash的对话框,接下来再看看该过程。

10. UiHandler

通过mUiHandler发送message,且消息的msg.waht=SHOW_ERROR_MSG,接下来进入UiHandler来看看handleMessage的处理过程。

[-> ActivityManagerService.java]

final class UiHandler extends Handler {
    public void handleMessage(Messagemsg) {
        switch (msg.what) {
        case SHOW_ERROR_MSG: {
          HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
          synchronized (ActivityManagerService.this) {
              ProcessRecordproc = (ProcessRecord)data.get("app");
              AppErrorResultres = (AppErrorResult) data.get("result");
              、
              boolean isBackground = (UserHandle.getAppId(proc.uid)
                      >= Process.FIRST_APPLICATION_UID
                      && proc.pid != MY_PID);
                ...
 
              if (mShowDialogs && !mSleeping && !mShuttingDown) {
                  //创建提示crash对话框,等待用户选择,5分钟操作等待。
                  Dialog d = new AppErrorDialog(mContext,
                          ActivityManagerService.this, res, proc);
                  d.show();
                  proc.crashDialog = d;
              } else {
                  //当处于sleep状态,则默认选择退出。
                  if (res != null) {
                      res.set(0);
                  }
              }
          }
        } break;
        ...
    }
}

在发生crash时,默认系统会弹出提示crash的对话框,并阻塞等待用户选择是“退出”或 “退出并报告”,当用户不做任何选择时5min超时后,默认选择“退出”,当手机休眠时也默认选择“退出”。到这里也并没有真正结束,在小节 2.uncaughtException 中在 finnally 语句块还有一个杀进程的动作。

11. killProcess

Process.killProcess(Process.myPid());
System.exit(10);

通过finnally语句块保证能执行并彻底杀掉Crash进程,关于杀进程的过程见我的另一篇文章 理解杀进程的实现原理 .。当Crash进程被杀后,并没有完全结束,还有Binder死亡通知的流程还没有处理完成。

12. 小结

当进程抛出未捕获异常时,则系统会处理该异常并进入crash处理流程。

理解 Android Crash处理流程

其中最为核心的工作图中红色部分 AMS.handleAppCrashLocked 的主要功能:

  1. 当同一进程1分钟之内连续两次crash,则执行的情况下:
    • 对于非persistent进程:
      • ASS.handleAppCrashLocked, 直接结束该应用所有activity
      • AMS.removeProcessLocked,杀死该进程以及同一个进程组下的所有进
      • ASS.resumeTopActivitiesLocked,恢复栈顶第一个非finishing状态的activity
    • 对于persistent进程,则只执行
      • ASS.resumeTopActivitiesLocked,恢复栈顶第一个非finishing状态的activity
  2. 否则,当进程没连续频繁crash
    • ASS.finishTopRunningActivityLocked,执行结束栈顶正在运行activity

另外, AMS.handleAppCrashLocked ,该方法内部主要调用链,如下:

AMS.handleAppCrashLocked
  ASS.handleAppCrashLocked
      AS.handleAppCrashLocked
          AS.finishCurrentActivityLocked
  AMS.removeProcessLocked
      ProcessRecord.kill
      AMS.handleAppDiedLocked
          ASS.handleAppDiedLocked
              AMS.cleanUpApplicationRecordLocked
              AS.handleAppDiedLocked
                  AS.removeHistoryRecordsForAppLocked
 
  ASS.resumeTopActivitiesLocked
      AS.resumeTopActivityLocked
          AS.resumeTopActivityInnerLocked
  ASS.finishTopRunningActivityLocked
      AS.finishTopRunningActivityLocked
          AS.finishActivityLocked

三、Binder死亡通知

进程被杀,如果还记得Binder的死亡回调机制,在应用进程创建的过程中有一个 attachApplicationLocked 方法的过程中便会创建死亡通知。

[-> ActivityManagerService.java]

private final boolean attachApplicationLocked(IApplicationThreadthread,
            int pid) {
    try {
        //创建binder死亡通知
        AppDeathRecipientadr = new AppDeathRecipient(
                app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
        app.deathRecipient = adr;
    } catch (RemoteException e) {
        app.resetPackageList(mProcessStats);
        startProcessLocked(app, "link fail", processName);
        return false;
    }
    ...
}

当binder服务端挂了之后,便会通过binder的DeathRecipient来通知AMS进行相应的清理收尾工作。前面已经降到crash的进程会被kill掉,那么当该进程会杀,则会回调到binderDied()方法。

1. binderDied

[-> ActivityManagerService.java]

private final class AppDeathRecipientimplements IBinder.DeathRecipient {
    public void binderDied() {
        synchronized(ActivityManagerService.this) {
            appDiedLocked(mApp, mPid, mAppThread, true);//【见小节2】
        }
    }
}

2. appDiedLocked

final void appDiedLocked(ProcessRecordapp, int pid, IApplicationThreadthread,
        boolean fromBinderDied) {
    ...
    if (!app.killed) {
        if (!fromBinderDied) {
            Process.killProcessQuiet(pid);
        }
        killProcessGroup(app.info.uid, pid);
        app.killed = true;
    }
 
    // Clean up already done if the process has been re-started.
    if (app.pid == pid && app.thread != null &&
            app.thread.asBinder() == thread.asBinder()) {
        boolean doLowMem = app.instrumentationClass == null;
        boolean doOomAdj = doLowMem;
        if (!app.killedByAm) {
            mAllowLowerMemLevel = true;
        } else {
            mAllowLowerMemLevel = false;
            doLowMem = false;
        }
        //【见小节3】
        handleAppDiedLocked(app, false, true);
 
        if (doOomAdj) {
            updateOomAdjLocked();
        }
        if (doLowMem) {
            doLowMemReportIfNeededLocked(app);
        }
    }
    ...
}

3 handleAppDiedLocked

[-> ActivityManagerService.java]

private final void handleAppDiedLocked(ProcessRecordapp,
        boolean restarting, boolean allowRestart) {
    int pid = app.pid;
    //清理应用程序service, BroadcastReceiver, ContentProvider相关信息【见小节4】
    boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
 
    if (!kept && !restarting) {
        removeLruProcessLocked(app);
        if (pid > 0) {
            ProcessList.remove(pid);
        }
    }
 
    //清理activity相关信息
    boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
    app.activities.clear();
    ...
    //恢复栈顶第一个非finish的activity
    if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) {
      mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
  }
}

4 cleanUpApplicationRecordLocked

该方法清理应用程序service, BroadcastReceiver, ContentProvider,process相关信息,为了便于说明将该方法划分为4个部分讲解

4.1 清理service

参数restarting = false, allowRestart =true, index =-1

private final boolean cleanUpApplicationRecordLocked(ProcessRecordapp,
        boolean restarting, boolean allowRestart, int index) {
    ...
    mProcessesToGc.remove(app);
    mPendingPssProcesses.remove(app);
 
    //如果存在,则清除crash/anr/wait对话框
    if (app.crashDialog != null && !app.forceCrashReport) {
        app.crashDialog.dismiss();
        app.crashDialog = null;
    }
    if (app.anrDialog != null) {
        app.anrDialog.dismiss();
        app.anrDialog = null;
    }
    if (app.waitDialog != null) {
        app.waitDialog.dismiss();
        app.waitDialog = null;
    }
 
    app.crashing = false;
    app.notResponding = false;
 
    app.resetPackageList(mProcessStats);
    app.unlinkDeathRecipient(); //解除app的死亡通告
    app.makeInactive(mProcessStats);
    app.waitingToKill = null;
    app.forcingToForeground = null;
    //将app移除前台进程
    updateProcessForegroundLocked(app, false, false);
    app.foregroundActivities = false;
    app.hasShownUi = false;
    app.treatLikeActivity = false;
    app.hasAboveClient = false;
    app.hasClientActivities = false;
    //清理service信息,这个过程也比较复杂,后续再展开
    mServices.killServicesLocked(app, allowRestart);
    boolean restart = false;
}
  • mProcessesToGc:记录着需要尽快执行gc的进程列表
  • mPendingPssProcesses:记录着需要收集内存信息的进程列表

4.2 清理ContentProvider

private final boolean cleanUpApplicationRecordLocked(...) {
    ...
    for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
        //获取该进程已发表的ContentProvider
        ContentProviderRecordcpr = app.pubProviders.valueAt(i);
        final boolean always = app.bad || !allowRestart;
        //ContentProvider服务端被杀,则client端进程也会被杀
        boolean inLaunching = removeDyingProviderLocked(app, cpr, always);
        if ((inLaunching || always) && cpr.hasConnectionOrHandle()) {
            restart = true; //需要重启
        }
 
        cpr.provider = null;
        cpr.proc = null;
    }
    app.pubProviders.clear();
 
    //处理正在启动并且是有client端正在等待的ContentProvider
    if (cleanupAppInLaunchingProvidersLocked(app, false)) {
        restart = true;
    }
 
    //取消已连接的ContentProvider的注册
    if (!app.conProviders.isEmpty()) {
        for (int i = app.conProviders.size() - 1; i >= 0; i--) {
            ContentProviderConnectionconn = app.conProviders.get(i);
            conn.provider.connections.remove(conn);
 
            stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
                    conn.provider.name);
        }
        app.conProviders.clear();
    }

4.3 清理BroadcastReceiver

private final boolean cleanUpApplicationRecordLocked(...) {
    ...
    skipCurrentReceiverLocked(app);
 
    // 取消注册的广播接收者
    for (int i = app.receivers.size() - 1; i >= 0; i--) {
        removeReceiverLocked(app.receivers.valueAt(i));
    }
    app.receivers.clear();
}

4.4 清理Process

private final boolean cleanUpApplicationRecordLocked(...) {
    ...
    //当app正在备份时的处理方式
    if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
        ...
        IBackupManagerbm = IBackupManager.Stub.asInterface(
                ServiceManager.getService(Context.BACKUP_SERVICE));
        bm.agentDisconnected(app.info.packageName);
    }
 
    for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
        ProcessChangeItemitem = mPendingProcessChanges.get(i);
        if (item.pid == app.pid) {
            mPendingProcessChanges.remove(i);
            mAvailProcessChanges.add(item);
        }
    }
    mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, null).sendToTarget();
 
    if (!app.persistent || app.isolated) {
        removeProcessNameLocked(app.processName, app.uid);
        if (mHeavyWeightProcess == app) {
            mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
                    mHeavyWeightProcess.userId, 0));
            mHeavyWeightProcess = null;
        }
    } else if (!app.removed) {
        //对于persistent应用,则需要重启
        if (mPersistentStartingProcesses.indexOf(app) < 0) {
            mPersistentStartingProcesses.add(app);
            restart = true;
        }
    }
 
    //mProcessesOnHold:记录着试图在系统ready之前就启动的进程。
    //在那时并不启动这些进程,先记录下来,等系统启动完成则启动这些进程。
    mProcessesOnHold.remove(app);
 
    if (app == mHomeProcess) {
        mHomeProcess = null;
    }
    if (app == mPreviousProcess) {
        mPreviousProcess = null;
    }
 
    if (restart && !app.isolated) {
        //仍有组件需要运行在该进程中,因此重启该进程
        if (index < 0) {
            ProcessList.remove(app.pid);
        }
        addProcessNameLocked(app);
        startProcessLocked(app, "restart", app.processName);
        return true;
    } else if (app.pid > 0 && app.pid != MY_PID) {
        //移除该进程相关信息
        boolean removed;
        synchronized (mPidsSelfLocked) {
            mPidsSelfLocked.remove(app.pid);
            mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
        }
        app.setPid(0);
    }
    return false;
}

对于需要重启进程的情形有:

  • mLaunchingProviders :记录着存在client端等待的ContentProvider。应用当前正在启动中,当ContentProvider一旦发布则将该ContentProvider将从该list去除。当进程包含这样的ContentProvider,则需要重启进程。
  • mPersistentStartingProcesses :记录着试图在系统ready之前就启动的进程。在那时并不启动这些进程,先记录下来,等系统启动完成则启动这些进程。当进程属于这种类型也需要重启。

5. 小结

当crash进程执行kill操作后,进程被杀。此时需要掌握binder 死亡通知原理,由于Crash进程中拥有一个Binder服务端 ApplicationThread ,而应用进程在创建过程调用attachApplicationLocked(),从而attach到system_server进程,在system_server进程内有一个 ApplicationThreadProxy ,这是相对应的Binder客户端。当Binder服务端 ApplicationThread 所在进程(即Crash进程)挂掉后,则Binder客户端能收到相应的死亡通知,从而进入binderDied流程。更多关于bInder原理,这里就不细说,博客中有关于binder系列的专题。

理解 Android Crash处理流程

四、 总结

本文主要以源码的视角,详细介绍了到应用crash后系统的处理流程:

  1. 首先发生crash所在进程,在创建之初便准备好了defaultUncaughtHandler,用来来处理Uncaught Exception,并输出当前crash基本信息;
  2. 调用当前进程中的AMP.handleApplicationCrash;经过binder ipc机制,传递到system_server进程;
  3. 接下来,进入system_server进程,调用binder服务端执行AMS.handleApplicationCrash;
  4. 从 mProcessNames 查找到目标进程的ProcessRecord对象;并将进程crash信息输出到目录 /data/system/dropbox ;
  5. 执行makeAppCrashingLocked
    • 创建当前用户下的crash应用的error receiver,并忽略当前应用的广播;
    • 停止当前进程中所有activity中的WMS的冻结屏幕消息,并执行相关一些屏幕相关操作;
  6. 再执行handleAppCrashLocked方法,
    • 当1分钟内同一进程 连续crash两次 时,且 非persistent 进程,则直接结束该应用所有activity,并杀死该进程以及同一个进程组下的所有进程。然后再恢复栈顶第一个非finishing状态的activity;
    • 当1分钟内同一进程 连续crash两次 时,且 persistent 进程,,则只执行恢复栈顶第一个非finishing状态的activity;
    • 当1分钟内同一进程 未发生连续crash两次 时,则执行结束栈顶正在运行activity的流程。
  7. 通过mUiHandler发送消息 SHOW_ERROR_MSG ,弹出crash对话框;
  8. 到此,system_server进程执行完成。回到crash进程开始执行杀掉当前进程的操作;
  9. 当crash进程被杀,通过binder死亡通知,告知system_server进程来执行appDiedLocked();
  10. 最后,执行清理应用相关的activity/service/ContentProvider/receiver组件信息。

这基本就是整个应用Crash后系统的执行过程。

(责任编辑:好模板)
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
栏目列表
热点内容