Android 8.0:java.lang.IllegalStateException:不允许启动服务Intent

phn*_*mnn 321 android android-service android-8.0-oreo

在应用程序启动时,应用程序启动应该执行某些网络任务的服务.在针对API级别26后,我的应用程序无法在后台启动Android 8.0上的服务.

引起:java.lang.IllegalStateException:不允许启动服务Intent {cmp = my.app.tt/com.my.service}:app在后台uid UidRecord {90372b1 u0a136 CEM空闲过程:1 seq(0,0) ,0)}

据我所知,它涉及: 后台执行限制

如果针对Android 8.0的应用程序在不允许创建后台服务的情况下尝试使用该方法,则startService()方法现在会抛出IllegalStateException.

" 在不允许的情况下 " - 它实际意味着什么?以及如何解决它.我不想把我的服务设置为"前景"

Sag*_*cha 213

我得到了解决方案 对于8.0之前的设备,您只需使用startService(),但对于7.0之后的设备,您必须使用startForgroundService().以下是启动服务的代码示例.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(new Intent(context, ServedService.class));
    } else {
        context.startService(new Intent(context, ServedService.class));
    }
Run Code Online (Sandbox Code Playgroud)

在服务类中,请添加以下代码以获取通知:

@Override
public void onCreate() {
    super.onCreate();
    startForeground(1,new Notification());
}
Run Code Online (Sandbox Code Playgroud)

其中O是Android版本26.

希望它会解决 startService()

  • 支持lib中有一个`ContextCompat.startForegroundService(...)`可以代替使用. (72认同)
  • 那不是解决方案. (33认同)
  • 我也同意这不是一个解决方案.这是一种解决方法,它有所帮助,但奥利奥的背景限制是出于某种原因而引入的.以这种方式绕过这些限制绝对不是正确的方法(即使它有效).最好的方法是使用JobScheduler(参考接受的答案). (17认同)
  • 前台服务是用户将意识到并需要通知的东西.如果运行时间太长,它也会是ANR.因此,如果应用程序已在后台运行,那么它并不是一个合适的答案. (8认同)
  • 如果您必须显示空前台通知,我认为这不会是一个很好的用户体验.考虑到你必须这样做的事实. - Android 8.0引入了新方法startForegroundService()以在前台启动新服务.系统创建服务后,应用程序有五秒钟的时间来调用服务的startForeground()方法来显示新服务的用户可见通知.如果应用程序未在时间限制内调用startForeground(),系统将停止该服务并将该应用程序声明为ANR. (5认同)
  • 似乎`new Notificaction()`在Android 8.1中不起作用; 您应该创建通知通道:stackoverflow.com/a/47533338/1048087 (2认同)
  • 此外,从文档:应用程序定位API Build.VERSION_CODES.P或更高版本必须请求权限Manifest.permission.FOREGROUND_SERVICE才能使用此API. (2认同)
  • 我是否可以检查应用程序是否在前台?如果没有,不要启动服务? (2认同)

Mur*_*göz 172

允许的情况是临时白名单,其中后台服务的行为与Android O之前相同.

在某些情况下,后台应用程序会被放置在临时白名单上几分钟.虽然应用程序位于白名单中,但它可以无限制地启动服务,并允许其后台服务运行.当应用程序处理用户可见的任务时,应用程序将放置在白名单中,例如:

  • 处理高优先级的Firebase云消息传递(FCM)消息.
  • 接收广播,例如SMS/MMS消息.
  • 从通知执行PendingIntent.
  • 在VPN应用程序将自身提升到前台之前启动VpnService.

资料来源:https://developer.android.com/about/versions/oreo/background.html

换句话说,如果您的后台服务不符合白名单要求,则必须使用新的JobScheduler.它与后台服务基本相同,但它会定期调用,而不是连续在后台运行.

如果您使用的是IntentService,则可以更改为JobIntentService.请参阅下面的 @ kosev的答案.

  • 您是否应该使用WorkManager(在这里:https://developer.android.com/topic/libraries/architecture/workmanager)代替JobScheduler或其他工具?我的意思是:https://youtu.be/IrKoBFLwTN0 (4认同)
  • `FirebaseInstanceIdService`及其`onTokenRefresh`方法是高优先级FCM消息吗? (2认同)

kos*_*sev 75

最好的方法是使用JobIntentService,它使用新的JobScheduler for Oreo或旧的服务(如果不可用).

在你的清单中声明:

<service android:name=".YourService"
         android:permission="android.permission.BIND_JOB_SERVICE"/>
Run Code Online (Sandbox Code Playgroud)

在您的服务中,您必须使用onHandleWork替换onHandleIntent:

public class YourService extends JobIntentService {

    public static final int JOB_ID = 1;

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, YourService.class, JOB_ID, work);
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        // your code
    }

}
Run Code Online (Sandbox Code Playgroud)

