Android各版本适配之8.0

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/cui130/article/details/80777738
相关链接:

●安装apk的权限

首先第一步:我们需要在清单文件中添加一项权限
 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> 
有了这个权限之后,安装apk时判断:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !getPackageManager().canRequestPackageInstalls()){
   PermissionUtil.requestPermissions(this,PermissionUtil.PERMISSION_INSTALL_PACKAGES_CODE, Manifest.permission.REQUEST_INSTALL_PACKAGES);
}

具体权限管理请移步文章开头的6.0的权限管理文章查看。

通知栏的适配

Android O版本对通知做了规范性的控制,强制用户在发送通知的时候,对通知进行系统性的管理,新增了channel渠道功能:

private String Notification_ID = "5";
private String Notification_CHANNEL_1 = "channel5";

private void showNotification(long cancelTime, String text) {
   try {
      Context app = context.getApplicationContext();
      NotificationManager nm = (NotificationManager) app
            .getSystemService(Context.NOTIFICATION_SERVICE);
      final int id = Integer.MAX_VALUE / 13 + 1;
      nm.cancel(id);

      long when = System.currentTimeMillis();
      Notification notification;
      PendingIntent pi = PendingIntent.getActivity(app, 0, new Intent(), 0);
      if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.O){
         NotificationChannel
               channel = new NotificationChannel(String.valueOf(Notification_ID), Notification_CHANNEL_1, NotificationManager.IMPORTANCE_LOW);
         channel.enableLights(true);//是否在桌面icon右上角展示小红点
         channel.setLightColor(Color.GREEN);//小红点颜色
         channel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知
         nm.createNotificationChannel(channel);

         notification = new Notification.Builder(context)
               .setChannelId(String.valueOf(Notification_ID))//该句适配android 8.0 版本
               .setSmallIcon(notifyIcon)
               .setContentTitle(notifyTitle)
               .setTicker(text)
               .setContentText(text)
               .setWhen(when)
               .setContentIntent(pi).setNumber(0)
               .getNotification();
         notification.flags |= Notification.FLAG_AUTO_CANCEL;

      }else {
         notification = new Notification.Builder(context)
               .setSmallIcon(notifyIcon)
               .setContentTitle(notifyTitle)
               .setTicker(text)
               .setContentText(text)
               .setContentIntent(pi).setNumber(0)
               .setWhen(when)
               .getNotification();
         notification.flags |= Notification.FLAG_AUTO_CANCEL;
      }

      nm.notify(id, notification);

      if (cancelTime > 0) {
         Message msg = new Message();
         msg.what = MSG_CANCEL_NOTIFY;
         msg.obj = nm;
         msg.arg1 = id;
         UIHandler.sendMessageDelayed(msg, cancelTime, this);
      }
   } catch (Exception e) {
      e.printStackTrace();
   }
}
这样就会在8.0系统上弹出通知了。当然还需要及时的关闭通知,当更新进度条加载完成或者其他时候
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    //关闭通知通道
    notificationManager.deleteNotificationChannel("1");
}
悬浮框(7.0,8.0会崩溃)
在一些广播中需要弹出对话框的应用场景,没有依附的activity,这个时候就可以用悬浮窗。
在Android系统中,如果应用需要弹出一个悬浮窗口,就需要申请一项特殊权限“
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Android O之前的系统中申请了该权限后,再给对应的window设置type:WindowManager.LayoutParams.TYPE_SYSTEM_ALERT悬浮窗口就可以显示出来。但是在Android O的系统中,google规定申请"android.permission.SYSTEM_ALERT_WINDOW"权限的应用需要给悬浮窗设置type:WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY.

悬浮窗口才能显示出来,如果不设置该TYPE,会Crash,8.0代码修改如下:

