Android版本适配指北

Android5.0

  1. multiDexEnabled
  2. Button将总是位于最上层

从5.0开始,在同一个layout下,就算你在Button上覆盖了相应的View,Button将总是位于最上层。产生原因:stateListAnimator属性。谷歌在Material Design中推出,是一个非常简单的方法用来实现在可视状态之间平滑过渡。这个属性可以通过android:stateListAnimator进行设置,可以使控件在点击时产生不同的交互。对于Button,点击时默认有个阴影的效果用于表示按下的状态(5.0以前就是简单的变色)。 解决方法:可以使用 android:stateListAnimator=”@null” 去掉阴影效果而使Button可以被正常的覆盖。

Android6.0

  1. 动态权限
  2. wifi

Android6.0之后,Wifi的使用更加严格。需要动态获取LOCATION权限,如果还想获取Wifi列表的话还需要打开GPS(位置信息)。

1
2
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>

Android7.0

  1. 使用FileProvider授权参考

  2. APK signature scheme v2

  3. org.apache不支持问题

Android8.0

  1. Notification(通知权限)

    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
    /**
    * 判断通知权限是否开启
    * @param context 上下文
    */
    public static boolean isNotificationEnabled(Context context){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    return ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).areNotificationsEnabled();
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
    ApplicationInfo appInfo = context.getApplicationInfo();
    String pkg = context.getApplicationContext().getPackageName();
    int uid = appInfo.uid;

    try {
    Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
    Method checkOpNoThrowMethod = appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class);
    Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION");
    int value = (Integer) opPostNotificationValue.get(Integer.class);
    return (Integer) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg) == 0;
    } catch (NoSuchMethodException | NoSuchFieldException | InvocationTargetException | IllegalAccessException | RuntimeException | ClassNotFoundException ignored) {
    return true;
    }
    } else {
    return true;
    }
    }
  2. Notification(通知适配)

  3. 静态广播无法正常接收

Google官方声明:从android 8.0(API26)开始,对清单文件中静态注册广播接收者增加了限制,建议大家不要在清单文件中静态注册广播接收者,改为动态注册。当然,如果你还是想用静态注册的方式也是有方法的,Intent里添加Component参数可实现。

Android9.0

  1. 刘海屏支持

    Android 9 支持最新的全面屏,其中包含为摄像头和扬声器预留空间的屏幕缺口。 通过 DisplayCutout类可确定非功能区域的位置和形状,这些区域不应显示内容。 要确定这些屏幕缺口区域是否存在及其位置,使用 getDisplayCutout() 函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
View decorView = getWindow().getDecorView();
WindowInsets rootWindowInsets = decorView.getRootWindowInsets();
if (rootWindowInsets != null) {
DisplayCutout cutout = rootWindowInsets.getDisplayCutout();
List<Rect> boundingRects = cutout.getBoundingRects();
if (boundingRects != null && boundingRects.size() > 0) {
String msg = "";
for (Rect rect : boundingRects) {
msg = msg +"left-" + rect.left;
Log.d(TAG, msg);
}
}
}
}

Android 10.0

  1. Scoped Storage(分区存储)
  • 特定目录(App-specific),使用getExternalFilesDir(String type)或 getExternalCacheDir()方法访问。无需权限,且卸载应用时会自动删除。
  • 照片、视频、音频这类媒体文件。使用MediaStore 访问,访问其他应用的媒体文件时需要READ_EXTERNAL_STORAGE权限。
  • 其他目录,使用存储访问框架SAF(Storage Access Framwork)
  1. 权限变化

  2. 后台启动 Activity 的限制

简单解释就是应用处于后台时,无法启动Activity。因为此项行为变更适用于在 Android 10 上运行的所有应用,所以这一限制导致最明显的问题就是点击推送信息时,有些应用无法进行正常的跳转(具体的实现问题导致)。所以针对这类问题,全屏 intent,注意设置最高优先级和添加USE_FULL_SCREEN_INTENT权限,这是一个普通权限。比如微信来语音或者视频通话时,弹出的接听页面就是使用这一功能。

Android11.0

  1. 强制执行分区存储