在Android启动过程中,系统中如果有多个Launcher时,会弹出一个对话框让我们选择哪个应用作为Launcher。所以我们来分析一下Launcher的启动过程。
Launcher的启动过程首先是从ActivityManagerService的startHomeActivityLocked方法开始。所以先来看该方法。
//Android6.0
boolean startHomeActivityLocked(int userId, String reason) {
//工厂测试模式
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
// the factory test app, so just sit around displaying the
// error message and don't try to start anything.
return false;
}
//获取Home Intent
Intent intent = getHomeIntent();
//获取Activity信息
ActivityInfo aInfo =
resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
//根据userId获取应用信息
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
//获取应用进程,每个android进程,在AMS中对应有一个ProcessRecord
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instrumentationClass == null) {
//如果进程不存在,就要在启动时为该应用新建一个堆栈(FLAG_ACTIVITY_NEW_TASK)
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
//启动Home Activity(Launcher)
mStackSupervisor.startHomeActivity(intent, aInfo, reason);
}
}
return true;
}
接下来看ActivityStackSupervisor类中的startHomeActivity方法
//Android6.0
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
//将Home Activity移动到栈顶
moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
//启动Home Activity
startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo,
null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */,
null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */,
null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */,
0 /* startFlags */, null /* options */, false /* ignoreTargetSecurity */,
false /* componentSpecified */,
null /* outActivity */, null /* container */, null /* inTask */);
if (inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
// again. We need to schedule another resume.
scheduleResumeTopActivities();
}
}
上面的过程如果只有一个Launcher时,会直接进入Launcher,但是如果有多个Launcher,会弹出一个Activity来让我们选择,这个Activity为ResolverActivity。
先看一下这个Activity的介绍:
/**
* This activity is displayed when the system attempts to start an Intent for
* which there is more than one matching activity, allowing the user to decide
* which to go to. It is not normally used directly by application developers.
*/
上面的意思就是当Intent要启动(隐式)的Activity时,有多个Activity匹配这个Intent,就会显示这个Activity,让用户选择要启动哪个。其实就是一个应用选择器,这个Activity不单单是有多个Launcher时才会显示,其它应用也会。
接下就来看它的onCreate方法
@Override
protected void onCreate(Bundle savedInstanceState) {
// Use a specialized prompt when we're handling the 'Home' app startActivity()
//当启动的是Launcher时会弹出一个专门的提示
final Intent intent = makeMyIntent();
final Set<String> categories = intent.getCategories();
if (Intent.ACTION_MAIN.equals(intent.getAction())
&& categories != null
&& categories.size() == 1
&& categories.contains(Intent.CATEGORY_HOME)) {
// Note: this field is not set to true in the compatibility version.
mResolvingHome = true;
}
setSafeForwardingMode(true);
onCreate(savedInstanceState, intent, null, 0, null, null, true);
}
最后一行又调用另一个onCreate方法,继续来看这个方法
protected void onCreate(Bundle savedInstanceState, Intent intent,
CharSequence title, int defaultTitleRes, Intent[] initialIntents,
List<ResolveInfo> rList, boolean alwaysUseOption) {
setTheme(R.style.Theme_DeviceDefault_Resolver);
super.onCreate(savedInstanceState);
// Determine whether we should show that intent is forwarded
// from managed profile to owner or other way around.
setProfileSwitchMessageId(intent.getContentUserHint());
try {
//获取UID,Android支持多用户,不同用户可能Launcher不同
mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
getActivityToken());
} catch (RemoteException e) {
mLaunchedFromUid = -1;
}
if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
// Gulp!
finish();
return;
}
//获取PackageManager
mPm = getPackageManager();
mPackageMonitor.register(this, getMainLooper(), false);
mRegistered = true;
//获取ActivityManager
final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
//获取Launcher Icon的大小
mIconDpi = am.getLauncherLargeIconDensity();
// Add our initial intent as the first item, regardless of what else has already been added.
mIntents.add(0, new Intent(intent));
//获取推荐的包名
final String referrerPackage = getReferrerPackageName();
mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
//设置布局,类似于setContentView
if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {
return;
}
// Prevent the Resolver window from becoming the top fullscreen window and thus from taking
// control of the system bars.
//防止选择窗口变成全屏
getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);
//接下来就是初始化界面,设置监听器
final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);
if (rdl != null) {
rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
@Override
public void onDismissed() {
finish();
}
});
if (isVoiceInteraction()) {
rdl.setCollapsed(false);
}
mResolverDrawerLayout = rdl;
}
if (title == null) {
title = getTitleForAction(intent.getAction(), defaultTitleRes);
}
if (!TextUtils.isEmpty(title)) {
final TextView titleView = (TextView) findViewById(R.id.title);
if (titleView != null) {
titleView.setText(title);
}
setTitle(title);
// Try to initialize the title icon if we have a view for it and a title to match
final ImageView titleIcon = (ImageView) findViewById(R.id.title_icon);
if (titleIcon != null) {
ApplicationInfo ai = null;
try {
if (!TextUtils.isEmpty(referrerPackage)) {
ai = mPm.getApplicationInfo(referrerPackage, 0);
}
} catch (NameNotFoundException e) {
Log.e(TAG, "Could not find referrer package " + referrerPackage);
}
if (ai != null) {
titleIcon.setImageDrawable(ai.loadIcon(mPm));
}
}
}
final ImageView iconView = (ImageView) findViewById(R.id.icon);
final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem();
if (iconView != null && iconInfo != null) {
new LoadIconIntoViewTask(iconInfo, iconView).execute();
}
if (alwaysUseOption || mAdapter.hasFilteredItem()) {
final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
if (buttonLayout != null) {
buttonLayout.setVisibility(View.VISIBLE);
//Always按钮
mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
//Once按钮
mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
} else {
mAlwaysUseOption = false;
}
}
if (mAdapter.hasFilteredItem()) {
setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
mOnceButton.setEnabled(true);
}
mProfileView = findViewById(R.id.profile_button);
if (mProfileView != null) {
mProfileView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final DisplayResolveInfo dri = mAdapter.getOtherProfile();
if (dri == null) {
return;
}
// Do not show the profile switch message anymore.
mProfileSwitchMessageId = -1;
onTargetSelected(dri, false);
finish();
}
});
bindProfileView();
}
if (isVoiceInteraction()) {
onSetupVoiceInteraction();
}
getWindow().getDecorView().addOnAttachStateChangeListener(
new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
v.getViewRootImpl().setDrawDuringWindowsAnimating(true);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
}
上面其实主要就是在初始化弹出的选择界面,设置监听器之类的。
接下来来看Always和once按钮的监听器
public void onButtonClick(View v) {
//获取Button id
final int id = v.getId();
startSelected(mAlwaysUseOption ?
mAdapterView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
id == R.id.button_always,
mAlwaysUseOption);
}
上面的监听器是在xml中设置onclick属性,这个方法就是调用了startSelected方法,用来启动所选的Launcher。
void startSelected(int which, boolean always, boolean filtered) {
if (isFinishing()) {
return;
}
//ResolveInfo是Activity信息的集合
ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
Toast.makeText(this, String.format(getResources().getString(
com.android.internal.R.string.activity_resolver_work_profiles_support),
ri.activityInfo.loadLabel(getPackageManager()).toString()),
Toast.LENGTH_LONG).show();
return;
}
//获取要启动的Activity(Launcher)
//TargetInfo包含了Activity的信息
TargetInfo target = mAdapter.targetInfoForPosition(which, filtered);
if (onTargetSelected(target, always)) {
finish();
}
}
上面最关键的就是最后几行了,调用onTargetSelected方法,然后结束ResolverActivity。
protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
//获取Activity信息
final ResolveInfo ri = target.getResolveInfo();
final Intent intent = target != null ? target.getResolvedIntent() : null;
........
if (filter != null) {
final int N = mAdapter.mOrigResolveList.size();
ComponentName[] set = new ComponentName[N];
int bestMatch = 0;
for (int i=0; i<N; i++) {
ResolveInfo r = mAdapter.mOrigResolveList.get(i).getResolveInfoAt(0);
set[i] = new ComponentName(r.activityInfo.packageName,
r.activityInfo.name);
if (r.match > bestMatch) bestMatch = r.match;
}
//always被选中
if (alwaysCheck) {
final int userId = getUserId();
final PackageManager pm = getPackageManager();
// Set the preferred Activity
//设置首选的Activity
pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
//省略
........
} else {
//省略
........
}
}
}
if (target != null) {
safelyStartActivity(target);
}
return true;
}
上面最重要的就是pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());这行代码,它将某个Launcher设置默认Launcher,这样以后启动后就优先选择这个。
所以假如我们要在Settings中加一个选项来设置默认Launcher,那就是使用PackageManager中的addPreferredActivity方法即可。
Android中的偏好设置会被保存在用户的目录下,比如:data/system/users/0/package-restrictions.xml
上面贴了很多源码,其实我们在看源码时要站在宏观的角度去看,要看到它的一个大概流程不要去抠每一行代码的意思。只要到真正需要去修改某一个东西时,我们才需要去关注它具体的实现。




