适配Android 知识Android

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

2019-01-02  本文已影响92人  Typist夫少

前言

新年第一篇总结与分享~
离19年新年就剩一个月左右了,整个公司的项目也不像以往一样紧张、忙碌,周末同事和朋友聊得最多的就是抢票和年终奖了(然而我们公司并没有😭),为自己心疼一分钟。。。
总结一下18年吧,主要负责了4个商业项目,还参与了公司 Android 新框架的封装,整体来说算是稳步提高吧。
先说一下写这篇文章的背景吧,主要就是不太忙了,再加上客户要求适配 Android 9.0 (第一个客户主动提出来的),在看完9.0的适配之后,也对之前的进行了一些整理,废话不多说,请看干货!

Android6.0适配

权限适配

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

权限适配三连问

危险权限分组说明

权限组 权限名称
CALENDAR android.permission.READ_CALENDAR
android.permission.WRITE_CALENDAR
CAMERA android.permission.CAMERA
CONTACTS android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
android.permission.GET_ACCOUNTS
LOCATION android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
MICROPHONE android.permission.RECORD_AUDIO
PHONE android.permission.READ_PHONE_STATE
android.permission.CALL_PHONE
android.permission.READ_CALL_LOG
android.permission.ADD_VOICEMAIL
android.permission.WRITE_CALL_LOG
android.permission.USE_SIP
android.permission.PROCESS_OUTGOING_CALLS
SENSORS android.permission.BODY_SENSORS
SMS android.permission.SEND_SMS
android.permission.RECEIVE_SMS
android.permission.READ_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.RECEIVE_MMS
STORAGE android.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在应用间共享文件这篇文章。

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 方法监听数据变化时,会出现上述异常。
解决方案:

  1. 在清单文件配置:
<provider
       android:name="com.activeandroid.content.ContentProvider"
       android:authorities="com.jz.androidclient"
       android:enabled="true"
       android:exported="false"/>
  1. 去掉这个监听刷新的方法,改为广播刷新

静态广播无法正常接收

问题原因: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 限制了明文流量的网络请求,非加密的流量请求都会被系统禁止掉
解决方案:

  1. 在资源文件新建xml目录,新建文件network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
  1. 清单文件配置:
<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);
上一篇下一篇

猜你喜欢

热点阅读