性能优化——App启动原理详解(启动时间分析检测)

在Android系统中,有两种操作会引发Activity的启动:

  第一种:用户点击应用程序图标时,Launcher会为我们启动应用程序的主Activity;

  第二种:应用程序的默认Activity启动起来后,它又可以在内部通过调用startActvity接口启动新的Activity;

依此类推,每一个Activity都可以在内部启动新的Activity。通过这种连锁反应,按需启动Activity,从而完成应用程序的功能。

本文主要讲点击应用图标,启动应用程序的过程。

在手机屏幕中点击应用程序图标的情景就会引发Android应用程序中的默认Activity的启动,从而把应用程序启动起来。这种启动方式的特点:会启动一个新的进程来加载相应的Activity。

这里,我们来研究下Android应用程序的启动过程,即App的启动流程。我们这里就不说lancher2包中的Launcher了,好像已经过时了,我们直接查看lancher3包中的Launcher。

1、从手机屏幕点击应用图标开始

我们来看下点击应用图标会发生什么事?

public final class Launcher extends Activity
        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
                   View.OnTouchListener {

通过分析源码我们可以知道,我们得Launcher就是一个Activity,而且还是先了几个点击事件的接口,我们去看一下Launcher里面的onClick方法。


   /**
     *
     * @param v 表示点击的应用程序的图标
     */
    public void onClick(View v) {
        // Make sure that rogue clicks don't get through while allapps is launching, or after the
        // view has detached (it's possible for this to happen if the view is removed mid touch).
        if (v.getWindowToken() == null) {
            return;
        }

        if (!mWorkspace.isFinishedSwitchingState()) {
            return;
        }

        if (v instanceof Workspace) {
            if (mWorkspace.isInOverviewMode()) {
                showWorkspace(true);
            }
            return;
        }

        if (v instanceof CellLayout) {
            if (mWorkspace.isInOverviewMode()) {
                showWorkspace(mWorkspace.indexOfChild(v), true);
            }
        }

        Object tag = v.getTag();      //获取图标的应用标识
        if (tag instanceof ShortcutInfo) {
            onClickAppShortcut(v);
        } else if (tag instanceof FolderInfo) {
            if (v instanceof FolderIcon) {
                onClickFolderIcon(v);
            }
        } else if (v == mAllAppsButton) {
            onClickAllAppsButton(v);
        } else if (tag instanceof AppInfo) {    //说明是应用程序
            startAppShortcutOrInfoActivity(v);
        } else if (tag instanceof LauncherAppWidgetInfo) {
            if (v instanceof PendingAppWidgetHostView) {
                onClickPendingWidget((PendingAppWidgetHostView) v);
            }
        }
    }

当点击Launcher上面的应用图标的时候,就会触发onClick方法的执行,然后根据传入参数(View v)的v.getTag()方法得到被点击应用图标的tag,然后判断tag的类型,我们从代码逻辑上可以看出AppInfo是我们的应用程序的类型。所以直接调用了Launcher的startAppShortcutOrInfoActivity(v)方法。

2、然后我们看Launcher的startAppShortcutOrInfoActivity(v)方法

  @Thunk void startAppShortcutOrInfoActivity(View v) {
        Object tag = v.getTag();
        final ShortcutInfo shortcut;
        final Intent intent;
        if (tag instanceof ShortcutInfo) {
            shortcut = (ShortcutInfo) tag;
            intent = shortcut.intent;
            int[] pos = new int[2];
            v.getLocationOnScreen(pos);
            intent.setSourceBounds(new Rect(pos[0], pos[1],
                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));

        } else if (tag instanceof AppInfo) {
            shortcut = null;
            intent = ((AppInfo) tag).intent;    //获取v中的意图,有了意图我们就可以启动Activity了
        } else {
            throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
        }

        boolean success = startActivitySafely(v, intent, tag);   //这里就是启动入口
        mStats.recordLaunch(v, intent, shortcut);

        if (success && v instanceof BubbleTextView) {
            mWaitingForResume = (BubbleTextView) v;
            mWaitingForResume.setStayPressed(true);
        }
    }

从上面的代码我们可以看出,launcher3和launcher2中的启动有一点不一样,就是launcher3中有了AppInfo这个类,launcher2中是没有的,这也更方便了我们的程序启动。最后还是同样调用了Launcher中的startActivitySafely(v, intent, tag);方法。

3、然后我们看Launcher的startActivitySafely(v, intent, tag)方法

public boolean startActivitySafely(View v, Intent intent, Object tag) {
        boolean success = false;
        if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
            Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
            return false;
        }
        try {
            success = startActivity(v, intent, tag);    //调用Launcher的 startActivity(v, intent, tag); 
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
        }
        return success;
    }

