前言
进行公司车机设备开发时,由于屏幕比例比较特殊(1920x720),导致部分应用显示时左侧和底部有很大的黑边,使用dumpsys分析黑边的View,移除后发现仍存在,后确定为低版本默认比例问题,耗费了几个小时,记录下这个问题。
出现问题的条件:
- Android 应用:targetSdkVersion < 24 (Android N);
- 设备的分辨率宽高比大于1.86(1.86为Android低版本最大宽高比);
- Android 应用的AndroidManifest.xml内未设置android:resizeableActivity=”true”属性;
问题解决方案
问题根据不同的情况有不同的解决方式
- 若拥有应用源码,则可以直接将targetSdkVersion设置为大于等于24即可;
- 若拥有应用源码,但不能修改targetSdkVersion版本,那么就在 应用的AndroidManifest.xml内设置android:resizeableActivity=”true”属性;
- 若拥有应用源码,但不能修改targetSdkVersion版本,但compileSdkVersion=26时, 可以尝试在的AndroidManifest.xml内设置android:maxAspectRatio=”“=”宽高比”属性;
- 若没有应用源码,则需要尝试修改系统层来达到适配的作用,具体修改见如下代码:
系统层修改
/frameworks/base/core/java/android/content/pm/PackageParser.java PackageParser在解析APK的AndroidManifest.xml文件时,有如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class PackageParser {
....
private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
....
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
....
// 这里会去判断xml内是否有设置android:resizeableActivity属性
if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
if (sa.getBoolean(R.styleable.AndroidManifestApplication_resizeableActivity, true)) {
// 设置该标志位后,应用的Activity可调整大小
ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
} else {
// 设置该标志位后,应用的Activity不可调整大小
ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
}
} else if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N) {
// 若应用未设置android:resizeableActivity属性,且其targetSdkVersion >= Android N (24)
// 设置该标志位后,应用的Activity可调整大小
ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
- }
+ }else {
+ ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
+ }
}
}
根据以上代码可以看到,默认情况下没有对未设置android:resizeableActivity属性,且targetSdkVersion < 24的应用不会设置其Activity可调整大小,所以我们需要为这种情况加上对应的配置,即添加上述代码中的相应内容。修改后编译进设备,即可看到应用全屏显示了。
问题分析
除了上面分析到的没有Activity可调整大小标志位导致的无法全屏问题,还有一个是关于默认最大宽高比(DEFAULT_PRE_O_MAX_ASPECT_RATIO)的问题,通过修改该值也可以实现应用的全屏,不过有时候屏幕比例变化较大时该值可能要经常改动,有点麻烦。下面主要是从源码来分析下该值是如何起作用的。 /frameworks/base/core/java/android/content/pm/PackageParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class PackageParser {
....
private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
....
private void setMaxAspectRatio(Package owner) {
// 对于 O 之前的应用程序默认为 (1.86) 16.7:9 纵横比,对于 O 及更高版本则未设置。
float maxAspectRatio = owner.applicationInfo.targetSdkVersion < O
? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
if (owner.applicationInfo.maxAspectRatio != 0) {
// 如果应用设置了android:maxAspectRatio 属性值,则使用应用程序所配置的最大宽高比。
maxAspectRatio = owner.applicationInfo.maxAspectRatio;
} else if (owner.mAppMetaData != null
&& owner.mAppMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) {
// 未设置则设置为默认的1.86
maxAspectRatio = owner.mAppMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
}
for (Activity activity : owner.activities) {
// If the max aspect ratio for the activity has already been set, skip.
if (activity.hasMaxAspectRatio()) {
continue;
}
// 这里可以获取activity设置的maxAspectRatio属性,若应用和activity都设置了,则以activity的优先
final float activityAspectRatio = activity.metaData != null
? activity.metaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio)
: maxAspectRatio;
// 将最大宽高比设置给activity
activity.setMaxAspectRatio(activityAspectRatio);
}
}
}
以上就是为何应用无法全屏显示的原因,以及另外一种修改为何也能生效的原因。