Android 8.0 适配

xiaoxiao2025-08-12  25

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.csdn.net/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()); } }
转载请注明原文地址: https://www.6miu.com/read-5034705.html

最新回复(0)