Android P (9.0)(API 28) 适配
最近将我们项目的targetSdkVersion升到28了,由于是从Android O (26)升到Android P(28)遇到的问题不算多,下面是一些需要适配的问题,后面遇到其他问题再补充:
一、适配,首先更改 build.gradle,我这边的更改如下:
android {
compileSdkVersion 28
buildToolsVersion 28.0.3
defaultConfig {
minSdkVersion 17
targetSdkVersion 28
}
}
dependencies {
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support:support-annotations:28.0.0'
}
编译过了就可以了,我们项目这边编译报错了,原因是 support 库依赖冲突了,有其他库依赖了其他版本的 support 库,解决方案如下:
通过下面命令查看下哪个库冲突了
gradlew :app:dependencies
1、然后在引入依赖的时候移除掉即可
implementation ('xxx') {
exclude group: 'com.android.support'
}
2、强制设置都依赖某个版本
android {
configurations.all {
resolutionStrategy.force "com.android.support:support-v4:28.0.0"
}
}
二、全面屏适配
全面屏适配使用的是github上面star比较多的 ImmersionBar 库,该库可以更改状态栏和导航栏的颜色等,具体使用方法可以去github上查看
三、Apache HTTP client 相关类找不到
将 compileSdkVersion 升级到 28 之后,如果在项目中用到了 Apache HTTP client 的相关类,就会抛出找不到这些类的错误。这是因为官方已经在 Android P 的启动类加载器中将其移除。我们项目中引用了科大讯飞的语音识别库会报下面错误。
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/util/ByteArrayBuffer;
解决方法为在AndroidManifest.xml的中加入以下代码
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
四、前台Service适配
在 Android P 中,如果 targeSdkVersion 升级到 28,使用前台 Service 必须要申请 FOREGROUND_SERVICE 权限,如果没有申请该权限,系统会抛出 SecurityException,该权限为普通权限,申请自动授予应用。在AndroidManifest.xml中加入
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
五、Only fullscreen opaque activities can request orientation 崩溃
1、崩溃信息
targetSdkVersion 升到28后,在 Android 8.0.0 版本的手机上程序会崩溃,报错如下
Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
at android.app.Activity.onCreate(Activity.java:987)
at android.support.v4.app.SupportActivity.onCreate(SupportActivity.java:66)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:297)
at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:84)
at xxx.XXXActivity.onCreate(XXXActivity.java:xx)
2、崩溃分析
根据上面调用栈可以看出是自己 XXXActivity 调用 onCreate 后,最终在 Activity 的第987行报错,到 Activity中看下源码(注意 Android API 28 中已经没有下面代码了)如下:
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
if (getApplicationInfo().targetSdkVersion > O && mActivityInfo.isFixedOrientation()) {
final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
ta.recycle();
if (isTranslucentOrFloating) {
throw new IllegalStateException(
"Only fullscreen opaque activities can request orientation");
}
}
...
}
public static boolean isTranslucentOrFloating(TypedArray attributes) {
final boolean isTranslucent =
attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent,
false);
final boolean isSwipeToDismiss = !attributes.hasValue(
com.android.internal.R.styleable.Window_windowIsTranslucent)
&& attributes.getBoolean(
com.android.internal.R.styleable.Window_windowSwipeToDismiss, false);
final boolean isFloating =
attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating,
false);
return isFloating || isTranslucent || isSwipeToDismiss;
}
public boolean isFixedOrientation() {
return isFixedOrientationLandscape() || isFixedOrientationPortrait()
|| screenOrientation == SCREEN_ORIENTATION_LOCKED;
}
通过源码可以看出下面三个条件都符合系统就抛出异常了。
- targetSdkVersion > O targetSdkVersion 大于26;
- isFixedOrientation 固定方向:横屏或竖屏或锁定
- isTranslucentOrFloating 透明或漂浮:在 AndroidManifest 中设置 windowIsTranslucent 或 windowSwipeToDismiss 或 windowIsFloating为 true
我们这边报错是在 AndroidManifest 中设置了如下代码,我们项目侧滑返回时设置了 Activity 透明,然后又设置了 Activity 布局竖直,这两个条件共同作用时就崩溃了
<activity
android:name=".XXXActivity"
android:screenOrientation="portrait"
android:theme="@style/TranslucentTheme" />
<style name="TranslucentTheme" parent="xxx">
...
<item name="android:windowIsTranslucent">true</item>
</style>
最终分析崩溃同时满足下面4点
- Android 8.0.0 版本手机
- targetSdkVersion 大于 26
- 固定方向
- 透明 Activity
3、解决方案
具体没有好的解决方案,源码中的代码也改不了,具体解决只能是规避了,如下:
1、将 targetSdkVersion 设置为26及以下
2、判断 Android 8.0.0 版本的手机不设置透明;或者非要设置透明的话,则不设置方向,此时这个透明 Activity 的方向是依赖其父Activity的
六、getCurrentFocus() 为空
隐藏软键盘时之前用的是下面代码:
((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(SearchActivity.this.getCurrentFocus().getWindowToken(), HIDE_NOT_ALWAYS);
然后空指针了如下,getCurrentFocus() 返回空了
java.lang.NullPointerException: Attempt to invoke virtual method 'android.os.IBinder android.view.View.getWindowToken()' on a null object reference
xxx.SearchActivity.requestSearch(SearchActivity.java:762)
at xxx.SearchActivity.access$200(SearchActivity.java:91)
at xxx.SearchActivity$3.onItemClick(SearchActivity.java:199)
getCurrentFocus() 方法是获取当前焦点的 View,之前是能获取到输入的 EditText 的,现在有时候获取不到了就不用 getCurrentFocus() 获取了,直接写当前的 EditText即可
EditText editText = findViewById(R.id.edit_text);
((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(editText.getWindowToken(), HIDE_NOT_ALWAYS);