3、然后我们看Launcher的 startActivity(v, intent, tag);

private boolean startActivity(View v, Intent intent, Object tag) {

        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  //并且传入参数intent,对Intent添加flag,FLAG_ACTIVITY_NEW_TASK
        ...........
             代码较多省略了许多            

        startActivity(intent, optsBundle); //最后就调用了Activity的startActivity(Intent intent, @Nullable Bundle)方法 options) 
        ...........
        return false;
    }

这里的intent包含的信息为:action = "android.intent.action.Main",category="android.intent.category.LAUNCHER", cmp="shy.luo.activity/.MainActivity",表示它要启动的Activity为shy.luo.activity.MainActivity。

Intent.FLAG_ACTIVITY_NEW_TASK表示要在一个新的Task中启动这个Activity,注意,Task是Android系统中的概念,它不同于进程Process的概念。

简单地说,一个Task是一系列Activity的集合,这个集合是以堆栈的形式来组织的,遵循后进先出的原则。

稍微扩展一下:
(1)我们知道,一个应用程序可以有多个Activity,每个Activity是同级别的。那么在启动程序时,最先启动哪个Activity呢?
(2)有些程序可能需要显示在程 序列表里,有些不需要。怎么定义呢?

android.intent.action.MAIN决定应用程序最先启动的Activity ;
android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里。

<intent-filter>
     <action android:name="android.intent.action.MAIN" />
     <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

Main和LAUNCHER同时设定才有意义,如果有多个同级的Activity都有过滤器,则只有最前面的Activity的 <action android:name="android.intent.action.MAIN" /> 有 效,启动该程序时,执行的是该Activity。且在程序列表中有多个图标,这些Activity都在程序列表中显示,该Application有多个入 口,执行不同的Activity,但是整个程序的主入口(整个程序最先运行的那个activity)只有最先定义的那个Activity。

如果一个应用没有Launcher则该apk仍能安装到设备上,但是在主程序图中看不到。如果给那个Activity定了Launcher,且同时设定了Main,则这个Activity就可出现在程序图中;如果没有Main,则不知启动哪Activity,故也不会有图标出现。

4、然后我们看Activity中的startActivity(intent, bundle)

    @Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }
    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);  
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);  //这个方法其实也是调用了上面的startActivityForResult(intent, -1, null);  
        }
    }

从源码可以看出Activity.startActivity方法又调用了Activity.startActivityForResult方法。

5、Activity中的startActivityForResult(Intent intent, int requestCode,Bundle options)

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(    //然后就走到了Instrumentation中的execStartActivity()
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                mStartedActivity = true;
            }
            cancelInputsAndStartExitTransition(options);
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

mInstrumentation是Activity类的成员变量,它的类型是Intrumentation,它用来监控应用程序和系统的交互。

mMainThread也是Activity类的成员变量,它的类型是ActivityThread,它代表的是应用程序的主线程。这里通过mMainThread.getApplicationThread获得它里面的ApplicationThread成员变量,它是一个Binder对象,ActivityManagerService会使用它来和ActivityThread来进行进程间通信,这里我们需注意的是,这里的mMainThread代表的是Launcher应用程序运行的进程。

mToken也是Activity类的成员变量,它是一个Binder对象的远程接口。

6、Instrumentation.execStartActivity() 里面的参数有点多

 {@hide}
