【安卓】Android兼容性优化-8.0之后禁止在后台启动服务的兼容性优化
前言
一、Android8.0之后IntentService启动异常跟踪
项目中在做启动优化时,在Application 通过IntentService启动第三方组件时,bugly时常会上报如下问题:
*android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground()*
# main(2)
android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground()
1 android.app.ActivityThread$H.handleMessage(ActivityThread.java:2056)
2 android.os.Handler.dispatchMessage(Handler.java:106)
3 android.os.Looper.loop(Looper.java:192)
4 android.app.ActivityThread.main(ActivityThread.java:6959)
5 java.lang.reflect.Method.invoke(Native Method)
6 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:557)
7 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:875)
1、Service和IntentService使用场景以及区别
Service的使用场景:
IntentService特点
==Android 8.0新增了startForegroundService方法,用于启动前台服务,前台服务是指带有通知栏的服务,如果我们使用startForegroundService启动服务,那么必须在5秒内调用startForeground()显示一个通知栏,否则就会报错==
2、明明调用startforeground了为什么还会报Context.startForegroundService() did not then call Service.startForeground()
首先看下启动IntentService的调用:
private void startInitService() {Intent intent = new Intent(this, InitIntentService.class);
intent.setAction("com.pxwx.student.action");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
}
在IntentService中的处理:
在onCreate方法中调用了startForeground方法
public class InitIntentService extends IntentService {public InitIntentService() {
super("InitIntentService");
}
@RequiresApi(api = Build.VERSION_CODES.O)
private Notification getNotification() {
NotificationChannel channel = new NotificationChannel("init", "", NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (manager != null) {
manager.createNotificationChannel(channel);
}
return new Notification.Builder(this, "init")
.setContentTitle("")
.setContentText("")
.setAutoCancel(true)
.setSmallIcon(com.pxwx.student.core.R.mipmap.small_icon)
.build();
}
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//notification ID must not be 0
startForeground(1,getNotification());
}
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
initThirdSDK();
}
onCreate方法中明明调用startforeground了为什么还会报Context.startForegroundService() did not then call Service.startForeground()?
分析:
1、启动IntentService服务在Application中执行了多次,IntentService在第二次启动时还未停止的话不知执行onCreate方法,但会走onStart()方法,所以在onStart()方法中也执行startforeground
@Overridepublic void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
//主要是针对后台保活的服务,如果在服务A运行期间,保活机制又startForegroundService启动了一次服务A,那么这样不会调用服务A的onCreate方法,只会调用onStart方法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(1,getNotification());
}
}
2、但是问题依旧存在,只是减少了该问题的发生,然后我又尝试了讲starttForeground方法放在了onHandleIntent中执行
@Overrideprotected void onHandleIntent(@Nullable Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(1,getNotification());
}
initThirdSDK();
}
3、但是问题依旧存在,只是又减少了该问题的发生,不能完全杜绝该问题的发生,具体分析下IntentService的特性:
3、 IntentService源码分析
1、首先看下IntentService的继承关系等声明信息:
public abstract class IntentService extends Service {...
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
可以看出IntentService是继承自Service的抽象类,有个抽象方法onHandleIntent需要子类覆写,通过注解我们知道该方法的执行是在子线程中的。
2、其次看下IntentService中声明的字段
//volatile修饰,保证其可见性//Service中子线程中的Looper对象
private volatile Looper mServiceLooper;
//与子线程中Looper关联的Hander对象
private volatile ServiceHandler mServiceHandler;
//与子线程HandlerThread相关的一个标识
private String mName;
//设置Service的标志位,根据它的值来设置onStartCommand的返回值
private boolean mRedelivery;
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
==mRedelivery是来处理onStartCommand返回值的一个标志位参数,着重看下onStartCommand的返回值在Service中定义的几个类型:==
public static final int START_CONTINUATION_MASK = 0xf;public static final int START_STICKY_COMPATIBILITY = 0;
public static final int START_STICKY = 1;
public static final int START_NOT_STICKY = 2;
public static final int START_REDELIVER_INTENT = 3;
根据这几个类型的注释,可以翻译解释:
3、上边关于mRedelivery值控制onStartCommand的返回值的问题:
4、总结分析:
==从IntentService的源码分析看,导致android.app.RemoteServiceException Context.startForegroundService() did not then call Service.startForeground():异常发生的原因:==
二、JobIntentService替代IntentService方案
综合上面的分析,没有能完全解决上面的异常情况,该如何解决呢?
通过IntentService源码中针对IntentService的部分注释如下:
* <p class="note"><b>Note:</b> IntentService is subject to all the* <a href="https://segmentfault.com/preview/features/background.html">background execution limits</a>
* imposed with Android 8.0 (API level 26). In most cases, you are better off
* using {@link android.support.v4.app.JobIntentService}, which uses jobs
* instead of services when running on Android 8.0 or higher.
* </p>
翻译一下:
1、JobIntentService介绍
JobIntentService是Android 8.0 新加入的类,它也是继承自Service,根据官方的解释:
大概翻译一下:
2、JobIntentService使用
1、在Manifest中声名Permission:
<uses-permission android:name="android.permission.WAKE_LOCK" />
2、在Manifest中声名Service:
<service android:name=".InitIntentService" android:permission="android.permission.BIND_JOB_SERVICE" />
3、实现JobIntentService类:
public class InitIntentService extends JobIntentService {public static final int JOB_ID = 1;
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, InitIntentService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
// 具体逻辑
}
}
4、调用启动实现JobIntentService
InitIntentService.enqueueWork(context, new Intent());
JobIntentService不需要关心JobIntentService的生命周期,不需要startService()方法,也就避免了开头中的crash问题,通过静态方法就可以启动,还是非常不错的。
关注我的技术公众号
以上是 【安卓】Android兼容性优化-8.0之后禁止在后台启动服务的兼容性优化 的全部内容, 来源链接: utcz.com/a/104121.html