public static void showLogoutDialog(final Context context) {
    AlertDialog.Builder builder = new AlertDialog.Builder(context);
    View view = View.inflate(context, R.layout.dialog_logout, null);
    builder.setView(view);
    final AlertDialog dialog = builder.create();
    
    //设置弹出全局对话框,但是这句话并不能解决在android的其他手机上能弹出来(例如用户华为p10 就无法弹框)
    // dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST);
    
    //只有这样才能弹框
    if (Build.VERSION.SDK_INT>=26) {//8.0新特性
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
    }else{
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    }
    dialog.show();
}
另外,6.0后需要申请悬浮窗权限,属于特殊权限,关于此权限的申请请看文章开头的6.0的适配文章链接;

透明Activity报错

Android 8.0透明Activity报错 "Only fullscreen activities can request orientation"

1、分析问题

首先,我的代码是这样的: 
style.xml

<style name="app_transparent_activity" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:windowContentTransitions">true</item>
    <item name="android:windowSoftInputMode">adjustPan</item>    <item name="android:windowDrawsSystemBarBackgrounds">false</item>
    <item name="android:windowBackground">@color/app_transparent_color</item>
    <item name="android:windowIsTranslucent">true</item>//透明    <item name="android:windowIsFloating">true</item>//悬浮
</style>
  • AndroidManifest.xml
<activity
    android:name=".appmanager.mianframe.UsagePermissionGuideActivity"
    android:launchMode="singleInstance"
    android:screenOrientation="portrait"
    android:theme="@style/app_transparent_activity"/>

从上面可以看出,我的activity是一个状态栏透明并且背景透明的activity。在26的编译版本时是可以正常的使用的,但是当把编译版本升级到27时,就会出现”Only fullscreen activities can request orientation”异常。这是google出于安全的考虑,对android8.0以后的版本做的处理,当一个Activity固定方向并且是透明的,在8.0以后的版本中就会抛出异常。相关源码如下:

Entry ent = AttributeCache.instance().get(packageName,realTheme, com.android.internal.R.styleable.Window, userId);
final boolean translucent = ent != null && (ent.array.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent, false)|| (!ent.array.hasValue(
                com.android.internal.R.styleable.Window_windowIsTranslucent) &&  ent.array.getBoolean(com.android.internal.R.styleable.Window_windowSwipeToDismiss,false)));
fullscreen = ent != null && !ent.array.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false) && !translucent;
fullscreen = ent != null && !ActivityInfo.isTranslucentOrFloating(ent.array);
noDisplay = ent != null && ent.array.getBoolean(com.android.internal.R.styleable.Window_windowNoDisplay, false);

if (ActivityInfo.isFixedOrientation(requestedOrientation) && !fullscreen && appInfo.targetSdkVersion >= O) {
    throw new IllegalStateException("Only fullscreen activities can request orientation");
}

上面是27的源码片段,通过上面我们可以看出当三个条件同时满足的时候,系统会抛出”Only fullscreen activities can request orientation”异常。先分别来说说这三个条件都表示什么意思:

  1. ActivityInfo.isFixedOrientation(requestedOrientation) —— 表示判断当前的|Activity是否固定了方向
  2. fullscreen —— 表示Activity是否是透明的或者是否悬浮在Activity上,是透明的或者悬浮在Activity上fullscreen就等于false
  3. appInfo.targetSdkVersion >= O —— 表示版本号大于等于26

当以上的三个条件同时满足的时候,系统框架就会抛出异常。

2、解决问题的方法

这个问题在最新的SDK中已经修复,我们在API Level 27的设备上已经无法重现,但我们手头的API Level 26的设备还是能重现。而且根据上面的代码来看,如果想保留当前Activity的style,“isTranslucentOrFloating”的逻辑根本没法绕过,所以想绕开很难,目前能想到的大概两个方向:

  1. 1.推迟SDK升级,等官方修复被大多数设备采用;
  2. 2.升级SDK,但重构一下代码,看看已有的非“fullscreen” Activity是不是都是必要的,例如用Fragment实现周围半透明效果,能不能直接把Fragment加入到当前Activity(当然Detach Fragment是有重绘View的开销的)。
  3. 3.使用Dialog代替透明的Activity
  4. 4.不固定Activity的方向
  5. 5.使用不透明且不是悬浮的主题(我采用此解决办法,8.0以后改为不透明不悬浮)
展开阅读全文

没有更多推荐了,返回首页