原生桌面的Activity聲明方式
<activity
android:name="com.android.launcher3.Launcher"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
</activity>
因此,普通應(yīng)用設(shè)置Launcher應(yīng)用的方式
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
設(shè)置中設(shè)置默認桌面邏輯
path:設(shè)置-應(yīng)用和通知-默認應(yīng)用-主屏幕瑩瑩

點擊后調(diào)用如下代碼:
Settings中的源碼DefaultHomePicker.java
//key傳遞的是包名+activity名稱::com.android.launcher3/.Launcher m=0x108000}
@Override
protected boolean setDefaultKey(String key) {
if (!TextUtils.isEmpty(key)) {
final ComponentName component = ComponentName.unflattenFromString(key);//根據(jù)key,轉(zhuǎn)化得到ComponentName
final List<ResolveInfo> homeActivities = new ArrayList<>();
mPm.getHomeActivities(homeActivities);//獲取系統(tǒng)中所有的桌面屬性ResolveInfo
final List<ComponentName> allComponents = new ArrayList<>();//獲取系統(tǒng)中所有的桌面屬性ComponentName
for (ResolveInfo info : homeActivities) {
final ActivityInfo appInfo = info.activityInfo;
ComponentName activityName = new ComponentName(appInfo.packageName, appInfo.name);
allComponents.add(activityName);
}
mPm.replacePreferredActivity(
DefaultHomePreferenceController.HOME_FILTER,
IntentFilter.MATCH_CATEGORY_EMPTY,
allComponents.toArray(new ComponentName[0]),//ComponentName數(shù)組,內(nèi)容同allComponents
component);//需要設(shè)置默認桌面的目標component
// Launch the new Home app so the change is immediately visible even if
// the Home button is not pressed.
//通過Intent的方式,啟動home應(yīng)用,因為replacePreferredActivity已經(jīng)制定了系統(tǒng)中默認桌面應(yīng)用,
//因此不會再彈框讓用戶去選擇默認Home應(yīng)用
final Context context = getContext();
Intent i = new Intent(Intent.ACTION_MAIN);
i.addCategory(Intent.CATEGORY_HOME);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
return true;
}
return false;
}
以上代碼中,核心邏輯是mPm.replacePreferredActivity,四個參數(shù)分別為:
- DefaultHomePreferenceController.HOME_FILTER
HOME_FILTER = new IntentFilter(Intent.ACTION_MAIN);
HOME_FILTER.addCategory(Intent.CATEGORY_HOME);
HOME_FILTER.addCategory(Intent.CATEGORY_DEFAULT);
- IntentFilter.MATCH_CATEGORY_EMPTY
The filter matched an intent that had no data specified. - allComponents.toArray(new ComponentName[0])
當前系統(tǒng)中,所有的Home Activity的Components - component
當前要設(shè)置為默認桌面的引用的主Activity
根據(jù)以上四個參數(shù),我們看下在PMS的具體處理邏輯
以上代碼通過binder,調(diào)用到PMS中去
PMS
@Override
public void replacePreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId) {
if (filter.countActions() != 1) {//只有一個Intent.ACTION_MAIN,滿足條件
throw new IllegalArgumentException(
"replacePreferredActivity expects filter to have only 1 action.");
}
//如上,filter沒有設(shè)置data數(shù)據(jù)
if (filter.countDataAuthorities() != 0
|| filter.countDataPaths() != 0
|| filter.countDataSchemes() > 1
|| filter.countDataTypes() != 0) {
throw new IllegalArgumentException(
"replacePreferredActivity expects filter to have no data authorities, " +
"paths, or types; and at most one scheme.");
}
final int callingUid = Binder.getCallingUid();
//如果是跨用戶調(diào)用,則要求調(diào)用者必須是系統(tǒng)應(yīng)用或者具有INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL權(quán)限
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"replace preferred activity");
synchronized (mPackages) {
if (mContext.checkCallingOrSelfPermission(
Manifest.permission.SET_PREFERRED_APPLICATIONS)//SET_PREFERRED_APPLICATIONS權(quán)限,9.x版本已經(jīng)deprecated
!= PackageManager.PERMISSION_GRANTED) {
if (getUidTargetSdkVersionLockedLPr(callingUid)
< Build.VERSION_CODES.FROYO) {//Android 2.2 以前的版本不能設(shè)置默認桌面
Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
+ Binder.getCallingUid());
return;
}
mContext.enforceCallingOrSelfPermission(
Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
}
//PreferredIntentResolver的數(shù)據(jù)結(jié)構(gòu),見后面詳細解釋
PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
if (pir != null) {
// Get all of the existing entries that exactly match this filter.
//根據(jù)傳遞的filter條件,查找系統(tǒng)中所有符合條件的PreferredActivity
ArrayList<PreferredActivity> existing = pir.findFilters(filter);
if (existing != null && existing.size() == 1) {
PreferredActivity cur = existing.get(0);
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Checking replace of preferred:");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
if (!cur.mPref.mAlways) {
Slog.i(TAG, " -- CUR; not mAlways!");
} else {
Slog.i(TAG, " -- CUR: mMatch=" + cur.mPref.mMatch);
Slog.i(TAG, " -- CUR: mSet="
+ Arrays.toString(cur.mPref.mSetComponents));
Slog.i(TAG, " -- CUR: mComponent=" + cur.mPref.mShortComponent);
Slog.i(TAG, " -- NEW: mMatch="
+ (match&IntentFilter.MATCH_CATEGORY_MASK));
Slog.i(TAG, " -- CUR: mSet=" + Arrays.toString(set));
Slog.i(TAG, " -- CUR: mComponent=" + activity.flattenToShortString());
}
}
//驗證從系統(tǒng)中查找的組件,跟要設(shè)置的組件是否一致
if (cur.mPref.mAlways && cur.mPref.mComponent.equals(activity)
&& cur.mPref.mMatch == (match&IntentFilter.MATCH_CATEGORY_MASK)
&& cur.mPref.sameSet(set)) {
// Setting the preferred activity to what it happens to be already
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Replacing with same preferred activity "
+ cur.mPref.mShortComponent + " for user "
+ userId + ":");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
}
return;
}
}
//存在多個符合符合條件的組件的情況下,則將原有的組件在數(shù)據(jù)結(jié)構(gòu)中刪除,并用傳遞的activity設(shè)置唯一與filter匹配的組件
if (existing != null) {
if (DEBUG_PREFERRED) {
Slog.i(TAG, existing.size() + " existing preferred matches for:");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
}
for (int i = 0; i < existing.size(); i++) {
PreferredActivity pa = existing.get(i);
if (DEBUG_PREFERRED) {
Slog.i(TAG, "Removing existing preferred activity "
+ pa.mPref.mComponent + ":");
pa.dump(new LogPrinter(Log.INFO, TAG), " ");
}
pir.removeFilter(pa);
}
}
}
//主要邏輯是根據(jù)傳遞的參數(shù),創(chuàng)建PreferredActivity對象,添加到PreferredIntentResolver中去
addPreferredActivityInternal(filter, match, set, activity, true, userId,
"Replacing preferred");
}
}
該方法的主要邏輯,總結(jié)如下
檢查傳遞的條件的有效性,傳遞的filter參數(shù)只能含有一個Action值,不能含有Authorities,DataPaths,
countDataTypes數(shù)據(jù)DataSchemes的數(shù)據(jù)不能大于1如果是跨用戶調(diào)用,則驗證跨用戶調(diào)用權(quán)限
根據(jù)userid,獲取包含當前用戶所有PreferredActivity數(shù)據(jù)的PreferredIntentResolver對象。然后根據(jù)傳遞的filter條件,
獲取所有符合條件的PreferredActivity數(shù)組如果獲取的PreferredActivity組件長度為1,并且該PreferredActivity已經(jīng)被設(shè)置為改filter默認的組件,則刪除原來的,用新的activity組件替換
如果獲取的PreferredActivity的組件長度不是1,則刪除該組件數(shù)組的所有數(shù)據(jù),并根據(jù)傳遞的參數(shù),創(chuàng)建PreferredActivity對象,添加到PreferredIntentResolver中去
該方法中主要用的類和數(shù)據(jù)結(jié)構(gòu)為:
Settings,PreferredIntentResolver,PreferredActivity,其類圖和數(shù)據(jù)圖如下:
PreferredActivity類圖依賴關(guān)系

