Activity启动过程详解

1. 简介

本文源码基于android 27
startActivity的流程较复杂,我们这里将其过程分成三部分:

  1. startActivity–>ActivityManagerService
  2. ActivityManagerService–>ApplicationThread
  3. ApplicationThread–>Activity

这样看起来稍微简单点。

通常,我们要启动一个Activity都是直接调用startActivity,我们就从这里开始进行分析。

2. startActivity–>ActivityManagerService

ActivityManagerService,后面都简称为AMS
从调用startActivityActivityManagerService,先放张时序图:
startActivity–>AMS时序图.png

可以看到,从调用startActivityActivityManagerService之间的过程其实并不复杂。

下面开始进入源码分析,首先来看下Activity中的startActivity方法。

2.1 Activity的startActivity

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            //第二个参数为-1,表示不需要知道Activity启动的结果
            startActivityForResult(intent, -1, options);
        } else {
            startActivityForResult(intent, -1);
        }
    }

startActivity会调用startActivityForResult方法

2.2 Activity的startActivityForResult

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
        if (mParent == null) {//mParent表示当前Activity的父类,一般为null
            options = transferSpringboardActivityOptions(options);

            //调用Instrumentation.execStartActivity(),启动新的Activity。
            //mMainThread类型为ActivityThread, 在attach()时被回调时被赋值。 
            Instrumentation.ActivityResult ar = mInstrumentation.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);
            }
        }
    }

mMainThread.getApplicationThread()会获得一个ApplicationThreadApplicationThreadActivityThread里的一个内部类,后面会用到。

接着调用InstrumentationexecStartActivity方法

2.3 Instrumentation的execStartActivity

Instrumentation类主要用来监控应用程序和系统的交互。

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        //将contextThread转成ApplicationThread.
        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);
                    ActivityResult result = null;
                    if (am.ignoreMatchingSpecificIntents()) {
                        result = am.onStartActivity(intent);
                    }
                    if (result != null) {
                        am.mHits++;
                        return result;
                    } else if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            //实际上这里是通过AIDL来调用AMS的startActivity方法,下面我们看下ActivityManager.getService()的代码
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);//检查StartActivity的结果
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

我们先来看下ActivityManager.getService()中的代码

2.4 ActivityManager的getService

    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    //创建一个单例IActivityManager
    private static final Singleton<IActivityManager> IActivityManagerSingleton =new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    //获取一个关联ActivityManagerService的Binder对象
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);

                    //返回一个IActivityManager的代理对象,基于Binder机制,通过调用代理对象的方法,能够调用到ActivityManagerService相应的方法
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);

                    return am;
                }
            };

Instrumentation中通过Binder调用了AMS的startActivity方法,这样就到了AMS中去了。。

ActivitystartActivityAMSstartActivity,实际上还是比较简单的,下面就交给AMS来处理了。

3. AMS–>ApplicationThread

下面我们来看下AMSApplicationThread的过程。还是先放时序图:
AMS–>ApplicationThread时序图.png

相比于startActivity–>AMS,AMS–>ApplicationThread流程看起来复杂好多了,实际上里面大多数都是一些细节处理。我们对主要流程分析一下,细节部分就略过了。

首先来看下AMSstartActivity方法:

3.1 AMS的startActivity

    @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());
    }

就是调用了startActivityAsUser

3.2 AMS的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);)//检查调用者是否有权限执行操作

        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser");
    }

startActivityAsUser实际上就是检查一下调用者的权限。
然后就是调用ActivityStarterstartActivityMayWait

3.3 ActivityStarter的startActivityMayWait

ActivityStarter这个类看名字就知道是用来启动Activity的,好多启动细节都是在ActivityStarter里面处理的。我们先来看下startActivityMayWait

