Android Service 系列2. Android开机启动Service和“ did not then call Service.startForeground()”异常

本文目录

1 需求

2 安卓开机自启服务

2.1 定义广播接收器

2.2 声明

3 服务启动时出现的crash问题

3.1 原因分析

3.2 解决方案

3.2.1 添加权限

3.2.2 在广播接收器里根据安卓版本确定启动server的方式

3.2.3 创建服务后,需要在5秒内调用服务的 startForeground() 显示一条可见通知


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 原因分析

  1. Android 8.0 不再允许后台service直接通过startService方式去启动,否则就会引起IllegalStateException。
  2. 因此在Android 8.0 中,需要在前台启动新服务,即 Context.startForegroundService()
  3. 在系统创建服务后,应用有5秒的时间来调用该服务的 startForeground() 方法以显示新服务的用户可见通知。
  4. 如果应用在此时间限制内未调用 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);
 
}

 

  

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值