Android(安卓)Fk:【JavaCrash】Android(安卓)26以后限制使用startService启动后台服务
16lz
2021-01-24
Android Fk:【JavaCrash】Android 26以后限制使用startService启动后台服务
一. 问题概述
1.出错调用栈
E AndroidRuntime: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.unionpay.uppay.action.HCE pkg=com.sankuai.meituan }: app is in background uid null
07-23 19:06:29.734 15328 15377 E AndroidRuntime: FATAL EXCEPTION: Thread-907-23 19:06:29.734 15328 15377 E AndroidRuntime: Process: com.unionpay.uppay, PID: 1532807-23 19:06:29.734 15328 15377 E AndroidRuntime: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.unionpay.uppay.action.HCE pkg=com.sankuai.meituan }: app is in background uid null07-23 19:06:29.734 15328 15377 E AndroidRuntime: at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1515)07-23 19:06:29.734 15328 15377 E AndroidRuntime: at android.app.ContextImpl.startService(ContextImpl.java:1471)07-23 19:06:29.734 15328 15377 E AndroidRuntime: at android.content.ContextWrapper.startService(ContextWrapper.java:654)07-23 19:06:29.734 15328 15377 E AndroidRuntime: at com.unionpay.mobile.android.hce.f.a(Unknown Source:33)07-23 19:06:29.734 15328 15377 E AndroidRuntime: at com.unionpay.mobile.android.hce.h.run(Unknown Source:6)
2.主要原因:
Android O 8.0(API 26之后) 之后不再允许后台service直接通过startService方式去启动, 具体行为变更
如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。新的 Context.startForegroundService() 函数将启动一个前台服务。
现在,即使应用在后台运行, 系统也允许其调用 Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。
3.解决办法:
1. 修改启动方式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(intent);} else { context.startService(intent);}//并且在service里再调用startForeground方法,不然就会出现ANRcontext.startForeground(SERVICE_ID, builder.getNotification());
该种方式启动用户会有感知啊,有个通知额,==
2. 调用者作好保护,防止被炸
try { Intent cameraIntent = new Intent("XXX.XXX"); cameraIntent.setPackage("com.XXX.XXX"); mContext.startServiceAsUser(cameraIntent, UserHandle.CURRENT); } catch (Exception e) { Slog.e(TAG, "IllegalAccessException", e);}
3. 放弃使用起后台服务唤醒进程
采用jobJobScheduler替换需要起后台服务的唤醒操作方式。
二.原因分析
1. 先搜所log打印的地方
搜索“Not allowed to start service”
//frameworks/base/core/java/android/app/ContextImpl.java private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) { try { ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( getContentResolver()), requireForeground, getOpPackageName(), user.getIdentifier()); if (cn != null) { ... } else if (cn.getPackageName().equals("?")) { //看出是在这里抛的异常 throw new IllegalStateException( "Not allowed to start service " + service + ": " + cn.getClassName()); } } return cn; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
搜索“app is in background”
//frameworks/base/services/core/java/com/android/server/am/ActiveServices.java ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId) throws TransactionTooLargeException { // If this isn't a direct-to-foreground start, check our ability to kick off an // arbitrary service if (!r.startRequested && !fgRequired) { ... return new ComponentName("?", "app is in background uid " + uidRec); ... } }
下面就是找这两者之间的联系了,概括在图中:
三.总结
- 从抛异常的地方可以看出,最终将是调用者会crash(如果不保护处理的话)
即 A应用进程以startservice的形式调用B应用进程的service,如果满足以下条件:
a.O及O以上的手机平台上
b.B应用进程的AndroidManifest里声明了targetSdk大于与等于26
c.B应用进程不是persistent应用
d.B应用进程当前进入后台且处于idle状态
e.B应用不在电源管理的白名单中
f.B应用进程不再运行后台运行的白名单中
此时A应用进程就会crash(如果不做相关保护的话) - A应用进程可以是system_server
- O及O以上的app尽量不要通过起后台service进行操作,需要用到后台service的话可以通过JobScheduler进行处理,即如果你是个简单的三方应用,不要再使用
调用后台service的形式唤醒应用了,调用者会很危险!!!
4.具体流程图如下:
(提供上面简图的draw.io的xml文件,可以使用draw.io导入修改
提供如下时序图的uml文件,可使用带plantuml插件的inteliJ AS打开编辑
https://pan.baidu.com/s/17MO9CXdcSBLI_kuywvCuZA)
更多相关文章
- cocos2dx android 真机调试时Logcat不显示日志信息
- android不死服务的实现方法
- 滚动条~~~xml方式(一)
- Android(安卓)opencv库使用遇到的坑
- Android(安卓)RIL CDMA分支总结(1)
- android实现系统的返回键和home键
- Eclipse 运行Android程序在虚拟机中,出现问题
- android分享,如何移除掉信息这项
- Android系统关机或重启的几种实现方式