startActivityMayWait中主要是解析Intent的信息。
接着就是调用startActivityLocked了。后面分析。
最后如果需要返回结果,会执行mService.wait(),所以这里叫MayWait
相关代码细节这里就不分析了,抓住主要部分。

    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, WaitResult outResult,
            Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
            TaskRecord inTask, String reason) {

            //...
            //解析Intent信息等等

            int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
                    aInfo, rInfo, voiceSession, voiceInteractor,
                    resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                    options, ignoreTargetSecurity, componentSpecified, outRecord, inTask,
                    reason);

            //...
            //判断是否需要返回结果

            return res; 

    }

接着看startActivityLocked

3.4 ActivityStarter的startActivityLocked

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

        //...

        mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,
                aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
                callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
                inTask);

        //...
    }

startActivityLocked比较简单,就是调用的startActivity

3.5 ActivityStarter的startActivity

这里的startActivity主要是检查启动权限,创建新的ActivityRecord等等,相关细节不说。

    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
            ActivityRecord[] outActivity, TaskRecord inTask) {

        //...
        //检查启动权限
        //创建ActivityRecord

        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
                options, inTask, outActivity);
    }

可以看到,这里会调另外一个重载的startActivity方法。
这里的startActivity主要是让WindowManager暂停布局。

    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {

        int result = START_CANCELED;
        try {
            mService.mWindowManager.deferSurfaceLayout();//暂停布局
            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, outActivity);
        //继续布局            
        //...    
    }            

继续看startActivityUnchecked

3.6 ActivityStarter的startActivityUnchecked

startActivityUnchecked主要处理intent携带的launch flags, 启动模式(launchMode)等等。
如果有需要,Activity任务(TaskRecord)会在这里创建。

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {

        //...

        mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                        mOptions);

        //...  
    }

然后会调用ActivityStackSupervisorresumeFocusedStackTopActivityLocked

3.7 ActivityStackSupervisor的resumeFocusedStackTopActivityLocked

ActivityStackSupervisor实际上是用来管理Activity任务栈(ActivityStack)的。
resumeFocusedStackTopActivityLocked中首先判断FocusedStack栈中是否存在target ActivityStack
然后就是调用ActivityStackresumeTopActivityUncheckedLocked

  boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {

        //...  

        if (targetStack != null && isFocusedStack(targetStack)) {
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);

        //...  
    }

继续往下看

3.8 ActivityStack的resumeTopActivityUncheckedLocked

ActivityStack就是任务栈,用来管理Activity任务(TaskRecord),而上面的ActivityStackSupervisor实际上用来管理ActivityStack的。

resumeTopActivityUncheckedLocked中首先会判断是否在ResumeTopActivity状态中。

    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;
            result = resumeTopActivityInnerLocked(prev, options);
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }

        //...  
        return result;
    }

然后就是调用resumeTopActivityInnerLocked

3.9 ActivityStack的resumeTopActivityInnerLocked

resumeTopActivityInnerLocked中首先暂停当前Activity,然后启动新Activity

    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {

        //...  
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);

            //暂停当前activity,调用Activity的onPause
            pausing |= startPausingLocked(userLeaving, false, next, false);
        }
        //...          
        mStackSupervisor.startSpecificActivityLocked(next, true, true);//启动新activity
        //...  
    }

startPausingLocked最终会调用到AMS中,AMS再通过调用相应的方法,然后调用resumeTopActivityInnerLocked再调用到mStackSupervisor.startSpecificActivityLocked。细节不表,只需知道这中间会经过AMS就好了。

然后继续看ActivityStackSupervisorstartSpecificActivityLocked

3.10 ActivityStackSupervisor的startSpecificActivityLocked

startSpecificActivityLocked中先获取ProcessRecord(若要启动的Activity的应用已经在运行),若ProcessRecord存在则直接调用realStartActivityLocked(),否则调用 ActivityManagerServices.startProcessLocked()创建新的ProcessRecord

    void startSpecificActivityLocked(ActivityRecord r,boolean andResume, boolean checkConfig) {

        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);//获取ProcessRecord

        r.getStack().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);
            }
        }

        //创建新的ProcessRecord
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