public ActivityResult execStartActivity(    //返回的是一个ActivityResult
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManagerNative.getDefault()     //启动Activity
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

从上面的代码可以看出,启动Activity真正的实现由ActivityManagerNative.getDefault().startActivity方法执行。

这里的ActivityManagerNative.getDefault() 它返回的是一个ActivityManagerService的一个代理类ActivityManagerProxy。

此时启动Activity这一操作交给了ActivityManagerNative.getDefault()的startActivity()方法.ActivityManagerNative是一个抽象类,继承了Binder,同时它又实现了IActivityManager接口,其实现类是ActivityManagerService(AMS).

ActivityManagerNative.getDefault()方法返回IActivityManager类型对象,其实现类是ActivityManagerProxy,其构造函数接受一个IBinder类型,其实就是ActivityManagerService对象,此时你应该发现ActivityManagerNative,IActivityManager以及ActivityManagerProxy三者之间的联系如下:

代理模式图

通过上图,我们看到这三者就是一个典型的代理模式:ActivityManagerProxy就是ActivityManagerService的远程代理,那么此时ActivityManagerNative的作用也就很明显:返回AMS的远程代理对象,这样Launcher应用就能和AMS服务通信了.

7. ActivityManagerService的代理类 ActivityManagerProxy.startActivity()

ActivityManagerProxy是ActivityManagerNative中的一个内部类。

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

由于ActivityManagerProxy是一个代理类,上面是通过IPC的Binder联系到ActivityManagerService,最后会调用ActivityManagerService的startActivity方法。

我们用一张图来简单的描述上述整个流程:

启动流程调用示意图

8. ActivityManagerService.startActivity()和startActivityAsUser()

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {

        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
      //调用了ActivityManagerService的startActivityAsUser()
    }
    @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {

        enforceNotIsolatedCaller("startActivity");
        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, false, ALLOW_FULL_ONLY, "startActivity", null);
        // TODO: Switch to user app stacks here.

        //这里的mActivityStarter的类型是ActivityStarter。
        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, bOptions, false, userId, null, null);
    }

9. ActivityStarter.startActivityMayWait()和ActivityStackSupervisor.resolveActivity()

final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, IActivityManager.WaitResult outResult, Configuration config,
            Bundle bOptions, boolean ignoreTargetSecurity, int userId,
            IActivityContainer iContainer, TaskRecord inTask) {
  
        //省略了很多代码 
       .................................    
        // 调用ActivityStackSupervisor.resolveActivity()
        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
        .................................

            final ActivityRecord[] outRecord = new ActivityRecord[1];
            int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
                    aInfo, rInfo, voiceSession, voiceInteractor,
                    resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                    options, ignoreTargetSecurity, componentSpecified, outRecord, container,
                    inTask);

          
      .................................
            return res;
        }
    }
ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
            ProfilerInfo profilerInfo) {
        final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
        if (aInfo != null) 
            intent.setComponent(new ComponentName(
                    aInfo.applicationInfo.packageName, aInfo.name));
            if (!aInfo.processName.equals("system")) {
                if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
                    mService.setDebugApp(aInfo.processName, true, false);
                }
                if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
                    mService.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
                }
                if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
                    mService.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
                }
                if (profilerInfo != null) {
                    mService.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
                }
            }
        }
        return aInfo;
    }

ActivityStackSupervisor的resolveActivity方法主要是获得对参数intent的内容进行解析,得到MainActivity的相关信息,保存在aInfo变量中。解析之后,得到的aInfo.applicationInfo.packageName的值为应用的包名,aInfo.name的值为MainActivity的完整名称,被定为

<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

的Activity,这是在这个实例的配置文件AndroidManifest.xml里面配置的。然后又调用了ActivityStarter的startActivityLocked方法。

