Activity源码浅析一

1. startActivity(Intent)和startActivity(Intent, Bundle)的区别

startActivity(Intent, Bundle)中options Bundle 仅在 Activity 启动过程中使用,不会传递给目标 Activity 作为其 Intent 的一部分,主要用于提供额外的启动选项和参数。

主要用途
  • 传递 Activity 启动选项:
    动画效果
    启动模式相关配置
    多窗口模式设置

  • 共享元素转场动画:
    实现 Activity 间的共享元素过渡动画
    需要与 ActivityOptions.makeSceneTransitionAnimation() 配合使用

  • 其他高级启动配置:
    启动到画中画模式
    设置初始窗口大小(针对可调整大小的 Activity)

多窗口模式启动

Intent intent = new Intent(this, SecondaryActivity.class);
Bundle options = ActivityOptions.makeBasic()
        .setLaunchDisplayId(secondaryDisplayId)
        .toBundle();
startActivity(intent, options);

带转场动画的启动

Intent intent = new Intent(this, TargetActivity.class);
Bundle options = ActivityOptions.makeCustomAnimation(
        this, R.anim.slide_in_right, R.anim.slide_out_left).toBundle();
startActivity(intent, options);

2. startActivities(Intent[])

startActivities(Intent[])可以一次启动多个Activity,典型的应用场景包括:当需要从外部链接直接跳转到应用内多层页面时,当需要清除现有栈并创建新栈时。

3. startActivity的流程

startActivity会存在两种情况,启动同一进程的Activity,启动不同进程的Activity。咱们先以启动不同进程的Activity为例,之后再分析两者的区别。
咱们以进程A启动进程B的Activity为例开始分析。

    @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();

        // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
        // generally not allowed, except if the caller specifies the task id the activity should
        // be launched in. A bug was existed between N and O-MR1 which allowed this to work. We
        // maintain this for backwards compatibility.
        final int targetSdkVersion = getApplicationInfo().targetSdkVersion;

        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                && (targetSdkVersion < Build.VERSION_CODES.N
                        || targetSdkVersion >= Build.VERSION_CODES.P)
                && (options == null
                        || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity"
                            + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                            + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, applyLaunchDisplayIfNeeded(options));
    }

实际执行的是ContextImpl中的startActivity,这个方法中首先会判断在Activity以外调用startActivity时是否添加了FLAG_ACTIVITY_NEW_TASK这个Flag,如果没添加则报异常,这个异常应该大部分人都见过,现在的问题是它怎么判断的是在Activity内调用还是不在Activity中调用的?
答案是这样的,如果是在Activity上下文启动Activity,会触发Activity类的startActivity方法,而不是ContextImpl中的startActivity方法,所以刚才的分析是错误的,这里也要分两种情况:

  1. 从Activity启动Activity;
  2. 从非Activity启动Activity,非Activity指Service,Broadcast等;

咱们先看从非Activity启动Activity的流程,估计最后两种情况应该殊途同归。

mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, applyLaunchDisplayIfNeeded(options));

这里mMainThread是ActivityThread实例,这个对象是从哪来的呢?咱们以从Service启动Activity为例,在handleCreateService中创建Service对象时是在handleCreateService方法中

#handleCreateService()
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
            ContextImpl context = ContextImpl.getImpl(service
                    .createServiceBaseContext(this, packageInfo));
            ...
            context.setOuterContext(service);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());

这里会触发service.createServiceBaseContext方法,如下

    public Context createServiceBaseContext(ActivityThread mainThread, LoadedApk packageInfo) {
        return ContextImpl.createAppContext(mainThread, packageInfo);
    }

在ContextImpl的createAppContext中触发了ContextImpl构造方法并传入了ActivityThread实例。

    @UnsupportedAppUsage
    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        return createAppContext(mainThread, packageInfo, null);
    }

    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
            String opPackageName) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo,
                ContextParams.EMPTY, null, null, null, null, null, 0, null, opPackageName,
                DEVICE_ID_DEFAULT, false);
        context.setResources(packageInfo.getResources());
        context.mContextType = isSystemOrSystemUI(context) ? CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
                : CONTEXT_TYPE_NON_UI;
        return context;
    }

这样咱们就追到了mMainThread的来源。
接下来是调用mMainThread的getInstrumentation()方法。

    public Instrumentation getInstrumentation()
    {
        return mInstrumentation;
    }

这个mInstrumentation是Instrumentation类的实例,Instrumentation是在handleBindApplication时创建的实例。Instrumentation中文翻译为:仪器,音乐,用具,这里可以理解为一个工具类,主要用来监控应用程序和系统的交互。
这里会触发Instrumentation#execStartActivity方法

    @UnsupportedAppUsage
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        if (DEBUG_START_ACTIVITY) {
            Log.d(TAG, "startActivity: who=" + who + " source=" + target + " intent=" + intent
                    + " requestCode=" + requestCode + " options=" + options, new Throwable());
        }
        Objects.requireNonNull(intent);
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        if (isSdkSandboxAllowedToStartActivities()) {
            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
        }
        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()) {
                        if (options == null) {
                            options = ActivityOptions.makeBasic().toBundle();
                        }
                        result = am.onStartActivity(who, intent, options);
                    }
                    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(who);
            intent.prepareToLeaveProcess(who);
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getOpPackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                    target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
            notifyStartActivityResult(result, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

这里调用了ActivityTaskManager.getService().startActivity,但是咱们先停一下,现在去看一下从Activity启动Activity的情况,会不会两种情况在这里交汇。

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            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) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