然后你开始服务:

YourService.enqueueWork(context, new Intent());
Run Code Online (Sandbox Code Playgroud)

  • 您将在哪里调用YourService.enqueueWork(context,new Intent()); ?从广播接收器? (2认同)

小智 34

如果该服务在后台线程通过延长运行IntentService,可以取代IntentServiceJobIntentService被设置为支持Android库的一部分

使用的优点JobIntentService是,它表现为IntentService预O设备,O和更高,它作为一个工作发送它

JobScheduler也可用于定期/随需工作.但是,请确保处理向后兼容性,因为JobSchedulerAPI仅可从API 21获得


小智 12

是的,那是因为你无法在API 26中在后台启动服务.所以你可以在API 26之上启动ForegroundService.

你必须使用

ContextCompat.startForegroundService(...)
Run Code Online (Sandbox Code Playgroud)

并在处理泄漏时发布通知.

  • OP 特别表示他不想作为前景。这应该作为评论或作为更完整答案的一部分。 (2认同)

Khe*_*raj 12

Oreo中, Android 为后台服务定义了限制

为了改善用户体验,Android 8.0(API级别26)对应用程序在后台运行时可以执行的操作施加了限制。

如果仍然需要始终运行的服务,则可以使用前台服务。

后台服务限制:当应用处于空闲状态时,其后台服务的使用受到限制。这不适用于前台服务,而前台服务对用户而言更为明显。