10. ActivityStarter.startActivityLocked()

 final int startActivityLocked(IApplicationThread caller,
            Intent intent, String resolvedType, ActivityInfo aInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode,
            int callingPid, int callingUid, String callingPackage,
            int realCallingPid, int realCallingUid, int startFlags, Bundle options,
            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
            ActivityContainer container, TaskRecord inTask) {

        int err = ActivityManager.START_SUCCESS;

        ProcessRecord callerApp = null;
        if (caller != null) {
            callerApp = mService.getRecordForAppLocked(caller);
            if (callerApp != null) {
                callingPid = callerApp.pid;
                callingUid = callerApp.info.uid;
            } else {
               .........................
            }
        }

       ...........................................

        ActivityRecord sourceRecord = null;
        ActivityRecord resultRecord = null;
        if (resultTo != null) {
            sourceRecord = isInAnyStackLocked(resultTo);
            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                    "Will send result to " + resultTo + " " + sourceRecord);
            if (sourceRecord != null) {
                if (requestCode >= 0 && !sourceRecord.finishing) {
                    resultRecord = sourceRecord;
                }
            }
        }

        final int launchFlags = intent.getFlags();

        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
            // Transfer the result target from the source activity to the new
            // one being started, including any failures.
            if (requestCode >= 0) {
                ActivityOptions.abort(options);
                return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
            ..................................
        }
        }

        if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
           .................
        }

        if (err == ActivityManager.START_SUCCESS && aInfo == null) {
           ................
        }

        if (err == ActivityManager.START_SUCCESS
             .................
        }

       ...........................................


        ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
                requestCode, componentSpecified, voiceSession != null, this, container, options);

        ....................................

        doPendingActivityLaunchesLocked(false);

        ....................................

从传进来的参数caller得到调用者的进程信息,并保存在callerApp变量中,这里就是Launcher应用程序的进程信息了。 前面说过,参数resultTo是Launcher这个Activity里面的一个Binder对象,通过它可以获得Launcher这个Activity的相关信息,保存在sourceRecord变量中。再接下来,创建即将要启动的Activity的相关信息,并保存在r变量中:

ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
                requestCode, componentSpecified, voiceSession != null, this, container, options);

然后调用 ActivityStarter. doPendingActivityLaunchesLocked(false);方法。

11. ActivityStarter.doPendingActivityLaunchesLocked(false)

final void doPendingActivityLaunchesLocked(boolean doResume) {
        while (!mPendingActivityLaunches.isEmpty()) {
            final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
            final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
            try {
                final int result = startActivityUnchecked(
                        pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null, null);

                postStartActivityUncheckedProcessing(
                        pal.r, result, mSupervisor.mFocusedStack.mStackId, mSourceRecord,
                        mTargetStack);
            } catch (Exception e) {
                Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
                pal.sendErrorResult(e.getMessage());
            }
        }
    }

12. ActivityStarter.startActivityUnchecked()

这个方法主要是创建Acitivity栈,根据Activity的启动模式,由于是点击图标进来的,一开始也设置了标志位Intent.FLAG_ACTIVITY_NEW_TASK,所以会新创建一个Activity栈。如果之前已经有栈,且不是singleinstance,就不会再创建新的栈,会将待启动的Activity添加到原来的栈中。最后将会调到ActivityStack的resumeTopActivityUncheckedLocked()中。

13、ActivityStack.resumeTopActivityUncheckedLocked()

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mStackSupervisor.inResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }

        boolean result = false;
        try {
            // Protect against recursion.
            mStackSupervisor.inResumeTopActivity = true;
            if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
                mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
                mService.updateSleepIfNeededLocked();
            }
            result = resumeTopActivityInnerLocked(prev, options);
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }
        return result;
    }

14、ActivityStack.resumeTopActivityInnerLocked()

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {

      ---------------------------
    省略多余的代码,太长了

       mStackSupervisor.startSpecificActivityLocked(next, true, true);

      ---------------------------

}

(1)这个方法还会调用通过ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;如果是通过其他Activity启动的。会将其他Activity进入Paused状态。由于这里是Launcher发起的startActivity,所以Launcher进入Paused状态。

(2)调用了ActivityStackSupervisor.startSpecificActivityLocked方法。

15、ActivityStackSupervisor.startSpecificActivityLocked()

