Android版本适配(基于 6.0 ~ 9.0)

Android版本适配(基于 6.0 ~ 9.0)

转载自:https://www.jianshu.com/p/46699e98dc59

Android6.0适配

权限适配

动态权限适配是 Android 6.0 最先开始的,也是 Android 系统对开发者影响最大的改动之一。

先给出一个自己用到的权限框架: https://github.com/googlesamples/easypermissions

权限适配三连问

Q: 是否 Android 6.0 所有权限都需要动态申请?
A: 不是。只有属于危险权限的才需要申请。
Q: 危险权限有哪些?
A: 见下面“危险权限分组说明”

Q: 危险权限是否需要一个一个申请?
A: 在 Android 6.0 ~ Android 8.0,不需要。如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用,即对于同一组内的权限,只要有一个被同意,其他的都会被同意。在 Android 8.0 之后,此行为已被纠正。系统只会授予应用明确请求的权限。然而一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准,但是若没有请求相应的权限而进行操作的话就会出现应用 crash 的情况。

危险权限分组说明

权限组

权限组权限名称
CALENDARandroid.permission.READ_CALENDAR
android.permission.WRITE_CALENDAR
CAMERAandroid.permission.CAMERA
CONTACTSandroid.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
android.permission.GET_ACCOUNTS
LOCATIONandroid.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
MICROPHONEandroid.permission.RECORD_AUDIO
PHONEandroid.permission.READ_PHONE_STATE
android.permission.CALL_PHONE
android.permission.READ_CALL_LOG
android.permission.READ_CALL_LOG
android.permission.ADD_VOICEMAIL
android.permission.WRITE_CALL_LOG
android.permission.USE_SIP
android.permission.PROCESS_OUTGOING_CALLS
SENSORSandroid.permission.BODY_SENSORS
SMSandroid.permission.SEND_SMS
android.permission.RECEIVE_SMS
android.permission.READ_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.RECEIVE_MMS
STORAGEandroid.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE

对应在清单文件中的展示如下:

 <!--CALENDAR-->
    <uses-permission android:name="android.permission.READ_CALENDAR"/>
    <uses-permission android:name="android.permission.WRITE_CALENDAR"/>
    <!--CAMERA-->
    <uses-permission android:name="android.permission.CAMERA"/>
    <!--CONTACTS-->
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
    <!--LOCATION-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <!--MICROPHONE-->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <!--PHONE-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.READ_CALL_LOG"/>
    <uses-permission android:name="android.permission.ADD_VOICEMAIL"/>
    <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
    <uses-permission android:name="android.permission.USE_SIP"/>
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <!--SENSORS-->
    <uses-permission android:name="android.permission.BODY_SENSORS"/>
    <!--SMS-->
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
    <uses-permission android:name="android.permission.RECEIVE_MMS"/>
    <!--STORAGE-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Android 7.0适配

应用间共享文件

在 targetSdkVersion 大于等于的 24 的 app 中,但是我们没有去适配 Android 7.0。那么在调用安装页面,或修改用户头像操作时,就会失败。那么就需要你去适配 Android 7.0或是将 targetSdkVersion 改为 24 以下(不推荐)。适配的方法这里就不细讲,大家可以看鸿洋大神的 Android 7.0 行为变更 通过FileProvider在应用间共享文件这篇文章。

https://blog.csdn.net/lmj623565791/article/details/72859156

APK signature scheme v2

Android 7.0 引入一项新的应用签名方案 APK Signature Scheme v2,它能提供更快的应用安装时间和
更多针对未授权 APK 文件更改的保护。在默认情况下,Android Studio 2.2 和 Android Plugin for 
Gradle 2.2 会使用 APK Signature Scheme v2 和传统签名方案来签署您的应用。

说明:

签名打包

  1. 只勾选V1签名就是传统方案签署,但是在 Android 7.0 上不会使用V2安全的验证方式。
  2. 只勾选V2签名7.0以下会显示未安装,Android 7.0 上则会使用了V2安全的验证方式。
  3. 同时勾选V1和V2则所有版本都没问题。

org.apache不支持问题