这样您就可以提供前台服务了。服务运行时,您需要向用户显示通知查看此答案(还有很多其他答案

一个解决方案,如果-

您不希望收到服务通知吗?

您可以执行以下定期任务:1.启动您的服务; 2.服务将完成其工作; 3.使其自身停止。如此一来,您的应用程式将不会被视为耗电。

您可以将定期任务与Alarm ManagerJob SchedulerEvernote-JobsWork Manager一起使用

我已经使用Work-Manager测试了永久运行的服务。


Dha*_*ani 6

firebase发行说明中,他们指出对Android O的支持是在10.2.1中首次发布的(尽管我建议使用最新版本)。

请为Android O添加新的Firebase消息传递依赖项

compile 'com.google.firebase:firebase-messaging:11.6.2'
Run Code Online (Sandbox Code Playgroud)

如有必要,升级Google Play服务和Google存储库。


TAL*_*ALE 6

我看到很多建议只使用 ForegroundService 的回复。为了使用 ForegroundService 必须有一个与之关联的通知。用户将看到此通知。根据情况,他们可能会对您的应用程序感到恼火并卸载它。

最简单的解决方案是使用名为 WorkManager 的新架构组件。您可以在此处查看文档:https : //developer.android.com/topic/libraries/architecture/workmanager/

您只需定义扩展 Worker 的工作类。

public class CompressWorker extends Worker {

    public CompressWorker(
        @NonNull Context context,
        @NonNull WorkerParameters params) {
        super(context, params);
    }

    @Override
    public Worker.Result doWork() {

        // Do the work here--in this case, compress the stored images.
        // In this example no parameters are passed; the task is
        // assumed to be "compress the whole library."
        myCompress();

        // Indicate success or failure with your return value:
        return Result.SUCCESS;

        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你安排你想要运行它的时间。

    OneTimeWorkRequest compressionWork = 
        new OneTimeWorkRequest.Builder(CompressWorker.class)
            .build();
    WorkManager.getInstance().enqueue(compressionWork);
Run Code Online (Sandbox Code Playgroud)

简单!有很多方法可以配置工作人员。它支持重复性工作,如果需要,您甚至可以执行诸如链接之类的复杂操作。希望这可以帮助。

  • 目前 WorkManager 仍然是 alpha 版。 (3认同)
  • 2019 年 3 月 5 日 - WorkManager 1.0.0 稳定版。 (3认同)

chi*_*nya 6

如果任何 Intent 之前在应用程序处于后台时工作正常,那么在 Android 8 及更高版本中将不再如此。仅指当应用程序在后台时必须进行一些处理的意图。

必须遵循以下步骤:

  1. 上面提到的意图应该使用JobIntentService而不是 IntentService.
  2. 扩展的类JobIntentService应该实现 -onHandleWork(@NonNull Intent intent)方法并且应该在方法下面,这将调用该onHandleWork方法:

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, xyz.class, 123, work);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. enqueueWork(Context, intent)从定义意图的类调用。

    示例代码:

    Public class A {
    ...
    ...
        Intent intent = new Intent(Context, B.class);
        //startService(intent); 
        B.enqueueWork(Context, intent);
    }
    
    Run Code Online (Sandbox Code Playgroud)

下面的类以前扩展了服务类

Public Class B extends JobIntentService{
...

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, B.class, JobId, work);
    }

    protected void onHandleWork(@NonNull Intent intent) {
        ...
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. com.android.support:support-compat需要JobIntentService- 我使用26.1.0 V.

  2. 最重要的是确保 Firebase 库版本至少为10.2.1,我遇到了问题10.2.0- 如果您有任何问题!

  3. 您的清单应该对 Service 类具有以下权限:

    service android:name=".B"
    android:exported="false"
    android:permission="android.permission.BIND_JOB_SERVICE"
    
    Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。


Ale*_*lev 6

正如@kosev在他的回答中所说,您可以使用JobIntentService。但是,我使用了另一种解决方案-我捕获了IllegalStateException并将服务启动为前台。例如,此功能启动我的服务:

@JvmStatic
protected fun startService(intentAction: String, serviceType: Class<*>, intentExtraSetup: (Intent) -> Unit) {
    val context = App.context
    val intent = Intent(context, serviceType)
    intent.action = intentAction
    intentExtraSetup(intent)
    intent.putExtra(NEED_FOREGROUND_KEY, false)

    try {
        context.startService(intent)
    }
    catch (ex: IllegalStateException) {
        intent.putExtra(NEED_FOREGROUND_KEY, true)
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(intent)
        }
        else {
            context.startService(intent)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当我处理意图时,我会做这样的事情:

override fun onHandleIntent(intent: Intent?) {
    val needToMoveToForeground = intent?.getBooleanExtra(NEED_FOREGROUND_KEY, false) ?: false
    if(needToMoveToForeground) {
        val notification = notificationService.createSyncServiceNotification()
        startForeground(notification.second, notification.first)

        isInForeground = true
    }

    intent?.let {
        getTask(it)?.process()
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 有一个潜在的错误 - 当 `context.startService(intent)` 被 `try` 块中的 `try/catch` 捕获时,同一代码行在 `catch` 块中再次重复,这可能会导致一场车祸。 (2认同)

Ans*_*507 6

使用 JobScheduler 的替代解决方案,它可以定期在后台启动服务。

首先创建名为Util.java 的

import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;

public class Util {
// schedule the start of the service every 10 - 30 seconds
public static void schedulerJob(Context context) {
    ComponentName serviceComponent = new ComponentName(context,TestJobService.class);
    JobInfo.Builder builder = new JobInfo.Builder(0,serviceComponent);
    builder.setMinimumLatency(1*1000);    // wait at least
    builder.setOverrideDeadline(3*1000);  //delay time
    builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);  // require unmetered network
    builder.setRequiresCharging(false);  // we don't care if the device is charging or not
    builder.setRequiresDeviceIdle(true); // device should be idle
    System.out.println("(scheduler Job");

    JobScheduler jobScheduler = null;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
        jobScheduler = context.getSystemService(JobScheduler.class);
    }
    jobScheduler.schedule(builder.build());
   }
  }
Run Code Online (Sandbox Code Playgroud)

然后,将 JobService 类命名为TestJobService.java

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.widget.Toast;
 
  /**
   * JobService to be scheduled by the JobScheduler.
   * start another service
   */ 
public class TestJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
    Util.schedulerJob(getApplicationContext()); // reschedule the job
    Toast.makeText(this, "Bg Service", Toast.LENGTH_SHORT).show();
    return true;
}

@Override
public boolean onStopJob(JobParameters params) {
    return true;
  }
 }
Run Code Online (Sandbox Code Playgroud)

在名为ServiceReceiver.java 的BroadCast Receiver 类之后

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

 public class ServiceReceiver extends BroadcastReceiver {
 @Override
public void onReceive(Context context, Intent intent) {
    Util.schedulerJob(context);
 }
}
Run Code Online (Sandbox Code Playgroud)

使用服务和接收器类代码更新清单文件

<receiver android:name=".ServiceReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    <service
        android:name=".TestJobService"
        android:label="Word service"
        android:permission="android.permission.BIND_JOB_SERVICE" >

    </service>
Run Code Online (Sandbox Code Playgroud)

将 main_intent 启动器留在默认创建的 mainActivity.java 文件中,MainActivity.java文件中的变化是

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Util.schedulerJob(getApplicationContext());
  }
 }
Run Code Online (Sandbox Code Playgroud)

哇哦!!后台服务在没有前台服务的情况下启动

[编辑]:您可以将工作管理器用于 Android 中的任何类型的后台任务。


归档时间:

查看次数:

157452 次

最近记录:

5 年,10 月 前