PreferredActivity數(shù)據(jù)結(jié)構(gòu)圖

Settings含有多個PreferredIntentResolver,每一個PreferredIntentResolver跟userid對應(yīng)
PreferredIntentResolver含有當前用戶的每個Activity的信息信息,信息存儲在PreferredActivity(extends PreferredComponent)
PreferredComponent存在指定的Component信息,已經(jīng)跟改Component同屬同一個filter的其他Component信息,比較典型的是,系統(tǒng)中可以存在多個桌面Component
多個Component存儲在PreferredComponent的以下數(shù)據(jù)結(jié)構(gòu)中
final String[] mSetPackages;
final String[] mSetClasses;
final String[] mSetComponents;
以上,就是Android 系統(tǒng)設(shè)置默認桌面的流程
擴展
以上過程主要涉及了PMS和Settings的PreferredIntentResolver數(shù)據(jù)結(jié)構(gòu),另一個涉及多個PreferredActivity的場景是隱式啟動Activity
隱式啟動一個Activity的過程
隱式拉起的方式,以相機為例:
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivityForResult(intent, 0);
啟動的掛機關(guān)鍵流程為

總結(jié)如下:
- 客戶端通過StartActivity啟動對應(yīng)的Intent,通過binder調(diào)用到AMS
- pms通過傳遞的Intent值,通過resolveIntent-->chooseBestActivity-->findPreferredActivity去解析獲取ResolveInfo
如果Intent值是顯式聲明,則返回唯一的Activity;
如果Intent是隱式聲明的,則去Settings的PreferredIntentResolver數(shù)據(jù)結(jié)構(gòu)中查詢該Intent對應(yīng)的component是否有默認值,如果.
有則返回;如果沒有,則返回使用ResolverActivity去創(chuàng)建一個ResolveInfo對象
- AMS根據(jù)返回的ResolveInfo去啟動對應(yīng)的Activity,如果ResolveInfo是ResolverActivity類型,則彈出選擇框,讓用戶選擇是用調(diào)用哪個類的組件