void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        r.task.stack.setLaunchTime(r);

         //该应用的进程已经存在
        if (app != null && app.thread != null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {

                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

        }

        //该应用的进程尚未创建,调用AMS的startProcessLocked()
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

注意,这里由于是第一次启动应用程序的Activity,取回来的app为null。在Activity应用程序中的AndroidManifest.xml配置文件中,我们没有指定Application标签的process属性,系统就会默认使用package的名称。

每一个应用程序都有自己的uid,因此,这里uid + process的组合就可以为每一个应用程序创建一个ProcessRecord。所以下面if (app != null && app.thread != null)条件不成立,那么条件里面realStartActivityLocked 方法就不会执行,而是执行ActivityManagerService.startProcessLocked函数进行下一步操作。

15、ActivityManagerService.startProcessLocked()

 final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
            boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
       
        .............................
            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
           ...................

        String hostingNameStr = hostingName != null
                ? hostingName.flattenToShortString() : null;

        if (app == null) {
            checkTime(startTime, "startProcess: creating new process record");
        //  创建一个新的ProcessRecord
            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);

            if (app == null) {
                Slog.w(TAG, "Failed making new process record for "
                        + processName + "/" + info.uid + " isolated=" + isolated);
                return null;
            }
            app.crashHandler = crashHandler;
            checkTime(startTime, "startProcess: done creating new process record");
        } else {
            ............................
        }

       ...............................

        startProcessLocked(
                app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
        
    ........

        return (app.pid != 0) ? app : null;
    }

首先调用getProcessRecordLocked,但是返回的app为null,所以下面这一句就会执行。

              app = newProcessRecordLocked(info, processName, isolated, isolatedUid);

newProcessRecordLocked方法会调用addProcessNameLocked方法,addProcessNameLocked方法中会执行mProcessNames.put(proc.processName, proc.uid, proc);这时候mProcessNames这个全局变量就有数据了,再调用getProcessRecordLocked,返回的就不是null了。

接着,ActivityManagerService.startProcessLocked方法会执行另一个无返回值的startProcessLocked方法。

 private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
        
        .............................

                try {
                    checkTime(startTime, "startProcess: getting gids from package manager");
                    final IPackageManager pm = AppGlobals.getPackageManager();
                    permGids = pm.getPackageGids(app.info.packageName, app.userId);
                    MountServiceInternal mountServiceInternal = LocalServices.getService(
                            MountServiceInternal.class);
                    mountExternal = mountServiceInternal.getExternalStorageMountMode(uid,
                            app.info.packageName);
                } catch (RemoteException e) {
                    throw e.rethrowAsRuntimeException();
                }

        .....................................

          //记住这里新进程导入的是android.app.ActivityThread类
          if (entryPoint == null)   entryPoint = "android.app.ActivityThread";

                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName);
                checkTime(startTime, "startProcess: asking zygote to start proc");

          Process.ProcessStartResult startResult = Process.start(entryPoint,
                app.processName, uid, uid, gids, debugFlags, mountExternal,
                app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                app.info.dataDir, entryPointArgs);

        .......................

这里主要是调用Process.start接口来创建一个新的进程,新的进程会导入android.app.ActivityThread类,并且执行它的main函数,这就是为什么我们前面说每一个应用程序都有一个ActivityThread实例来对应的原因.

到现在我们同样用一张图来描述这过程:

image.png

16、走进Zygote 调用Process.start接口来创建一个新的进程

    /* 
     * {@hide}
     */
    public static final ProcessStartResult start(final String processClass,
                                  final String niceName,
                                  int uid, int gid, int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
        }
    }

start方法又调用了startViaZygote方法,该方法会通过zygote机制开启一个新的进程。由于我们导入的类名是android.app.ActivityThread,开启一个ActivityThread进程,这也是为什么一个应用程序只有一个ActivityThread,然后会执行他的main方法。

17、执行ActivityThread.main()函数和ActivityThread.attach(false)函数

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());  //在libcore中设置事件日志记录的报告程序

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        //确保可信证书在正确的位置查找CA证书
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");
        
        //将当前线程初始化为Looper,将其标记为应用程序的主Loopper对象,应用程序的主线程是由Androi环境创建的,因此您永远不需要自己调用此函数。

        Looper.prepareMainLooper();    

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

我们在这个方法里创建ActivityThread,并且构建主线程的消息循环系统。