// build.gradle里面加上这句话
defaultConfig {
       useLibrary 'org.apache.http.legacy'
}

SharedPreferences闪退

// MODE_WORLD_READABLE:Android 7.0以后不能使用这个获取,会闪退
// 应修改成MODE_PRIVATE
SharedPreferences read = getSharedPreferences(RELEASE_POOL_DATA, MODE_WORLD_READABLE);

Android 8.0适配

Android 8.0中PHONE权限组新增两个权限

ANSWER_PHONE_CALLS:允许您的应用通过编程方式接听呼入电话。要在您的应用中处理呼入电话,您可以使用
acceptRingingCall() 函数。 READ_PHONE_NUMBERS:权限允许您的应用读取设备中存储的电话号码。

通知适配

Android 8.0中,为了更好的管制通知的提醒,不想一些不重要的通知打扰用户,新增了通知渠道,用户可以根据渠道来屏蔽一些不想要的通知。
代码示例如下:

/**
 * Description: Android 8.0通知的兼容类
 * Author: Jack Zhang
 * create on: 2019/1/2 3:16 PM
 */
public class MyNotification
{

  public static final String CHANNEL_ID_GL = "com.jz.gailun";
  public static final String CHANNEL_NAME_GL = "盖伦";
  public static final String CHANNEL_ID_AX = "com.jz.aixi";
  public static final String CHANNEL_NAME_AX = "艾希";
  public static final String CHANNEL_ID_LL = "com.jz.liulang";
  public static final String CHANNEL_NAME_LL = "流浪";

  public static void setONotifyChannel(NotificationManager manager, String channeId, String channelName)
  {
    setONotifyChannel(manager, null, channeId, channelName);
  }

  public static void setONotifyChannel(NotificationManager manager, NotificationCompat.Builder builder, String channeId, String channelName)
  {
    if (TextUtils.isEmpty(channeId) || TextUtils.isEmpty(channelName))
      Logger.e("Android 8.0 Notification的channeId与channelName不能为空");

    if (Build.VERSION.SDK_INT >= 26)
    {
      // 第三个参数设置通知的优先级别
      NotificationChannel channel = new NotificationChannel(channeId, channelName, NotificationManager.IMPORTANCE_DEFAULT);
      // 是否可以绕过请勿打扰模式
      channel.canBypassDnd();
      // 是否可以显示icon角标
      channel.canShowBadge();
      // 是否显示通知闪灯
      channel.enableLights(true);
      // 收到消息时震动提示
      channel.enableVibration(true);
      // 设置绕过免打扰
      channel.setBypassDnd(true);
      channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_SECRET);
      // 设置闪光灯颜色
      channel.setLightColor(Color.RED);
      // 获取设置铃声设置
      channel.getAudioAttributes();
      // 设置震动模式
      channel.setVibrationPattern(new long[]{100, 200, 100});
      // 是否会闪光
      channel.shouldShowLights();
      if (manager != null)
        manager.createNotificationChannel(channel);
      if (builder != null)
        builder.setChannelId(channeId);//这个id参数要与上面channel构建的第一个参数对应
    }
  }

  public static Notification getNotification(Context context, String channelId)
  {
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId);
    Notification notification = notificationBuilder.setOngoing(true)
            .setSmallIcon(R.mipmap.ic_logo)
            .setPriority(NotificationManager.IMPORTANCE_MIN)
            .setCategory(Notification.CATEGORY_SERVICE)
            .build();
    return notification;
  }
}
/**
 * Description: 通知管理类
 * Author: Jack Zhang
 * create on: 2019/1/2 3:23 PM
 */
public class NotifyManager
{
  private volatile static NotifyManager INSTANCE;

  private NotifyManager(Context context)
  {
    initNotifyManager(context);
  }

  public static NotifyManager getInstance(Context context)
  {
    if (INSTANCE == null)
      synchronized (NotifyManager.class)
      {
        if (INSTANCE == null)
          INSTANCE = new NotifyManager(context);
      }
    return INSTANCE;
  }

  private NotificationManager manager;
  // NotificationManagerCompat
  private NotificationCompat.Builder builder;

