Android Fk:【JavaCrash】Android 26以后限制使用startService启动后台服务

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-9
07-23 19:06:29.734 15328 15377 E AndroidRuntime: Process: com.unionpay.uppay, PID: 15328
07-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 null
07-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方法,不然就会出现ANR
context.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)
    这里写图片描述
发布了54 篇原创文章 · 获赞 17 · 访问量 6万+
展开阅读全文

启动不了service的原因?

12-01

小白一枚,最近在学习郭神的第一行代码,关于启动service学习中遇到个问题,代码也反复对照了好几遍确认无误,字有点多希望大神们百忙之中多多鞭策指导,感谢!Orz 开发环境:Android studio 运行环境:API21的系统 **问题内容:** 1 Service启动不了的原因是否是startService()方法处或者是Manifest中的设置问题导致?该如何进行下一步调试? 在学习到第九章启动和停止一个服务按照书中内容照搬,并按书中在创建的MyService类中的各个函数方法中都加上Log输出,运行程序后,点击启动Service的按钮之后,发现Service并没有启动(在应用程序管理运行中没有对应程序服务,Logcat下观察没有对应的Log输出),也没有报异常和错误,debug看了下在点击按钮事件中,startService()语句是执行了的,想知道下一步如何进行调试。 2在回调函数中设置断点是否有用,是否可以像普通函数一样能在debug时停到回调函数中设置的断点处? 这个问题就是上面调试的过程中一直没能将调试过程走到回调函数的断点处,我分析不出来是在回调函数中设置断点就不起作用还是由于自己程序错误根本没有执行到回调函数的原因导致的,所以求指教! **附上代码如下:** MainActivity.class ``` public class MainActivity extends Activity implements OnClickListener { private Button startService; private Button stopService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startService = (Button) findViewById(R.id.start_service); stopService = (Button)findViewById(R.id.stop_service); startService.setOnClickListener(this); stopService.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.start_service: Log.d("MyService", "From Activity beefore startService executed"); Intent startIntent = new Intent(this, MyService.class); startService(startIntent); Log.d("MyService", "From Activity after startService executed"); break; case R.id.stop_service: Intent stopIntent = new Intent(this, MyService.class); stopService(stopIntent); break; default: break; } } } ``` MyService.class ``` public class MyService extends Service { @Override public IBinder onBind(Intent intent){ return null; } @Override public void onCreate() { super.onCreate(); Log.d("MyService", "onCreate executed"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d("MyService", "onStartCommand exetuted"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { Log.d("MyService", "onDestroy"); super.onDestroy(); } } ``` Manifest.xml ``` <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.servicetest"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <Service android:name=".MyService"> </Service> </application> </manifest> ``` 调试过程中Logcat输出结果图: ![图片说明](https://img-ask.csdn.net/upload/201612/01/1480588205_927466.png) 问答

安卓广播启动Service失败的问题

12-30

各位大侠好,我的Service可以在主线程启动,输出显示只执行了 onCreate 方法,Service 的 onStart 有一条删除线。但是如果用 BroatcastReceiver 来启动的话,onCreate 也没有输出,请问这是为什么呢?下面是代码: MyBroadcastReceiver.java ``` public class MyBroadcastReceiver extends BroadcastReceiver { private final String TAG = "MyBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { Log.v(TAG, context+""); Intent i = new Intent(context, NofyService.class); i.addCategory(Intent.CATEGORY_DEFAULT); //context.startService(i); Log.v(TAG, context.startService(intent)+""); Log.v(TAG, "Receiv"); } } ``` NofyService.java ``` public class NofyService extends Service { private final String TAG = "NofyService"; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { Log.v(TAG, "onCreate"); super.onCreate(); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Log.v(TAG, "onstart"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } } ``` AndroidManifest.xml ``` <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cj_finger.dyfin"> <permission android:protectionLevel="normal" android:name=".service.NofyService"></permission> <!-- 权限 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <!-- 首页 --> <activity android:name=".activity.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 新建日程表 --> <activity android:name=".activity.NewActivity"> </activity> <!-- 编辑日程表 --> <activity android:name=".activity.EditActivity"> </activity> <!-- 接收广播 --> <receiver android:name=".broadcastReceiver.MyBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.MY_BROADCAST"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <!-- 服务 --> <service android:permission=".service.NofyService" android:enabled="true" android:name=".service.NofyService" android:process="com.cj_finger.dyfinService" > <intent-filter> <action android:name=".service.NofyService"/> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service> </application> </manifest> ``` 问答

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

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览