Quickly modify the configChanges attribute in AndroidManifest

problem

Recently, a problem was discovered when using our app. When the app is opened to use the system's split screen function, the page will reload, resulting in a bad experience. By consulting the Android documentation , the reason is that when a configuration change occurs at runtime, the Activity will be closed and restarted by default. You can declare the configuration in the configChanges property to prevent Activity from restarting.

The following is taken from the documentation:

value description
density The display density has changed — the user may have specified a different display scale, or a different display may now be active. This item is a new configuration in API level 24 .
fontScale The font scaling factor has changed — the user has selected a new global font size.
keyboard The keyboard type has changed — for example, the user plugs in an external keyboard.
keyboardHidden Keyboard accessibility changes—for example, the user displays a hard keyboard.
layoutDirection The layout direction has changed—for example, from left to right (LTR) to right to left (RTL). This item is a new configuration in API level 17 .
locale The locale has changed-the user has selected a new display language for the text.
mcc IMSI mobile device country/region code (MCC) has changed — SIM is detected and MCC is updated.
mnc IMSI Mobile Device Network Code (MNC) has changed-SIM is detected and MNC is updated.
navigation The navigation type (trackball/direction keys) has changed. (This situation usually does not happen.)
orientation The screen orientation changes-the user rotates the device. ** Note: ** If the application for Android 3.2 (13 API level) or higher version of the system, the statement should be "screenSize"arranged, because when the device is switched between horizontal and vertical, the configuration will also change.
screenLayout The screen layout has changed-different displays may now be active.
screenSize The currently available screen size has changed. This value represents the change of the current available size relative to the current aspect ratio, and it will change when the user switches between landscape and portrait. This item is a new configuration in API level 13 .
smallestScreenSize The physical screen size has changed. This value represents a size change that is independent of orientation, so it will only change when the actual physical screen size changes (such as switching to an external display). Changes made to this configuration correspond to changes in the smallestWidth configuration . This item is a new configuration in API level 13 .
touchscreen The touch screen has changed. (This situation usually does not happen.)
uiMode The interface mode has changed — the user has placed the device on the desktop or car dock, or the night mode has changed. To learn more about the different interface modes, see UiModeManager. This item is a new configuration in API level 8 .

All these configuration changes may affect the resource values ​​seen by the application. Therefore, calling onConfigurationChanged(), it is usually necessary to retrieve all the resources (including view layout, you can draw objects, etc.), in order to correctly handle the change again.

** Note: ** If you want to handle all multi-window mode related to configuration changes, use "screenLayout"and "smallestScreenSize". Android 7.0 (API level 24) or higher versions support multi-window mode.

Solution:

Method 1: Use python script
# -*- coding: utf-8 -*-
import os
from xml.dom.minidom import parse

def parse_manifest(file_path):
    if not os.path.exists(file_path):
        print("文件不存在:{}".format(file_path))
        return
    print("文件路径:" + file_path)
    file = open(path, 'r', encoding='utf-8')
    dom_tree = parse(file)
    activity_elements = dom_tree.documentElement.getElementsByTagName("activity")
    for activity in activity_elements:
        if activity.hasAttribute("android:configChanges"):
            old_attribute = activity.getAttribute("android:configChanges")
            if old_attribute.find("screenLayout") == -1:
                print("修改前:{}".format(activity.getAttribute("android:configChanges")))
                activity.setAttribute("android:configChanges",
                                      "{0}|screenLayout|smallestScreenSize".format(old_attribute))
                print("修改后:{}".format(activity.getAttribute("android:configChanges")))
                print("----------")
        else:
            activity.setAttribute("android:configChanges", "screenLayout|smallestScreenSize")
            print("修改后:{}".format(activity.getAttribute("android:configChanges")))
            print("----------")
    try:
        with open(file_path, 'w', encoding='utf-8') as f:
            dom_tree.writexml(f, indent='', addindent='', newl='', encoding='utf-8')
            print("写入成功")
    except Exception as e:
        print(e)


if __name__ == "__main__":
    path = input("请输入AndroidManifest文件路径:")
    parse_manifest(path)
Method 2: Use gradle script
project.afterEvaluate {
    
    
    def variants = null
    try {
    
    
        variants = android.applicationVariants
    } catch (Throwable t) {
    
    
        t.printStackTrace()
        try {
    
    
            variants = android.libraryVariants
        } catch (Throwable tt) {
    
    
            tt.printStackTrace()
        }
    }
    if (variants != null) {
    
    
        variants.all {
    
     variant ->
            variant.outputs.each {
    
     output ->
                def task = output.processManifestProvider.get()
                if (task == null) {
    
    
                    return
                }
                task.doLast {
    
    
                  	//AGP 4.1.0 
                    def manifestFile = new File(multiApkManifestOutputDirectory.get().asFile, "AndroidManifest.xml")
                    if (manifestFile == null || !manifestFile.exists()) {
    
    
                        return
                    }
                    def parser = new XmlSlurper(false, true)
                    def manifest = parser.parse(manifestFile)
                    def app = manifest.'application'[0]
                    app.'activity'.each {
    
     act ->
                        String value = act.attributes()['android:configChanges']
                        if (value == null || value.isEmpty()) {
    
    
                            value = "keyboardHidden|orientation|screenSize|screenLayout"
                            act.attributes()['androidconfigChanges'] = value
                        } else {
    
    
                            String[] valueSplit = value.split("\\|")
                            if (!valueSplit.contains("keyboardHidden")) {
    
    
                                value += "|keyboardHidden"
                            }
                            if (!valueSplit.contains("orientation")) {
    
    
                                value += "|orientation"
                            }
                            if (!valueSplit.contains("screenSize")) {
    
    
                                value += "|screenSize"
                            }
                            if (!valueSplit.contains("screenLayout")) {
    
    
                                value += "|screenLayout"
                            }
                            act.attributes()['android:configChanges'] = value
                        }
                    }

                    def tmpManifest = XmlUtil.serialize(manifest).replaceAll("androidconfigChanges", "android:configChanges")
                    manifest = parser.parseText(tmpManifest)
                    manifestFile.setText(XmlUtil.serialize(manifest), "utf-8")
                }
            }
        }
    }
}

Method one requires the computer to have a python environment installed, and executing the script will directly modify the manifest file. Method two is dynamically modified globally during the process of compiling and packaging the apk, which will not affect the manifest file. Method two is recommended.

Guess you like

Origin blog.csdn.net/ZYJWR/article/details/110489668
Recommended