Android 启动前台服务的实现方法与注意事项

问题描述

Android R平台 收音机启动前台服务 startForegroundService() 报错

E AndroidRuntime: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{990dd99 u0 com.android.fmradio/.FmService}

问题分析

在Android O中,有一个新的背景限制。尝试启动startService()时,将获得IlleagalStateException,因此现在应使用startForegroundService(),但是如果通过此新方法启动服务,则会在屏幕截图中看到类似的错误。为避免此异常,您需要在startForegroundService()之后有5秒钟的时间来创建startForeground(),以通知用户您正在后台工作。否则将崩溃出现这样的崩溃信息。

使用方法

  1. 权限

启动前台服务需要添加如下权限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

这是一个自 Android 9.0 (API level 28) 引入的新权限,用于确保应用程序不会在未经用户许可的情况下创建前台服务。如果您的应用程序未声明此权限并尝试启动前台服务,则可能会引发 SecurityException 异常。

需注意,自Android O(API级别26)以后,仅仅在AndroidManifest.xml中声明权限是不够的,还必须在代码中请求权限。可以按以下方式检查和请求权限:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.FOREGROUND_SERVICE)
        != PackageManager.PERMISSION_GRANTED) {
    // Permission not yet granted, request it
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.FOREGROUND_SERVICE},
            MY_PERMISSIONS_REQUEST_FOREGROUND_SERVICE);
}
  1. 启动服务

在 Android 8.0 及以上版本,为了遵循后台限制策略,应用程序必须使用 startForegroundService() 方法来启动前台服务,而不能使用 startService()。

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    context.startForegroundService(intent);
} else {
    context.startService(intent);
}
  1. service服务中的Notification

在 API 26 及以上版本,首先需要创建一个 NotificationChannel 对象,并为其设置一些属性(例如声音、震动等)。然后用该 Channel 创建一个 Notification,并调用 startForeground() 方法将其赋给前台服务。这样,即使在设备处于 Doze 策略模式时,该服务仍能保持活动状态。在 API 26 以下版本,直接调用 startService() 方法即可正常启动服务。

值得注意的是,为了满足前台服务的要求,通知必须包含在 Notification 对象中。通知提供了有关服务当前状态和活动实例的信息,以便用户随时可以查看和监视该服务的运行情况。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
   // 创建一个 NotificationChannel  
   NotificationChannel channel = new NotificationChannel(channelId,channelName, NotificationManager.IMPORTANCE_DEFAULT); 
   // 自定义设置通知声音、震动等
   channel.enableVibration(true); 
   channel.setVibrationPattern(new long[]{100, 200}); 
   NotificationManager notificationManager = getSystemService(NotificationManager.class); 
   notificationManager.createNotificationChannel(channel);
   // 构建一个 Notification 
   Notification notification = new NotificationCompat.Builder(this, channelId) 
       .setContentTitle("Some Service") 
       .setContentText("服务正在运行...") 
       .setSmallIcon(R.drawable.ic_notification) 
       .build(); 
    // 启动前台服务 
    // 通知栏标识符 前台进程对象唯一SERVICE_ID
    startForeground(SERVICE_ID, notification);
} else {
    startService(intent); // API < 26 直接启动服务即可
}

注意事项

启动一个前台服务要做的事情有:

(1) 调用 ContextCompat.startForegroundService() 可以创建一个前台服务,相当于创建一个后台服务并将它推到前台;

(2) 创建一个用户可见的 Notification ;

(3) 必须在5秒内调用该服务的 startForeground(int id, Notification notification) 方法,否则将停止服务并抛出 android.app.RemoteServiceException:Context.startForegroundService() did not then call Service.startForeground()异常。

  • 如果在 5 秒内没有调用startForeground(),timeout 就会触发,会报出ANR
  • 一旦通过startForegroundService() 启动前台服务,必须在service 中有startForeground() 配套,不然会出现ANR 或者crash
  • startForeground() 中的id 和notification 不能为0 和 null

相关拓展

类别区别应用
前台服务会在通知一栏显示 ONGOING 的 Notification, 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务
后台服务默认的服务即为后台服务,即不会在通知一栏显示 ONGOING 的 Notification。 当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。

startForegroundService()startService() 是 Android 中用于启动服务的两种方法,它们之间的主要区别在于:

  1. API 级别:startForegroundService() 方法是在 Android O (API 级别 26) 引入的,而 startService() 方法则是基于早期版本的 API 设计的。

  2. 后台限制策略:为了避免系统和用户资源被滥用,Android 8.0(API 级别 26)以上版本中增加了一些针对后台服务的限制策略。在这些限制策略下,应用程序必须使用 startForegroundService() 方法来启动前台服务,而不能使用 startService()

  3. 前台通知:startForegroundService() 方法需要与 startForeground() 方法配合使用,以在状态栏中创建前台通知。前台通知可让用户明确知道应用正在执行某些操作,并防止系统将其进程或服务结束或处于 Doze 模式。而 startService() 则不会创建前台通知,因此系统可能会更轻易地终止其进程或服务。

因此,如果您计划启动一个长时间运行的服务或者希望保持服务在设备休眠时仍然运行,可以考虑使用 startForegroundService()startForeground() 方法。如果只是启动一个短暂的服务并且不需要该服务在设备休眠时仍然保持运行,则可以使用 startService() 方法。

需要注意的是,即使您使用了 startForegroundService() 方法,系统也会在必要时终止其进程或服务。因此,在设计应用程序时,应该考虑将服务设计为尽可能少消耗系统资源,并及时释放不必要的资源来避免出现资源过度占用等问题。

  • 9
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的Android前台服务示例代码: ``` public class MyForegroundService extends Service { private static final int NOTIFICATION_ID = 123; @Override public int onStartCommand(Intent intent, int flags, int startId) { // 创建通知 Notification notification = createNotification(); // 将服务设置为前台服务 startForeground(NOTIFICATION_ID, notification); // 执行耗时任务 doLongRunningTask(); // 返回 START_NOT_STICKY,因为我们不希望服务在意外终止后重新启动 return START_NOT_STICKY; } @Override public void onDestroy() { super.onDestroy(); // 停止前台服务 stopForeground(true); } private Notification createNotification() { // 创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("my_channel_id", "My Channel", NotificationManager.IMPORTANCE_DEFAULT); NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); } // 创建通知 NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "my_channel_id") .setSmallIcon(R.drawable.ic_notification) .setContentTitle("My Foreground Service") .setContentText("Running...") .setPriority(NotificationCompat.PRIORITY_DEFAULT); return builder.build(); } private void doLongRunningTask() { // 执行耗时任务 } @Nullable @Override public IBinder onBind(Intent intent) { return null; } } ``` 要启动服务,可以使用以下代码: ``` Intent intent = new Intent(this, MyForegroundService.class); startService(intent); ``` 注意,如果在 Android 10 及以上版本中启动前台服务,还需要申请 FOREGROUND_SERVICE 权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值