Android 8.0 适配

本文详细介绍Android O(8.0)系统下应用适配的关键点,包括通知、安装APK、应用图标及后台服务运行限制等场景的具体调整策略。

Android O (8.0)(API 26) 适配

最近将我们项目的targetSdkVersion升到26了,下面是一些需要适配的问题,后面遇到其他问题再补充:
一、参考我另一篇文章 Android 8.0 通知适配
二、安装APK
1、在Android 8.0及以上系统调用安装Apk的代码会发现屏幕闪一下就结束了,并没有跳转到安装Apk的界面。因为Android 8.0中,Google 移除掉了容易被滥用的允许未知来源应用的开关,在安装应用商店之外的第三方来源Apk的时候,需要用户手动授予安装未知应用的许可。
2、适配
首先在AndroidManifest文件中添加安装未知来源应用的权限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
上面一步是必须的,添加了上面的权限,在Android8.0及以上也可以正常的安装Apk了,安装时如果Apk未允许安装则会弹窗让用户选择允许安装,如果允许则会安装,不允许则不安装。下面还有些设置,不是必须的。canRequestPackageInstalls方法(使用注意添加上面权限)查看此Apk是否已经允许安装了,不允许安装可以跳转到安装未知应用的设置页,让用户设置,然后返回App会回到onActivityResult中。示例如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    boolean installAllowed = getActivity().getPackageManager().canRequestPackageInstalls();
    if (installAllowed) {
        installApk();
    } else {
        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
        startActivityForResult(intent, 100);
    }
} else {
    installApk();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode == 100) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            boolean installAllowed = getActivity().getPackageManager().canRequestPackageInstalls();
            if (installAllowed) {
                installApk();
            }
        }
    }
}
参考链接:https://blog.youkuaiyun.com/qq_17766199/article/details/80965631
三、应用图标适配
1、背景
建议看一下郭神写的 Android应用图标微技巧,8.0系统中应用图标的适配,不适配会有什么问题,是否一定要适配。答案是targetSdkVersion26以下不需要适配,targetSdkVersion26及以上不适配不会引起崩溃,只是图片在Android 8.0以上的手机上会变成下面这样,会将我们原来的icon放到一个白色层上了。

在这里插入图片描述

2、适配
在app的res下右键->New->Image Asset,打开AS提供的编辑器就可以生成icon了

在这里插入图片描述

Icon Type: 保持默认,表示同时创建兼容8.0系统以及老版本系统的应用图标。下面三个页签Foreground Layer用于编辑前景层,Background Layer用于编辑背景层,Legacy用于编辑老版本系统的图标,Trim选择No。右边是预览区。设置Foreground Layer的Image path,Background Layer的Image path或color。最后选择图片后注意Resize的大小,别超出那个黑圈了,里面是安全区域,超出去可能被截掉。

在这里插入图片描述

3、注意:在8.0以后的手机上会使用mipmap-anydpi-v26里面的icon,ic_launcher_round是Android 7.1系统上的过渡版本,若要使用则在AndroidManifest中application下使用android:roundIcon属性设置。看下面图片,这是在Andoird8.0下的系统会使用mipmap-(x)hdpi下的图片,可以看到图片四周都是透明的,在手机上看的icon比其他app的小。如果嫌小可以将除mipmap-anydpi-v26下的ic_laucher替换为之前的。

在这里插入图片描述

四、后台服务运行的限制
1、官网介绍如下:

Android 8.0 还对特定函数做出了以下变更:

  • 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。
  • 新的 Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。

如需了解详细信息,请参阅后台执行限制

2、我自己重现了一下,具体如下:

我简单重现了下,我是在Activity的onPause()中延时调用启动了启动服务,按home键进入后台,生命周期执行onPause(),我试了下我手机100s就会重现崩溃了

@Override
protected void onPause() {
    super.onPause();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            Intent intent = new Intent(context, MyService.class);
            context.startService(intent);
        }
    }, 100000);
}

报错如下,IllegalStateException异常

12-20 17:57:12.526 15250-15250/com.bill.test E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.bill.test, PID: 15250
    java.lang.IllegalStateException: Not allowed to start service Intent { act=com.bill.test.service.action.task cmp=com.bill.test/com.bill.test.MyService (has extras) }: app is in background uid UidRecord{dd3b2cd u0a159 LAST bg:+1m40s41ms idle change:cached procs:2 seq(0,0,0)}
        at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1595)
        at android.app.ContextImpl.startService(ContextImpl.java:1550)
        at android.content.ContextWrapper.startService(ContextWrapper.java:664)
        at com.peopledaily.common.statistics.post.MyService$1.run(MyService.java:69)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
3、解决方案按照上面1中的方案:
  • 启动服务时判断一下在Android 8.0后调用startForegroundService启动服务,8.0前还是用startService
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(intent);
    } else {
        context.startService(intent);
    }

添加完上面代码还会报下面错误,这时还需要调用startForeground

12-20 18:30:05.066 15831-15831/com.bill.test E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.bill.test, PID: 15831
    android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{5ca9d62 u0 com.bill.test/com.bill.test.MyService}
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1835)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
  • Google要求在调用startForegroundService之后5s内调用startForeground,我是Service的onCreate中调用的startForeground,如下:
 @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(0x64, new Notification());
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值