通过Launcher启动应用的过程分析
通过Lanucher启动 应用时会调用Lanucher的onClick方法。
Lanucher.java
/**
* 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();
/*
ShortcutInfo是Lanucher界面上的应用图标,封装了应用的信息吧
*/
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()));
/*
这一步开始去启动Activity,什么时候能返回呢,应该是调用传递给了系统服务后就可以了
*/
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);
}
}
}
Lanucher.java
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;
}
boolean startActivity(View v, Intent intent, Object tag) {
//设置FLAG的目的是保证每次都在新的Task中启动应用,每一个应用都有自己的task
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);
/*
该类用于检索当前用户可以启动的所有的Activity的列表以及用户可见的相关的配置文件,Lanucher主要使用该 类,可以通过配置文件查询所有的APP的信息,PackageManager不会为其他的配置文件发送广播,所以如果你想要监听这这些应用包的变化,可以注册监听此PackagerManager的广播Intent.ACTION_MANAGED_PROFILE_ADDED and Intent.ACTION_MANAGED_PROFILE_REMOVED.
*/
LauncherApps launcherApps = (LauncherApps)
this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
if (useLaunchAnimation) {
//Activity过度动画相关
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())) {
//Lanucher继承自Activity所以调用到父类Activity中
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;
}
startActivity会调用到Activity的如下代码
Activity.java
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
//Bundle中保存了之前启动得Activity的信息,如果之前没有Activity则信息为空
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);
}
}
launcherApps.startMainActivity, mService是LanucherAppsService.java
LanucherApps.java
/**
* Starts a Main activity in the specified profile.
*
* @param component The ComponentName of the activity to launch
* @param user The UserHandle of the profile
* @param sourceBounds The Rect containing the source bounds of the clicked icon
* @param opts Options to pass to startActivity
*/
public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
Bundle opts) {
logErrorForInvalidProfileAccess(user);
if (DEBUG) {
Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier());
}
try {
mService.startActivityAsUser(mContext.getPackageName(),
component, sourceBounds, opts, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
LancherAppsService.java
@Override
public void startActivityAsUser(String callingPackage,
ComponentName component, Rect sourceBounds,
Bundle opts, UserHandle user) throws RemoteException {
if (!canAccessProfile(callingPackage, user, "Cannot start activity")) {
return;
}
if (!isUserEnabled(user)) {
throw new IllegalStateException("Cannot start activity for disabled profile " + user);
}
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIntent.setSourceBounds(sourceBounds);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
launchIntent.setPackage(component.getPackageName());
final int callingUid = injectBinderCallingUid();
long ident = Binder.clearCallingIdentity();
try {
final PackageManagerInternal pmInt =
LocalServices.getService(PackageManagerInternal.class);
ActivityInfo info = pmInt.getActivityInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
callingUid, user.getIdentifier());
if (!info.exported) {
throw new SecurityException("Cannot launch non-exported components "
+ component);
}
// Check that the component actually has Intent.CATEGORY_LAUCNCHER
// as calling startActivityAsUser ignores the category and just
// resolves based on the component if present.
List<ResolveInfo> apps = pmInt.queryIntentActivities(launchIntent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
callingUid, user.getIdentifier());
final int size = apps.size();
/*
没搞清楚这个for循环在干嘛,for循环的意义在保证系统中存在相应的Activity信息?
*/
for (int i = 0; i < size; ++i) {
ActivityInfo activityInfo = apps.get(i).activityInfo;
if (activityInfo.packageName.equals(component.getPackageName()) &&
activityInfo.name.equals(component.getClassName())) {
// Found an activity with category launcher that matches
// this component so ok to launch.
launchIntent.setComponent(component);
mContext.startActivityAsUser(launchIntent, opts, user);
return;
}
}
throw new SecurityException("Attempt to launch activity without "
+ " category Intent.CATEGORY_LAUNCHER " + component);
} finally {
Binder.restoreCallingIdentity(ident);
}
}