前进之路 发表于 2024-12-20 11:05:23

android:configChanges分析

android:configChanges



[*]如果要在android源码确认是否是configChanges导致了Activity重启,发起把ActivityThread.DEBUG_CONFIGURATION改为true。
[*]Activity无法内部消化此次设置改变时,会调用relaunchActivityLocked方法。不同的是,如果此Activity正在前台,那立即调用;在背景的,则等切到前台后再调用

一、configChanges作用
Android程序在运行时,一些设备的设置可能会改变,如:横竖屏的切换、软键盘的弹出等。这些事件一旦发生,当前活动的Activity会重新启动,其中的过程是:在销毁之前会先调用onSaveInstanceState()方法去保存你应用中的一些数据,然后调用onDestroy()方法,最后调用onCreate()、onStart()、onResume()等方法启动一个新的Activity。
如果想让某些设置在发生改变的时间不重启Activity,需要为Activity添加android:configChanges属性,该属性可以设置多个值,用"|"隔开,例如:“locale|navigation|orientation。设置了android:configChanges属性后,当指定的属性发生变革时,不会去重新启动Activity,而是关照程序去调用Activity的onConfigurationChanged()方法。例如:在进行横竖屏的切换时,会重新启动Activity,而定义了android:configChanges=“orientation|keyboardHidden”,就不会重新启动Activity了,而是去调用onConfigurationChanged()方法。
简言之,在Activity中添加了android:configChanges属性,目的是当android:configChanges所设置的属性值对应的设置属性发生改变时,关照程序调用 onConfigurationChanged()函数,而不会重启Activity。

二、android:configChanges属性可以指定的值


[*]mcc(0x0001):国际移动用户识别码所属国家代号改变了-----sim被侦测到了,去更新mcc,mcc是移动用户所属国家代号
[*]mnc(0x0002):国际移动用户识别码的移动网号码改变了------sim被侦测到了,去更新mnc,MNC是移动网号码,最多由两位数字组成,用于识别移动用户所归属的移动通信网
[*]locale(0x0004):语言改变了-----用户选择了一个新的语言,一般和layoutDirection同时发生
[*]touchscreen(0x0008):触摸屏改变了------通常是不会发生的
[*]keyboard(0x0010):键盘发生了改变----例如用户用了外部的键盘
[*]keyboardHidden(0x0020):键盘的可用性发生了改变
[*]navigation(0x0040):导航发生了变革-----通常也不会发生
[*]orientation(0x0080):屏幕方向改变了
[*]screenLayout(0x0100):屏幕的显示发生了变革------不同的显示被激活
[*]uiMode(0x0200): 用户的模式发生了变革
[*]screenSize(0x0400):屏幕巨细改变了
[*]smallestScreenSize(0x0800):屏幕的物理巨细改变了,如:毗连到一个外部的屏幕上
[*]layoutDirection(0x2000):文字誊写朝向变革----一般和locale同时发生
[*]fontScale(0x40000000):字体比例发生了变革----选择了不同的全局字体