  // 初始化通知栏配置
  private void initNotifyManager(Context context)
  {
    context = context.getApplicationContext();
    manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
//    // 如果存在则清除上一个消息
//    manager.cancel(lastNotificationId);
    builder = new NotificationCompat.Builder(context, MyNotification.CHANNEL_ID_GL);

    MyNotification.setONotifyChannel(manager, builder, MyNotification.CHANNEL_ID_GL, MyNotification.CHANNEL_NAME_GL);

    // 设置标题
    builder.setContentTitle("Title");
    // 状态栏的动画提醒语句
    builder.setTicker("Ticker");
    // 什么时候提醒
    builder.setWhen(System.currentTimeMillis());
    // 设置通知栏的优先级
    builder.setPriority(Notification.PRIORITY_DEFAULT);
    // 设置点击可消失
    builder.setAutoCancel(true);
    // 设置是否震动等
    builder.setDefaults(Notification.DEFAULT_VIBRATE);
    // 设置icon
    builder.setSmallIcon(R.mipmap.ic_logo);
    // 设置点击意图
    Intent intent = new Intent(context, MainActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    builder.setContentIntent(pendingIntent);
  }

  /**
   * 显示盖伦通知栏
   *
   * @author Jack Zhang
   * create at 2019/1/2 3:28 PM
   */
  public void showGLNotify(Context context)
  {
    // 设置内容
    builder.setContentText("盖伦");
    manager.notify(1, builder.build());
  }

  /**
   * 显示艾希通知栏
   *
   * @author Jack Zhang
   * create at 2019/1/2 3:28 PM
   */
  public void showAXNotify(Context context)
  {
    builder.setContentText("艾希");
    manager.notify(2, builder.build());
  }

  /**
   * 显示流浪通知栏
   *
   * @author Jack Zhang
   * create at 2019/1/2 3:28 PM
   */
  public void showLLNotify(Context context)
  {
    builder.setContentText("流浪");
    manager.notify(3, builder.build());
  }
}

SecurityException的闪退

问题原因:项目使用了ActiveAndroid,在 8.0 或 8.1 系统上使用 26 或以上的版本的 SDK 时,调用 ContentResolver 的 notifyChange 方法通知数据更新,或者调用 ContentResolver 的 registerContentObserver 方法监听数据变化时,会出现上述异常。

解决方案:
在清单文件配置:

<provider
       android:name="com.activeandroid.content.ContentProvider"
       android:authorities="com.jz.androidclient"
       android:enabled="true"
       android:exported="false"/>

去掉这个监听刷新的方法,改为广播刷新

静态广播无法正常接收

问题原因:Android 8.0 引入了新的广播接收器限制,因此您应该移除所有为隐式广播 Intent 注册的广播接收器。
解决方案:使用动态广播代替静态广播。

Only fullscreen opaque activities can request orientation

Caused by: java.lang.IllegalStateException: Only fullscreen opaque
activities can request orientation

问题原因:Android 8.0 非全屏透明页面不允许设置方向(后面8.1系统谷歌就去掉了这个限制,可能是真的没必要)

解决方案:

  1. android:windowIsTranslucent设置为false。
  2. 如果还是想用的话,就去掉清单文件中Activity中的android:screenOrientation=“portrait”。
  3. 使用透明的dialog或者PopupWindow来代替,也可以用DialogFragment,看自己的需求和喜好。

Android 9.0适配

CLEARTEXT communication to life.115.com not permitted by network security policy

CLEARTEXT communication to life.115.com not permitted by network
security polic

问题原因: Android P 限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉

解决方案:

在资源文件新建xml目录,新建文件network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

清单文件配置:

<application
    android:networkSecurityConfig="@xml/network_security_config">
    <!--Android 9.0加的-->
    <uses-library
        android:name="org.apache.http.legacy"
        android:required="false" />
</application>

其他Api的修改

java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT
and DIFFERENCE are allowed

if (Build.VERSION.SDK_INT >= 26) 
  canvas.clipPath(mPath); 
else
  canvas.clipPath(mPath, Region.Op.REPLACE);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值