继续看realStartActivityLocked

3.11 ActivityStackSupervisor的realStartActivityLocked

直接看码

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

        //...              
        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                System.identityHashCode(r), r.info,

                mergedConfiguration.getGlobalConfiguration(),
                mergedConfiguration.getOverrideConfiguration(), r.compat,
                r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                r.persistentState, results, newIntents, !andResume,
                mService.isNextTransitionForward(), profilerInfo);
        //...  
     }           

这里的app.thread实际上就是ApplicationThread,可以看到,兜兜转转之后终于到了ApplicationThread

4. ApplicationThread–>Activity

先来看相应的时序图:
ApplicationThread–>Activity时序图.png

从ApplicationThread到真正启动一个Activity,其实也并不是很复杂。

4.1 ApplicationThread的scheduleLaunchActivity

ApplicationThreadActivityThread的内部类。

scheduleLaunchActivity中就是将启动Activity的参数封装成ActivityClientRecord,然后调用ActivityThreadsendMessage方法。

        @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);

            //调用ActivityThread的sendMessage方法,发送LAUNCH_ACTIVITY信息
            sendMessage(H.LAUNCH_ACTIVITY, r);
        }

4.2 ActivityThread的sendMessage

sendMessage实际就是给一个名为mHHandler发信息。

    private void sendMessage(int what, Object obj) {
        sendMessage(what, obj, 0, 0, false);
    }

    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

我们看看H这个类

4.3 H类

    private class H extends Handler { //继承Handler
        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {//启动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);
                } break;
                case RELAUNCH_ACTIVITY: {
                   //...
                } break;
                case PAUSE_ACTIVITY: {
                    //...
                } break;
                case PAUSE_ACTIVITY_FINISHING: {
                    //...
                } break;

可以看到H里面会对各种状态进行处理。LAUNCH_ACTIVITY中会调用handleLaunchActivity,继续往下看。

4.4 ActivityThread的handleLaunchActivity

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        //...
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            //这里最终会调用Activity的onResume生命周期
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        //...
        }

最终会调用performLaunchActivity

4.5 ActivityThread的performLaunchActivity

看代码中的注释吧

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        //从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);
        }

        //ContextImpl在这里创建,ContextImpl是Context的具体实现
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;

        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();

            //通过Instrumentation的newActivity使用类加载器来创建Activity对象
            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);
            }
        } 

        try {
            //创建Application对象,如果已经创建过,则不会重复创建,保证一个应用中只有一个Application对象;
            //实际上跟Activity一样也是在Instrumentation中使用类加载器来创建的
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

           if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());

            if (activity != null) {
                //...

                //attach中会建立Activity与ContextImpl的联系
                //attach中还会创建Window并与Window建立联系 可以看Activivity中的源码
                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, window, r.configCallback);

                //...

                if (r.isPersistable()) {
                    //回调Activity的OnCreate
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    //回调Activity的OnCreate
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }

        //...
        return activity;
    }   

再来看看InstrumentationcallActivityOnCreate

4.6 Instrumentation的callActivityOnCreate

    public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState);
        postPerformCreate(activity);
    }

继续看下activity.performCreate

4.7 Activity的performCreate

    final void performCreate(Bundle icicle) {
        performCreate(icicle, null);
    }

    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        mCanEnterPictureInPicture = true;
        restoreHasCurrentPermissionRequest(icicle);
        if (persistentState != null) {
            onCreate(icicle, persistentState);//onCreate
        } else {
            onCreate(icicle);//onCreate
        }
        mActivityTransitionState.readState(icicle);

        mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
                com.android.internal.R.styleable.Window_windowNoDisplay, false);
        mFragments.dispatchActivityCreated();
        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
    }

可以看到,onCreate被调用起来了。至此,Activity被启动起来了。

最后,放张总的时序图(图太大,请另外打开或保存):
startActivity时序图.png

猜你喜欢

转载自blog.csdn.net/u011810352/article/details/79143764