Activity#startActivity最终会调用startActivityForResult,在这个方法中当mParent为null时也来到了Instrumentation#execStartActivity方法,神奇的事情就这样发生了,两种情况交汇了。
现在又有三个问题:

  1. mParent是什么?
  2. ActivityTaskManager是什么?
  3. execStartActivity方法的注释中说,可以通过此方法监听和修改Activity启动,具体怎么做?

咱们先看第一个问题,mParent是什么
mParent是一个Activity实例,mParent在Activity#attach方法中进行了赋值

-->ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
              activity.attach(activityBaseContext, 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.activityConfigCallback,
                        r.assistToken, r.shareableActivityToken, r.initialCallerInfoAccessToken);
...
}

ActivityThread#performLaunchActivity中创建activity实例之后触发了它的attach方法,并传入了r.parent,这里的r是ActivityClientRecord实例,在这里绕了很久,把这个方法给丢了

-->ActivityThread
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public final Activity startActivityNow(Activity parent, String id,
            Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
            Activity.NonConfigurationInstances lastNonConfigurationInstances, IBinder assistToken,
            IBinder shareableActivityToken) {
        ActivityClientRecord r = new ActivityClientRecord();
            r.token = token;
            r.assistToken = assistToken;
            r.shareableActivityToken = shareableActivityToken;
            r.ident = 0;
            r.intent = intent;
            r.state = state;
            r.parent = parent;
            r.embeddedID = id;
            r.activityInfo = activityInfo;
            r.lastNonConfigurationInstances = lastNonConfigurationInstances;
        if (localLOGV) {
            ComponentName compname = intent.getComponent();
            String name;
            if (compname != null) {
                name = compname.toShortString();
            } else {
                name = "(Intent " + intent + ").getComponent() returned null";
            }
            Slog.v(TAG, "Performing launch: action=" + intent.getAction()
                    + ", comp=" + name
                    + ", token=" + token);
        }
        // TODO(lifecycler): Can't switch to use #handleLaunchActivity() because it will try to
        // call #reportSizeConfigurations(), but the server might not know anything about the
        // activity if it was launched from LocalAcvitivyManager.
        return performLaunchActivity(r, null /* customIntent */);
    }

在这个方法中给r.parent = parent;赋值了,这个方法又被LocalActivityManager调用了,这个类已经废弃了,被Fragment和FragmentManager取代了,应该是只在TabActivity中才会使用,正常启动activity的情况mParent一直是空的,所以对于mParent不空的情况咱们也不分析了。
接下来咱们回调第二个问题:2. ActivityTaskManager是什么?
ActivityTaskManager是ActivityTaskManagerService的客户端,这里就发生了一次进程通信,之前的逻辑都在触发的进程执行,ActivityTaskManagerService是一个新的系统服务,用来管理Activity和任务栈。
第三个问题:3. execStartActivity方法的注释中说,可以通过此方法监听和修改Activity启动,具体怎么做?
这个方法是hide的,不支持APP使用,不知道为什么注释中说可以重写,难道是之前老版本遗留的注释?或者通过动态代理和反射?
插桩
普通应用好像没法使用,再就是在单元测试中可以使用。

咱们继续往下分析流程。

-->Instrumentation
    @UnsupportedAppUsage
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
...
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
...
}

如果是从Activity启动的,则target不为空,则可以重写Activity的onProvideReferrer方法提供启动来源,在目标Activity中通过getReferrer()方法获取启动来源。

    /**
     * Override to generate the desired referrer for the content currently being shown
     * by the app.  The default implementation returns null, meaning the referrer will simply
     * be the android-app: of the package name of this activity.  Return a non-null Uri to
     * have that supplied as the {@link Intent#EXTRA_REFERRER} of any activities started from it.
     */
    public Uri onProvideReferrer() {
        return null;
    }
    /**
     * Return information about who launched this activity.  If the launching Intent
     * contains an {@link android.content.Intent#EXTRA_REFERRER Intent.EXTRA_REFERRER},
     * that will be returned as-is; otherwise, if known, an
     * {@link Intent#URI_ANDROID_APP_SCHEME android-app:} referrer URI containing the
     * package name that started the Intent will be returned.  This may return null if no
     * referrer can be identified -- it is neither explicitly specified, nor is it known which
     * application package was involved.
     *
     * <p>If called while inside the handling of {@link #onNewIntent}, this function will
     * return the referrer that submitted that new intent to the activity only after
     * {@link #setIntent(Intent)} is called with the provided intent.</p>
     *
     * <p>Note that this is <em>not</em> a security feature -- you can not trust the
     * referrer information, applications can spoof it.</p>
     */
    @Nullable
    public Uri getReferrer() {
        Intent intent = getIntent();
        if (intent != null) {
            try {
                Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER, android.net.Uri.class);
                if (referrer != null) {
                    return referrer;
                }
                String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME);
                if (referrerName != null) {
                    return Uri.parse(referrerName);
                }
            } catch (BadParcelableException e) {
                Log.w(TAG, "Cannot read referrer from intent;"
                        + " intent extras contain unknown custom Parcelable objects");
            }
        }
        if (mReferrer != null) {
            return new Uri.Builder().scheme("android-app").authority(mReferrer).build();
        }
        return null;
    }

接下来mActivityMonitors是什么?

        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()) {
                        if (options == null) {
                            options = ActivityOptions.makeBasic().toBundle();
                        }
                        result = am.onStartActivity(who, intent, options);
                    }
                    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;
                    }
                }
            }
        }

mActivityMonitors是一个列表,它的元素是ActivityMonitor,

    /**
     * Add a new {@link ActivityMonitor} that will be checked whenever an 
     * activity is started.  The monitor is added 
     * after any existing ones; the monitor will be hit only if none of the 
     * existing monitors can themselves handle the Intent. 
     *  
     * @param monitor The new ActivityMonitor to see. 
     *  
     * @see #addMonitor(IntentFilter, ActivityResult, boolean) 
     * @see #checkMonitorHit 
     */
    public void addMonitor(ActivityMonitor monitor) {
        synchronized (mSync) {
            if (mActivityMonitors == null) {
                mActivityMonitors = new ArrayList();
            }
            mActivityMonitors.add(monitor);
        }
    }

通过addMonitor可以添加ActivityMonitor,通过Instrumentation#waitForMonitor等待目标Activity启动完成,这应该也是用在测试代码中。

    /**
     * Wait for an existing {@link ActivityMonitor} to be hit.  Once the 
     * monitor has been hit, it is removed from the activity monitor list and 
     * the first created Activity object that matched it is returned.
     *  
     * @param monitor The ActivityMonitor to wait for.
     * 
     * @return The Activity object that matched the monitor.
     */
    public Activity waitForMonitor(ActivityMonitor monitor) {
        Activity activity = monitor.waitForActivity();
        synchronized (mSync) {
            mActivityMonitors.remove(monitor);
        }
        return activity;
    }
        /**
         * Block until an Activity is created that matches this monitor, 
         * returning the resulting activity. 
         * 
         * @return Activity
         */
        public final Activity waitForActivity() {
            synchronized (this) {
                while (mLastActivity == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
                Activity res = mLastActivity;
                mLastActivity = null;
                return res;
            }
        }
        final boolean match(Context who,
                            Activity activity,
                            Intent intent) {
            if (mIgnoreMatchingSpecificIntents) {
                return false;
            }
            synchronized (this) {
                if (mWhich != null
                    && mWhich.match(who.getContentResolver(), intent,
                                    true, "Instrumentation") < 0) {
                    return false;
                }
                if (mClass != null) {
                    String cls = null;
                    if (activity != null) {
                        cls = activity.getClass().getName();
                    } else if (intent.getComponent() != null) {
                        cls = intent.getComponent().getClassName();
                    }
                    if (cls == null || !mClass.equals(cls)) {
                        return false;
                    }
                }
                if (activity != null) {
                    mLastActivity = activity;
                    notifyAll();
                }
                return true;
            }
        }

match方法又是在execStartActivity中调用的,咱们回到execStartActivity分析中。

int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getOpPackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                    target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);

接下来跨进程调到ActivityTaskManager中了,其实调用了ActivityTaskManagerService#startActivityAsUser。

        // TODO: Switch to user app stacks here.
        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setCallingFeatureId(callingFeatureId)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(opts)
                .setUserId(userId)
                .execute();

getActivityStartController返回ActivityStartController的实例

ActivityStartController.java
    /**
     * @return A starter to configure and execute starting an activity. It is valid until after
     *         {@link ActivityStarter#execute} is invoked. At that point, the starter should be
     *         considered invalid and no longer modified or used.
     */
    ActivityStarter obtainStarter(Intent intent, String reason) {
        return mFactory.obtain().setIntent(intent).setReason(reason);
    }

obtainStarter返回ActivityStarter,mFactory是ActivityStarter#DefaultFactory的实例

        @Override
        public ActivityStarter obtain() {
            ActivityStarter starter = mStarterPool.acquire();

            if (starter == null) {
                if (mService.mRootWindowContainer == null) {
                    throw new IllegalStateException("Too early to start activity.");
                }
                starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
            }

            return starter;
        }

obtain()方法会返回ActivityStarter对象,调用一系列set之后会执行它的execute()去启动Activity,在这之前咱们先看一下obtain(),这里有个mStarterPool,咱们稍微研究下。
mStarterPool是SynchronizedPool的实例,SynchronizedPool是线程安全的对象池。

4. Activity栈是什么

参考:

  1. StartActivity路上的mParent
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容