本文目录
1 需求
项目需要Android设备开机后,自动启动一个后台服务。
在实现过程中发现,安卓版本在8.0之后,在启动服务时会频繁出现下述异常:
“Context.startForegroundService() did not then call Service.startForeground()”。
下面依次讲述如何实现Android开机自启服务和异常处理的方法。
2 安卓开机自启服务
实现思路:定义广播接收器,在android系统开机时,后台启动服务。
2.1 定义广播接收器
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 检查是否是开机广播
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Log.v("BootReceiver","BootReceiver");
//开启服务
Intent intent = new Intent(context, TestService.class );
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//需要增加,否则会报错
context.startService(intent);
}
}
2.2 声明
在AndroidManifest.xml里声明广播接收器。
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
3 服务启动时出现的crash问题
3.1 原因分析
- Android 8.0 不再允许后台service直接通过startService方式去启动,否则就会引起IllegalStateException。
- 因此在Android 8.0 中,需要在前台启动新服务,即 Context.startForegroundService()。
- 在系统创建服务后,应用有5秒的时间来调用该服务的 startForeground() 方法以显示新服务的用户可见通知。
- 如果应用在此时间限制内未调用 startForeground(),则系统将停止服务并声明此应用为 ANR。
3.2 解决方案
3.2.1 添加权限
<!--android 9.0上使用前台服务,需要添加权限,此权限为级别为nomarl-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
3.2.2 在广播接收器里根据安卓版本确定启动server的方式
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 检查是否是开机广播
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Log.v("BootReceiver","BootReceiver");
//开启服务
Intent intent = new Intent(context, TestService.class );
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//需要增加,否则会报错
context.startService(intent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//安卓8.0之后
context.startForegroundService(new Intent(context, TestService.class));
} else {
context.startService(new Intent(context, TestService.class));
}
}
}
3.2.3 创建服务后,需要在5秒内调用服务的 startForeground() 显示一条可见通知
在Server中onStartCommand()方法中调用startForeground(),声明有服务在挂着,否则系统会停止服务,提示异常。
public static final String CHANNEL_ID_STRING = "id";
public static final String CHANNEL_NAME_STRING = "name";
private Notification notification;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//适配8.0service
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel mChannel = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
mChannel = new NotificationChannel(CHANNEL_ID_STRING, CHANNEL_NAME_STRING, NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(mChannel);
notification = new Notification.Builder(getApplicationContext(), CHANNEL_ID_STRING).build();
startForeground(1, notification);
}
//其他初始化操作...
return super.onStartCommand(intent, flags, startId);
}