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);             ...        }    }

下面就是找这两者之间的联系了,概括在图中:

三.总结

  1. 从抛异常的地方可以看出,最终将是调用者会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(如果不做相关保护的话)
  2. A应用进程可以是system_server
  3. O及O以上的app尽量不要通过起后台service进行操作,需要用到后台service的话可以通过JobScheduler进行处理,即如果你是个简单的三方应用,不要再使用
    调用后台service的形式唤醒应用了,调用者会很危险!!!
    4.具体流程图如下:
    (提供上面简图的draw.io的xml文件,可以使用draw.io导入修改
    提供如下时序图的uml文件,可使用带plantuml插件的inteliJ AS打开编辑
    https://pan.baidu.com/s/17MO9CXdcSBLI_kuywvCuZA)

更多相关文章

  1. cocos2dx android 真机调试时Logcat不显示日志信息
  2. android不死服务的实现方法
  3. 滚动条~~~xml方式(一)
  4. Android(安卓)opencv库使用遇到的坑
  5. Android(安卓)RIL CDMA分支总结(1)
  6. android实现系统的返回键和home键
  7. Eclipse 运行Android程序在虚拟机中,出现问题
  8. android分享,如何移除掉信息这项
  9. Android系统关机或重启的几种实现方式

随机推荐

  1. Android React Native在Android Studio中
  2. Android Studio android APP混淆打包
  3. Android(安卓)permission 权限类及中英文
  4. Android中调用webservice小结
  5. Android UI---自定义形状shape
  6. Android中目的地Intent的使用
  7. android基础入门布局讲解(2)
  8. android tab选项卡效果
  9. android 中文api (63) —— SimpleAdapter.
  10. Android中Drawable Bitmap Canvas Paint