private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ViewRootImpl.addFirstDrawHandler(new Runnable() {
                @Override
                public void run() {
                    ensureJitEnabled();
                }
            });
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            // Watch for getting close to heap limit.
            BinderInternal.addGcWatcher(new Runnable() {
                @Override public void run() {
                    if (!mSomeActivitiesChanged) {
                        return;
                    }
                    Runtime runtime = Runtime.getRuntime();
                    long dalvikMax = runtime.maxMemory();
                    long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
                    if (dalvikUsed > ((3*dalvikMax)/4)) {
                        if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
                                + " total=" + (runtime.totalMemory()/1024)
                                + " used=" + (dalvikUsed/1024));
                        mSomeActivitiesChanged = false;
                        try {
                            mgr.releaseSomeActivities(mAppThread);
                        } catch (RemoteException e) {
                            throw e.rethrowFromSystemServer();
                        }
                    }
                }
            });
        } else {
            // Don't set application object here -- if the system crashes,
            // we can't display an alert, we just want to die die die.
            android.ddm.DdmHandleAppName.setAppName("system_process",
                    UserHandle.myUserId());
            try {
                mInstrumentation = new Instrumentation();
                ContextImpl context = ContextImpl.createAppContext(
                        this, getSystemContext().mPackageInfo);
                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                mInitialApplication.onCreate();
            } catch (Exception e) {
                throw new RuntimeException(
                        "Unable to instantiate Application():" + e.toString(), e);
            }
        }

        // add dropbox logging to libcore
        DropBox.setReporter(new DropBoxReporter());

        ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
            @Override
            public void onConfigurationChanged(Configuration newConfig) {
                synchronized (mResourcesManager) {
                    // We need to apply this change to the resources
                    // immediately, because upon returning the view
                    // hierarchy will be informed about it.
                    if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
                        updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
                                mResourcesManager.getConfiguration().getLocales());

                        // This actually changed the resources!  Tell
                        // everyone about it.
                        if (mPendingConfiguration == null ||
                                mPendingConfiguration.isOtherSeqNewer(newConfig)) {
                            mPendingConfiguration = newConfig;

                            sendMessage(H.CONFIGURATION_CHANGED, newConfig);
                        }
                    }
                }
            }
            @Override
            public void onLowMemory() {
            }
            @Override
            public void onTrimMemory(int level) {
            }
        });
    }

ActivityThread的内部类ApplicationThread是一个Binder,获得系统的ActivityManagerService, 调用它的attachApplication(mAppThread)并把ApplicationThread的对象mAppThread(是一个binder对象)传给ActivityManagerService,AMS拿到了这个Binder对象后做了3件事。

18、ActivityManagerService.attachApplication()和ActivityManagerService.attachApplicationLocked()

    @Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
        ProcessRecord app;
        if (pid != MY_PID && pid >= 0) {
            synchronized (mPidsSelfLocked) {
                app = mPidsSelfLocked.get(pid);
            }
        } else {
            app = null;
        }

        ................................
        final String processName = app.processName;
        try {
            AppDeathRecipient adr = 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;
        }

        EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);

        app.makeActive(thread, mProcessStats);
        app.curAdj = app.setAdj = -100;
        app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
        app.forcingToForeground = null;
        updateProcessForegroundLocked(app, false, false);
        app.hasShownUi = false;
        app.debugging = false;
        app.cached = false;
        app.killedByAm = false;

        mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

        boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
        
            ..............

        boolean badApp = false;
        boolean didSomething = false;

        // See if the top visible activity is waiting to run in this process...
        if (normalMode) {
            try {
                if (mStackSupervisor.attachApplicationLocked(app)) {    //在这里有调用了ActivityStackSupervisor.attachApplicationLocked()
                    didSomething = true;
                }
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
            }
        }

        ..............................

        return true;
    }

19、ActivityStackSupervisor.attachApplicationLocked(app)

boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = false;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                if (!isFocusedStack(stack)) {
                    continue;
                }
                ActivityRecord hr = stack.topRunningActivityLocked();
                if (hr != null) {
                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                            && processName.equals(hr.processName)) {
                        try {
                            if (realStartActivityLocked(hr, app, true, true)) {
                                didSomething = true;
                            }
                        } catch (RemoteException e) {
                            Slog.w(TAG, "Exception in new application when starting activity "
                                  + hr.intent.getComponent().flattenToShortString(), e);
                            throw e;
                        }
                    }
                }
            }
        }
        if (!didSomething) {
            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
        }
        return didSomething;
    }

