美文网首页
Android 8 setting源码分析三

Android 8 setting源码分析三

作者: 梧叶已秋声 | 来源:发表于2019-12-21 14:44 被阅读0次

    以setting—>system—>Languages & input—>Languages 这个点击路径来说,图1依托于Settings图2依托于.Settings$xxxActivity,图三和图4是在hierarchy view中显示为SubSettings。(别的不一定,例如Display中并没有依托于SubSettings)。

    图1
    图2
    图3
    图4 image.png

    那么SubSettings和Settings以及Settings$xxxActivity的差别是什么?

    //packages\apps\Settings\src\com\android\settings\SubSettings.java
    /**
     * Stub class for showing sub-settings; we can't use the main Settings class
     * since for our app it is a special singleTask class.
     */
    public class SubSettings extends SettingsActivity {
    
        @Override
        public boolean onNavigateUp() {
            finish();
            return true;
        }
    
        @Override
        protected boolean isValidFragment(String fragmentName) {
            Log.d("SubSettings", "Launching fragment " + fragmentName);
            return true;
        }
    }
    
    /**
     * Top-level Settings activity
     */
    public class Settings extends SettingsActivity {
    .........
        public static class LanguageAndInputSettingsActivity extends SettingsActivity { /* empty */ }
    .........
        public static class SystemDashboardActivity extends SettingsActivity {}
    
    }
    

    从定义上来看,虽然Settings 和SubSettings 都是继承自SettingsActivity ,但是Settings中只是定义了一堆不重写函数的Settings$xxxActivity,SubSettings 中简单定义了几个函数,目前看不出差异。

    //AndroidManifest.xml
    ......
            <activity android:name=".Settings$SystemDashboardActivity"
                      android:label="@string/header_category_system"
                      android:icon="@drawable/ic_settings_about">
                <intent-filter android:priority="-1">
                    <action android:name="com.android.settings.action.SETTINGS"/>
                </intent-filter>
                <meta-data android:name="com.android.settings.category"
                           android:value="com.android.settings.category.ia.homepage"/>
                <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                           android:value="com.android.settings.system.SystemDashboardFragment"/>
                <meta-data android:name="com.android.settings.summary"
                           android:resource="@string/system_dashboard_summary"/>
            </activity>
    ......
            <activity android:name=".Settings$LanguageAndInputSettingsActivity"
                android:label="@string/language_settings"
                android:icon="@drawable/ic_settings_language"
                android:taskAffinity="com.android.settings"
                android:parentActivityName="Settings$SystemDashboardActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.VOICE_LAUNCH" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
                <intent-filter android:priority="260">
                    <action android:name="com.android.settings.action.SETTINGS"/>
                </intent-filter>
                <meta-data android:name="com.android.settings.category"
                           android:value="com.android.settings.category.ia.system"/>
                <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                           android:value="com.android.settings.language.LanguageAndInputSettings"/>
                <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
                           android:value="true"/>
            </activity>
    ......
    

    图三显示的是.Settings$LanguageAndInputSettingsActivity,LanguageAndInputSettingsActivity是由LanguageAndInputSettings这个fragment具体定义显示的内容,而LanguageAndInputSettings extends DashboardFragment,与此同时,上一级的SystemDashboardFragment也是继承自DashboardFragment,并且SystemDashboardActivity 的写法也是与LanguageAndInputSettingsActivity类似, 都是定义在Settings类中的,并且fragment的显示也都是通过SettingsActivity中的launchSettingFragment函数去显示。那么又是怎么跟SubSettings 扯上关系?为什么LanguageAndInputSettingsActivity在hierarchy view中显示的是SubSettings,而SystemDashboardActivity 是Settings$SystemDashboardActivity

    由于SettingsActivity中判断条件是
    final boolean isSubSettings = this instanceof SubSettings || intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
    通过打印log
    Log.d(TAG,"this instanceof SubSettings = " + String.valueOf(this instanceof SubSettings) + " , getBooleanExtra " + String.valueOf(intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false)));
    发现
    01-02 01:43:22.792 1155-1155/? D/SettingsActivity: this instanceof SubSettings = true , getBooleanExtra false
    判断是否为SubSettings关键在this instanceof SubSettings。那么为什么LanguageAndInputSettingsActivity在hierarchy view中显示是SubSettings?
    在点击system下的Languages & input 的时候,会调用SettingsActivity中的startPreferencePanel函数。

    //vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\SettingsActivity.java
        /**
         * Start a new fragment containing a preference panel.  If the preferences
         * are being displayed in multi-pane mode, the given fragment class will
         * be instantiated and placed in the appropriate pane.  If running in
         * single-pane mode, a new activity will be launched in which to show the
         * fragment.
         *
         * @param fragmentClass Full name of the class implementing the fragment.
         * @param args Any desired arguments to supply to the fragment.
         * @param titleRes Optional resource identifier of the title of this
         * fragment.
         * @param titleText Optional text of the title of this fragment.
         * @param resultTo Optional fragment that result data should be sent to.
         * If non-null, resultTo.onActivityResult() will be called when this
         * preference panel is done.  The launched panel must use
         * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
         * @param resultRequestCode If resultTo is non-null, this is the caller's
         * request code to be received with the result.
         */
        public void startPreferencePanel(Fragment caller, String fragmentClass, Bundle args,
                int titleRes, CharSequence titleText, Fragment resultTo, int resultRequestCode) {
            Log.d(TAG,"startPreferencePanel");
            String title = null;
            if (titleRes < 0) {
                if (titleText != null) {
                    title = titleText.toString();
                } else {
                    // There not much we can do in that case
                    title = "";
                }
            }
            Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
                    titleRes, title, mIsShortcut, mMetricsFeatureProvider.getMetricsCategory(caller));
        }
    

    而startWithFragment中又调用了onBuildStartFragmentIntent,这里构造了一个intent, intent.setClass(context, SubSettings.class),ComponentName的class为SubSettings,因此hierarchy view中显示SubSettings,而没有显示真正的activity的名字。至于这样做的原因,由于本人知识的局限性,目前没有想到。

    //vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\Utils.java
    ......
        public static void startWithFragment(Context context, String fragmentName, Bundle args,
                Fragment resultTo, int resultRequestCode, int titleResId,
                CharSequence title, boolean isShortcut, int metricsCategory) {
            Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
                    null /* titleResPackageName */, titleResId, title, isShortcut, metricsCategory);
            if (resultTo == null) {
                context.startActivity(intent);
            } else {
                resultTo.getActivity().startActivityForResult(intent, resultRequestCode);
            }
        }
    ........
        public static Intent onBuildStartFragmentIntent(Context context, String fragmentName,
                Bundle args, String titleResPackageName, int titleResId, CharSequence title,
                boolean isShortcut, int sourceMetricsCategory) {
            Log.d(TAG,"onBuildStartFragmentIntent");
            Intent intent = new Intent(Intent.ACTION_MAIN);
            intent.setClass(context, SubSettings.class);
            intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
            intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
            intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
                    titleResPackageName);
            intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
            intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
            intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
            intent.putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY, sourceMetricsCategory);
            return intent;
        }
    
    .............
    

    到了这里,基本上捋清楚了显示流程。

    总结就是:通过SettingsActivity中的switchToFragment,去显示所有的界面,其他的这些定义的activity只是一个用来定义fragment的壳子。

    另外,使用中可能需要通过intent跳转到setting中的界面。如
    startActivity(new Intent(Settings.ACTION_SETTINGS));
    根据官方文档
    https://developer.android.com/reference/android/provider/Settings.html#ACTION_APP_NOTIFICATION_SETTINGS可知
    Settings.ACTION_SETTINGS ,Constant Value: "android.settings.SETTINGS"即下面AndroidManifest.xml中的action 标签中的所定义的name。

            <activity android:name="Settings"
                    android:taskAffinity="com.android.settings"
                    android:label="@string/settings_label_launcher"
                    android:launchMode="singleTask">
                <intent-filter android:priority="1">
                    <action android:name="android.settings.SETTINGS" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
                <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
                    android:value="true" />
            </activity>
    .........................
            <activity android:name="Settings$AppNotificationSettingsActivity"
                    android:exported="true">
                <intent-filter android:priority="1">
                    <action android:name="android.settings.APP_NOTIFICATION_SETTINGS" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
                <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                    android:value="com.android.settings.notification.AppNotificationSettings" />
            </activity>
    

    因此,如果新增的activity,只要定义新的action然后使用这个string去新建intent,最后通过startActivity(intent)跳转。

    参考链接:
    关于Android Settings中的八个问题
    Android跳转各种系统设置界面-总结

    相关文章

      网友评论

          本文标题:Android 8 setting源码分析三

          本文链接:https://www.haomeiwen.com/subject/tlgwnctx.html