快捷修改AndroidManifest中configChanges属性

问题

最近在使用我们的app发现了一个问题,当打开app使用系统的分屏功能时页面会重新加载,导致体验不好。通过查阅Android文档,原因是在运行时发生配置变更时,默认情况下会关闭 Activity 并将其重启。可以在configChanges属性申明配置来防止Activity重启。

以下内容摘自文档:

描述
density 显示密度发生变更 — 用户可能已指定不同的显示比例,或者有不同的显示现处于活跃状态。此项为 API 级别 24 中的新增配置
fontScale 字体缩放系数发生变更 — 用户已选择新的全局字号。
keyboard 键盘类型发生变更 — 例如,用户插入外置键盘。
keyboardHidden 键盘无障碍功能发生变更 — 例如,用户显示硬键盘。
layoutDirection 布局方向发生变更 — 例如,自从左至右 (LTR) 更改为从右至左 (RTL)。此项为 API 级别 17 中的新增配置
locale 语言区域发生变更 — 用户已为文本选择新的显示语言。
mcc IMSI 移动设备国家/地区代码 (MCC) 发生变更 — 检测到 SIM 并更新 MCC。
mnc IMSI 移动设备网络代码 (MNC) 发生变更 — 检测到 SIM 并更新 MNC。
navigation 导航类型(轨迹球/方向键)发生变更。(这种情况通常不会发生。)
orientation 屏幕方向发生变更 — 用户旋转设备。**请注意:**如果应用面向 Android 3.2(API 级别 13)或更高版本的系统,则还应声明 "screenSize" 配置,因为当设备在横向与纵向之间切换时,该配置也会发生变更。
screenLayout 屏幕布局发生变更 — 不同的显示现可能处于活跃状态。
screenSize 当前可用屏幕尺寸发生变更。该值表示当前可用尺寸相对于当前纵横比的变更,当用户在横向与纵向之间切换时,它便会发生变更。此项为 API 级别 13 中的新增配置
smallestScreenSize 物理屏幕尺寸发生变更。该值表示与方向无关的尺寸变更,因此它只有在实际物理屏幕尺寸发生变更(如切换到外部显示器)时才会变化。对此配置所作变更对应 smallestWidth 配置的变化。此项为 API 级别 13 中的新增配置
touchscreen 触摸屏发生变更。(这种情况通常不会发生。)
uiMode 界面模式发生变更 — 用户已将设备置于桌面或车载基座,或者夜间模式发生变更。如需了解有关不同界面模式的更多信息,请参阅 UiModeManager此项为 API 级别 8 中的新增配置

所有这些配置变更都可能影响应用所看到的资源值。因此,调用 onConfigurationChanged() 时,通常有必要再次检索所有资源(包括视图布局、可绘制对象等),以正确处理变更。

**请注意:**如要处理所有多窗口模式相关的配置变更,请使用 "screenLayout""smallestScreenSize"。Android 7.0(API 级别 24)或更高版本的系统支持多窗口模式。

解决方法:

方法一:使用python脚本
# -*- 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)
方法二:使用gradle脚本
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")
                }
            }
        }
    }
}

方法一需要电脑安装有python环境,执行脚本会直接修改manifest文件。方法二在编译打包apk的过程中全局动态修改,不会影响到manifest文件,推荐使用方法二。

猜你喜欢

转载自blog.csdn.net/ZYJWR/article/details/110489668