调用ActivityStackSupervisor.realStartActivityLocked执行真正的Activity启动操作。这里要启动的Activity通过调用ActivityStackSupervisor.topRunningActivityLocked(null)从堆栈顶端取回来,这时候在堆栈顶端的Activity就是MainActivity了。

20、ActivityStackSupervisor.topRunningActivityLocked()

 final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig)
            throws RemoteException {

        
            .....................
            app.forceProcessStateUpTo(mService.mTopProcessState);
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);

            ..........................

        return true;
    }

app.thread是一个代理对象,代理的就是ApplicationThread,ApplicationThread是ActivityThread的一个内部类.所以逻辑转向了ApplicationThread的scheduleLaunchActivity方法。

20、ApplicationThreadProxy.scheduleLaunchActivity

@Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);    //发送启动acitvity的消息
        }

函数首先创建一个ActivityClientRecord实例,并且初始化它的成员变量,然后发送一个启动Activity的消息交给Handler处理,这个Handler有着一个很简洁的名字:H,然后看一下H对消息的处理,H定义ActivityThrad类中。

21.在ActivityThread类中的内部类H(是一个Handler)中接收消息

public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } 

                ----------------------------------

可以看出这个消息的处理交给了ActivityThread的handleLaunchActivity方法来处理。

22.ActivityThread.handleLaunchActivity()

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }

        handleConfigurationChanged(null, null);

        if (localLOGV) Slog.v(
            TAG, "Handling launch of " + r);

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

            if (!r.activity.mFinished && r.startsNotResumed) {

                performPauseActivityIfNeeded(r, reason);

                if (r.isPreHoneycomb()) {
                    r.state = oldState;
                }
            
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

从上面的源码可以看出,performLaunchActivity最终完成了Activity对象的创建和启动过程,并且Activity通过handleResumeActivity方法来调用被启动Activity的onResume这一生命周期方法。而onCreate这个这个生命周期方法在performLaunchActivity方法中被回调。

23、ActivityThread.performLaunchActivity

performLaunchActivity()主要做了以下几个事:

(1)从ActivityClientRecord中获取待启动的Activity的组件信息。

        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

(2)通过Instrumenttation的newActivity方法使用类加载器创建Activity对象

       Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

(3)通过LoadeApk的makeApplication方法来尝试创建Application对象

 Application app = r.packageInfo.makeApplication(false, mInstrumentation);
 public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            if (!mPackageName.equals("android")) {
                initializeJavaContextClassLoader();
            }
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }

        // Rewrite the R 'constants' for all library apks.
        SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
                .getAssignedPackageIdentifiers();
        final int N = packageIdentifiers.size();
        for (int i = 0; i < N; i++) {
            final int id = packageIdentifiers.keyAt(i);
            if (id == 0x01 || id == 0x7f) {
                continue;
            }

            rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
        }

        return app;
    }

(4)创建ContextImpl对象并通过Activity的attach方法来完成一些重要数据的初始化

Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor);

ContextImpl是一个很重要的数据结构,它是Context的具体实现,Context中的大部分逻辑都是由ContextImpl来完成的。ContextImpl是通过Activity的attach方法来和Activity建立连接的,处理之外,在attach方法中Activity还会完成Window的创建并建立自己和Window的关联,这样当Window接受到外部输入事件后就可以将时间传递给Activity。

(5)调用Activity的onCreate

if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    if (r.isPersistable()) {  //如果是重新启动
                        mInstrumentation.callActivityOnPostCreate(activity, r.state,r.persistentState);  
                    } else {
                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onPostCreate()");
                    }
                }

最后回调Activity的onCreate方法。这也意味着应用已经启动起来了。

到现在,我们已经完全走完应用冷启动的流程,上面的过程看起来繁琐,但是结合时序图来看应该是比较清晰的.抛开细节不看,那么上述的整个流程可以用下图概括:


image.png

最后为了方便系统理解整个流程,附上一张相对完整的时序图:

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容