Android 9.x 設(shè)置默認桌面流程

原生桌面的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)用-主屏幕瑩瑩

image

點擊后調(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é)如下

  1. 檢查傳遞的條件的有效性,傳遞的filter參數(shù)只能含有一個Action值,不能含有Authorities,DataPaths,
    countDataTypes數(shù)據(jù)DataSchemes的數(shù)據(jù)不能大于1

  2. 如果是跨用戶調(diào)用,則驗證跨用戶調(diào)用權(quán)限

  3. 根據(jù)userid,獲取包含當前用戶所有PreferredActivity數(shù)據(jù)的PreferredIntentResolver對象。然后根據(jù)傳遞的filter條件,
    獲取所有符合條件的PreferredActivity數(shù)組

  4. 如果獲取的PreferredActivity組件長度為1,并且該PreferredActivity已經(jīng)被設(shè)置為改filter默認的組件,則刪除原來的,用新的activity組件替換

  5. 如果獲取的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類圖依賴關(guān)系

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

PreferredActivity數(shù)據(jù)結(jié)構(gòu)圖
  1. Settings含有多個PreferredIntentResolver,每一個PreferredIntentResolver跟userid對應(yīng)

  2. PreferredIntentResolver含有當前用戶的每個Activity的信息信息,信息存儲在PreferredActivity(extends PreferredComponent)

  3. PreferredComponent存在指定的Component信息,已經(jīng)跟改Component同屬同一個filter的其他Component信息,比較典型的是,系統(tǒng)中可以存在多個桌面Component

  4. 多個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)鍵流程為


隱式拉起Activity

總結(jié)如下:

  1. 客戶端通過StartActivity啟動對應(yīng)的Intent,通過binder調(diào)用到AMS
  2. pms通過傳遞的Intent值,通過resolveIntent-->chooseBestActivity-->findPreferredActivity去解析獲取ResolveInfo
    如果Intent值是顯式聲明,則返回唯一的Activity;
    如果Intent是隱式聲明的,則去Settings的PreferredIntentResolver數(shù)據(jù)結(jié)構(gòu)中查詢該Intent對應(yīng)的component是否有默認值,如果.
    有則返回;如果沒有,則返回使用ResolverActivity去創(chuàng)建一個ResolveInfo對象
  1. AMS根據(jù)返回的ResolveInfo去啟動對應(yīng)的Activity,如果ResolveInfo是ResolverActivity類型,則彈出選擇框,讓用戶選擇是用調(diào)用哪個類的組件
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容