抛出问题:
1. Android系统桌面是什么
2. 点击应用图标后Android系统执行了什么操作
用文字总结App启动流程可以分为以下步骤:
1. Launcher通过Binder建立Launcher所在进程与system_server进程(ActivityManagerService所在进程)的通信,通知ActivityManagerService即将要启动一个Activity
2. ActivityManagerService通过Binder让Launcher进入pause状态
3. Launcher进入pause状态后,通过Binder告知ActivityManagerService,随后ActivityManagerService创建一个进程(将要打开的应用进程)并启动ActivityThread(应用的UI线程)
4. ActivityThread通过Binder将ApplicationThread类型的Binder对象传递给ActivityManagerService,方便ActivityManagerService后续与其的通信
5. 准备工作完成后,ActivityManagerService通知ActivityThread启动Activity
6. ActivityThread调度执行Activity的生命周期方法,完成启动Activity的工作
Activity是视图存在的根本,那么我们可以通过命令adb shell dumpsys activity activities
判断是哪个Activity为我们呈现桌面视图的
点击应用图标后Android系统执行了什么操作
呈现Android桌面视图(View)->点击View上某个应用图标->产生点击事件->点击事件被响应->通知Android系统的某个/某些进程->Android系统执行某些操作->启动App
Launcher如何响应由我们产生的点击事件
产生点击事件后,如果产生点击事件的View的Tag是ShortcutInfo(即启动应用的快捷方式),就会取得ShortcutInfo中保存的Intent(这个Intent指向我们要启动的App),然后执行startActivitySafely(v, intent, tag)
方法,而startActivitySafely方法只是对startActivity方法的简单封装。
所以,Launcher响应我们产生的点击事件后,实际上就是启动一个新的Activity。请看代码:
/**
* Launches the intent referred by the clicked shortcut.
*
* @param v The view representing the clicked shortcut.
*/
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;
}
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
// Open shortcut
final Intent intent = ((ShortcutInfo) tag).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()));
boolean success = startActivitySafely(v, intent, tag);
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) v;
handleFolderClick(fi);
}
} else if (v == mAllAppsButton) {
if (isAllAppsVisible()) {
showWorkspace(true);
} else {
onClickAllAppsButton(v);
}
}
}
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
try {
success = 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;
}
现在回想下App开发时,每个App都需要有一个“MainActivity”,这个Activity必须在AndroidManifest.xml文件中有以下配置:
在配置AndroidManifest.xml文件时,将Activity的Action指定为android.intent.action.MAIN
,会使Activity在一个新的Task中启动(Task是一个Activity栈)。将category指定为android.intent.category.LAUNCHER
,表示通过Intent启动此Activity时,只接受category为LAUNCHER的Intent。
所以,Launcher将会通过App的快捷方式(ShortcutInfo)得到应用的Intent,并通过这个Intent启动应用的“MainActivity”,从而启动应用。
所以我们研究的问题就从“App启动流程”变为“Activity启动流程”。
Launcher通过Binder通知ActivityManagerService启动Activity
,将Intent的Flag设为Intent.FLAG_ACTIVITY_NEW_TASK
,使得Android系统将创建一个新的Task来放置即将被打开的新Activity(应用的“MainActivity)。然后获取一个布尔值以用于后续判断是否显示启动App的动画。
然后获取Intent中是否传输了Parcelable格式的用户句柄,并通过Context.LAUNCHER_APPS_SERVICE
获取用于在多用户情境下启动App的系统服务。
不管是否显示启动App的动画,最终都会执行startActivity(intent)
或launcherApps.startMainActivity
方法以启动应用的“MainActivity”。
代码如下:boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
UserHandle user = (UserHandle) intent.getParcelableExtra(ApplicationInfo.EXTRA_PROFILE);
LauncherApps launcherApps = (LauncherApps)
this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
if (useLaunchAnimation) {
ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
v.getMeasuredWidth(), v.getMeasuredHeight());
if (user == null || user.equals(android.os.Process.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, opts.toBundle());
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(),
opts.toBundle());
}
} else {
if (user == null || user.equals(android.os.Process.myUserHandle())) {
startActivity(intent);
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(), null);
}
}
return true;
} catch (SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Launcher does not have the permission to launch " + intent +
". Make sure to create a MAIN intent-filter for the corresponding activity " +
"or use the exported attribute for this activity. "
+ "tag="+ tag + " intent=" + intent, e);
}
return false;
}
launcherApps.startMainActivity
只在用户句柄不为空且用户句柄不等于当前进程句柄(handle)时(其他用户的句柄)调用,
为什么用户句柄会影响Activity的启动方式?
我们需要取得用户A的句柄(和用户A相关的数据),将我们想启动的用户B的App的Intent、用户A的句柄交给内核,让拥有权限的
startActivity(intent)如何启动Activity
进入Activity类后层层深入就可以看到最终调用的是startActivityForResult
方法:
从代码上看,如果Launcher有mParent Activity,就会执行mParent.startActivityFromChild
;如果没有,就会执行mInstrumentation.execStartActivity
。进入mParent.startActivityFromChild
方法会看到最终也是执行了mInstrumentation.execStartActivity
。执行完成后,会取得一个ActivityResult对象,用于给调用者Activity传递一些数据,最后在Activity切换时显示Transition动画。
这里有一点需要指出的是:这里的ParentActivity指的是类似TabActivity、ActivityGroup关系的嵌套Activity。之所以要强调parent和child,是要避免混乱的Activity嵌套关系。代码如下:
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
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);
}
}
}
我们进入Instrumentation(管家婆类)类看看execStartActivity方法吧:
我们通过参数IBinder contextThread
取得一个IApplicationThread类型的对象whoThread,而contextThread是由mMainThread.getApplicationThread()
取得的ApplicationThread对象,此时mMainThread指的就是Launcher应用的主线程,所以whoThread指代的自然是Launcher的ApplicationThread。
因为Activity的onProvideReferrer()
方法默认返回null,除非该方法被重写,而我们使用的Launcher并没有重写该方法,所以不用管referrer。
然后判断是否有ActivityMonitor,如果有,则即将要打开的Activity是否和ActivityMonitor中保存的IntentFilter匹配,如果匹配则增加ActivityMonitor的计数。大致是用于监控符合匹配规则的Activity的数量的。
最后调用ActivityManagerNative.getDefault().startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
启动Activity,并检查启动是否成功。换句话说,最终负责启动Activity的是ActivityManager,前面得到的ApplicationThread也是在这里使用的。代码:
public ActivityResult execStartActivity(
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= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.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;
}