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方法,所以刚才的分析是错误的,这里也要分两种情况:
- 从Activity启动Activity;
- 从非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方法,神奇的事情就这样发生了,两种情况交汇了。
现在又有三个问题:
- mParent是什么?
- ActivityTaskManager是什么?
- 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栈是什么
参考: