问题
最近在使用我们的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文件,推荐使用方法二。