三、(android-12)frameworks处理处罚流程
<aosp>/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
------
final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {
    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
            boolean ignoreVisibility) {
        ......
        // changes类型是int,存储着此次发生的配置改变。举个例子:修改语言后,changes是0x2004。
        // shouldRelaunchLocked用于判断Activify是否可以内部消化掉changes指示的改变 
        if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
            // 重启分支。Activify无法消化此次改变,或强制要求重启(forceNewConfig==true)
            ....
            if (mState == PAUSING) {
                ...
                // 如果Activity此时为Pause状态,先设置一个标志位
                deferRelaunchUntilPaused = true;
                preserveWindowOnDeferredRelaunch = preserveWindow;
                return true;
            } else {
                ...
                relaunchActivityLocked(preserveWindow);
            }

            // All done...  tell the caller we weren't able to keep this activity around.
            return false;
        }
        ...

    }  
    shouldRelaunchLocked用于判断app是否可以内部消化掉changes指定的改变
     private boolean shouldRelaunchLocked(int changes, Configuration changesConfig) {
        int configChanged = info.getRealConfigChanged();
        ...
        // changes存储着此次发生的配置改变。
        // configChanged存储着此个Activity在android:configChanges写的可消化改变。
        return (changes&(~configChanged)) != 0;
    }
} 假设用户在“设置”修改了语言,像从英文改为中文,这时android会依次调用各个正运行着的Activity的ensureActivityConfiguration。一个ActivityRecord对象对应一个Activity,成员变量packageName存储着此Activity归属app的Application ID,像com.kos.launcher,成员函数ensureActivityConfiguration也就对应一个Activity的处理处罚过程。
在ensureActivityConfiguration,起首得到此次发生的设置改变值0x2004,值存储在变量changes,然后调用shouldRelaunchLocked,判断是否能内部消化掉这次改变。shouldRelaunchLocked逻辑分两步,第一步调用info.getRealConfigChanged()读出android:configChanges写的那改变值0x00C4,值存储在configChanged,第二步用“(changes&(~configChanged)) != 0”判断此次发生的改变(changes)是否有的不在configChanged。如果有,像示例的0x2004和0x00C4,shouldRelaunchLocked返回true,表示内部没法消化掉此次改变,否则返回false。
如果内部没法消化掉,或调用者要求一定要重启(forceNewConfig==true),那进入重启分支。在这分支,如果此app的生命周期正处于停息状态,像运行在背景,则把deferRelaunchUntilPaused设为true。否则立即调用relaunchActivityLocked。
<aosp>/frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java
------
    void completePause(boolean resumeNext, ActivityRecord resuming) {
        ...

        if (prev != null) {
            ...
            if (prev.finishing) {
                ...
            } else if (prev.hasProcess()) {
                ...
                if (prev.deferRelaunchUntilPaused) {
                    ...
                    // 这里是关键,如果deferRelaunchUntilPaused为true,调用重启
                    prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
                }
                ...
            }
            ...
        }
        ...
    } 当Activity从Pause到Resume状态过渡时,像从背景切到前台,一旦发现deferRelaunchUntilPaused是true,会调用relaunchActivityLocked。综上所述:Activity无法内部消化此次设置改变时,会调用relaunchActivityLocked方法。不同的是,如果此Activity正在前台,那立即调用;在背景的,则等切到前台后再调用。
relaunchActivityLocked实验着重启流程。内中如何转的就不分析了,终极会调用handleRelaunchActivityInner。
<aosp>/frameworks/base/core/java/android/app/ActivityThread.java
------
    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
        ...
        // 销毁此个Activity
        handleDestroyActivity(r, false, configChanges, true, reason);
        ...
        // 启动此个Activity
        handleLaunchActivity(r, pendingActions, customIntent);
    } 四、确保设置改变不会导致重启Activity
按上面规则,只要在android:configChanges写上所有可能的设置改变,那无论设置怎么改变都不会导致该Activity重启。那这里为什么还要提这个问题呢?——我不知道如何写android:configChanges才能包罗所有设置改变。
<aosp>/frameworks/base/core/java/android/content/pm/ActivityInfo.java
------
public static final int CONFIG_ASSETS_PATHS = 0x80000000;
public static final int CONFIG_WINDOW_CONFIGURATION = 0x20000000; 在Android-12,不知道如何写android:configChanges才能包罗以上这两个设置。
<aosp>/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
------
    private boolean shouldRelaunchLocked(int changes, Configuration changesConfig) {
        ...
        if (packageName != null && packageName.equals("com.kos.launcher")) {
            return false;
        }
        return (changes&(~configChanged)) != 0;
    } 如果android:configChanges无法包罗所有改变,但又要确保设置改变不会导致重启Activity,可以修改shouldRelaunchLocked。发现是白名单中的app时,逼迫返回false,即认为内部能消化掉,不必重启此Activity。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: android:configChanges分析