Android 8.0:java.lang.IllegalStateException:サービスインテントの開始は許可されていません


360

アプリケーションの起動時に、アプリはネットワークタスクを実行するサービスを開始します。APIレベル26をターゲットにした後、Android 8.0でアプリケーションがバックグラウンドでサービスを開始できません。

原因:java.lang.IllegalStateException:サービスの開始を許可されていませんIntent {cmp = my.app.tt / com.my.service}:アプリがバックグラウンドuid UidRecord {90372b1 u0a136 CEMアイドルprocs:1 seq(0,0 、0)}

私が理解しているように: バックグラウンド実行制限

startService()メソッドは、Android 8.0をターゲットとするアプリがバックグラウンドサービスの作成が許可されていない状況でそのメソッドを使用しようとすると、IllegalStateExceptionをスローするようになりました。

それが許可されていない状況で」- それは実際にはどういう意味ですか?そして、それを修正する方法。サービスを「フォアグラウンド」に設定したくない


4
これは、アプリがバックグラウンドにあるときはサービスを開始できないことを意味します
Tim

22
これはランタイムのアクセス許可とは関係ありません
Tim

10
startForegroundService()代わりに使用しますstartService()
frogatto 2017年

2
targetSdkVersion 25を使用して、compileSdkVersion 26でコンパイルすることができます。これにより、Android 8の新しいクラスと最新のサポートライブラリを使用できますが、アプリはバックグラウンド実行制限によって制限されません。
Kacper Dziubek

2
@KacperDziubekこれは機能するはずですが、2018
。– RightHandedMonkey

回答:


194

許可される状況は、バックグラウンドサービスがAndroid O以前と同じように動作する一時的なホワイトリストです。

特定の状況では、バックグラウンドアプリが一時的なホワイトリストに数分間置かれます。アプリはホワイトリストに登録されていますが、サービスを制限なく起動でき、バックグラウンドサービスの実行が許可されています。アプリは、ユーザーに表示される次のようなタスクを処理するときに、ホワイトリストに配置されます。

  • 優先度の高いFirebase Cloud Messaging(FCM)メッセージの処理。
  • SMS / MMSメッセージなどのブロードキャストを受信します。
  • 通知からPendingIntentを実行します。
  • VPNアプリがフォアグラウンドに昇格する前にVpnServiceを開始する。

ソース:https : //developer.android.com/about/versions/oreo/background.html

つまり、バックグラウンドサービスがホワイトリストの要件を満たしていない場合は、新しいJobSchedulerを使用する必要があります。。基本的にはバックグラウンドサービスと同じですが、バックグラウンドで継続的に実行されるのではなく、定期的に呼び出されます。

IntentServiceを使用している場合は、JobIntentServiceに変更できます。以下の @kosevの回答を参照してください。


「高」プリオのGCMメッセージを受信した直後にサービスを開始したいのですが、クラッシュします。私はまだGCMを使用しています:「com.google.firebase:firebase-messaging:11.4.2」ではなく、「com.google.android.gms:play-services-gcm:11.4.2」。わからないことは...けれども重要
アレックスRadzishevsky

「基本的にはバックグラウンドサービスと同じですが、バックグラウンドで継続的に実行されるのではなく、定期的に呼び出されます。」-Androidサービスが継続的に実行されたことがないため、これが何を意味するのかわかりません。起動、実行、シャットダウンします。
Melllvar 2017年

2
FirebaseInstanceIdService 、そのonTokenRefresh方法は、優先度の高いFCMメッセージは?
Cord Rehn 2017年

@phnmnnいいえ、GCMTaskServiceは実際にはFCMに準拠していないため、機能しません。
Abhinav Upadhyay

4
JobSchedulerなどの代わりにWorkManager(ここではdeveloper.android.com/topic/libraries/architecture/workmanager)を使用するべきではありませんか?つまり、youtu.be
Android開発者

256

私は解決策を得ました。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));
    }

また、サービスクラスで、通知用に以下のコードを追加してください。

@Override
public void onCreate() {
    super.onCreate();
    startForeground(1,new Notification());
}

OはAndroidバージョン26です。


9
フォアグラウンドサービスは、ユーザーが認識し、通知が必要なサービスです。実行時間が長すぎる場合もANRになります。そのため、アプリがすでにバックグラウンドで実行されている場合、これは実際には適切な答えではありません。
SimonH

80
あるContextCompat.startForegroundService(...)代わりに使用することができますサポートでlibに。
jayeffkay 2017

37
それは解決策ではありません。
JacksOnF1re

17
これは解決策ではないことにも同意します。これは回避策であり、役立ちますが、オレオのバックグラウンドの制限は理由のために導入されました。これらの制限をこの方法で回避することは、確かに正しいアプローチではありません(機能する場合でも)。最善の方法はJobSchedulerを使用することです(受け入れられた回答を参照してください)。
Vratislavジンドラ2018

6
空のフォアグラウンド通知を表示する必要がある場合、それは良いユーザーエクスペリエンスになるとは思いません。あなたがしなければならないという事実を考慮してください。-Android 8.0では、フォアグラウンドで新しいサービスを開始するための新しいメソッドstartForegroundService()が導入されています。システムがサービスを作成した後、アプリは5秒以内にサービスのstartForeground()メソッドを呼び出して、新しいサービスのユーザーに表示される通知を表示します。アプリが制限時間内にstartForeground()を呼び出さない場合、システムはサービスを停止し、アプリがANRであると宣言します。
heeleeaz 2018年

85

最善の方法は、JobIntentServiceを使用することです、Oreoの新しいJobSchedulerまたは使用できない場合は古いサービスを使用することです。

マニフェストで宣言します。

<service android:name=".YourService"
         android:permission="android.permission.BIND_JOB_SERVICE"/>

そしてあなたのサービスでは、onHandleIntentをonHandleWorkに置き換える必要があります:

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
    }

}

次に、サービスを次のように開始します。

YourService.enqueueWork(context, new Intent());


静的メソッド内で非静的メソッドを呼び出すにはどうすればよいですか?説明していただけますか?
マディ

@Maddy enqueueWork(...)も静的メソッドです。
hgoebl 2018年

2
YourService.enqueueWork(context、new Intent());をどこで呼び出しますか。?放送受信機から?
TheLearner、

これが最も簡単な解決策だとは思いません。WorkManagerに関する以下の私のコメントを参照してください。必要に応じてJobIntentServiceを使用しますが、ボイラープレートは大幅に少なくなっています。
TALE

36

サービスを拡張してバックグラウンドスレッドで実行している場合は、Androidサポートライブラリの一部として提供されているとIntentService置き換えることができます。IntentServiceJobIntentService

使用の利点JobIntentServiceは、IntentServiceOより前のデバイスとO以上で動作し、ジョブとしてディスパッチすることです。

JobScheduler定期的/オンデマンドのジョブにも使用できます。ただし、JobSchedulerAPIはAPI 21からのみ利用できるため、下位互換性を処理するようにしてください。


1
JobIntentServiceの問題は、Androidが作業を任意にスケジュールできることと、IntentServiceとは異なり、いじり回さずに暗黙的に開始できないことです。stackoverflow.com/questions/52479262/…を
キロカーン

15

Oreo Android では、バックグラウンドサービスの制限を定義しました。

ユーザーエクスペリエンスを改善するために、Android 8.0(APIレベル26)では、バックグラウンドで実行中にアプリが実行できる操作に制限が課されます。

それでも、常にサービスを実行する必要がある場合は、フォアグラウンドサービスを使用できます。

バックグラウンドサービスの制限:アプリがアイドル状態の間、バックグラウンドサービスの使用には制限があります。これは、ユーザーにとってより目立つフォアグラウンドサービスには適用されません。

したがって、フォアグラウンドサービスを作成できます。サービスの実行中にユーザー通知を表示する必要があります。この回答を見る(他にもたくさんあります)

解決策-

あなたのサービスの通知をしたくないですか?

定期的なタスクを作成できます。1。サービスを開始します。2。サービスが機能し、3。自分自身を停止します。これにより、アプリはバッテリーの消耗とは見なされなくなります。

アラームマネージャージョブスケジューラEvernoteジョブワークマネージャーで定期的なタスクを使用できます。

私はWork-Managerを使用して永久に実行されるサービスをテストしました。


WorkManagerは、作業をすぐに実行する必要がないとすれば、最善の方法のようです。API 14との下位互換性があります。API23以上のデバイスでJobSchedulerを使用し、API 14〜22のデバイスでBroadcastReceiver + AlarmManagerの組み合わせを使用します
James Allen

WorkManagerの重要な点は、WorkManagerが延期可能なタスク(つまり、すぐに実行する必要がないタスク)を対象としていることです
touhid udoy

13

ええ、API 26ではバックグラウンドでサービスを開始できなくなったためです。そのため、API 26の上でForegroundServiceを開始できます。

使用する必要があります

ContextCompat.startForegroundService(...)

リークの処理中に通知を投稿します。


1
OPは特に、フォアグラウンドにしたくないと述べました。これは、コメントとして、またはより完全な回答の一部として含める必要があります。
Ricardo A.

7

@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)
        }
    }
}

そして私がインテントを処理するとき、私はそのようなことをします:

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()
    }
}

私はあなたのトライキャッチソリューションが好きです。時にはので、私にとってはそれが解決策であるcontext.startServiceだけで最良の方法のように、このルックスは、そうでない場合、あなたのメインクラスでより多くのコードを実装する必要があります-時々ない-バックグラウンドで動作extending Applicationし、implementing ActivityLifecycleCallbacksアプリがフォアグラウンドまたはバックグラウンドにあるかどうかを追跡し、あなたの意図を開始しますそれに応じて。
ピエール

この例外をキャッチできますか?
thecr0w

5

firebaseリリースノート Android Oのサポートは10.2.1で最初にリリースされたと述べてい(ただし、最新バージョンを使用することをお勧めします)。

Android Oの新しいFirebaseメッセージング依存関係を追加してください

compile 'com.google.firebase:firebase-messaging:11.6.2'

必要に応じて、Google Play ServicesとGoogleリポジトリをアップグレードします。


これは質問に答えることも、質問がfirebaseに関係することもありません。コメントとして入れてください。
Ricardo A.

5

アプリがバックグラウンドで動作しているときに、以前は意図的に問題がなかった場合、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);
    }
  3. enqueueWork(Context, intent)インテントが定義されているクラスから呼び出します。

    サンプルコード:

    Public class A {
    ...
    ...
        Intent intent = new Intent(Context, B.class);
        //startService(intent); 
        B.enqueueWork(Context, intent);
    }

以下のクラスは以前にServiceクラスを拡張していました

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) {
        ...
        ...
    }
}
  1. com.android.support:support-compatJobIntentService- が必要です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"

お役に立てれば。


4

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.)
    }
}

次に、いつ実行するかをスケジュールします。

    OneTimeWorkRequest compressionWork = 
        new OneTimeWorkRequest.Builder(CompressWorker.class)
            .build();
    WorkManager.getInstance().enqueue(compressionWork);

かんたん!ワーカーを構成する方法はたくさんあります。定期的なジョブをサポートし、必要に応じてチェーンのような複雑なこともできます。お役に立てれば。


3
現在、WorkManagerはまだアルファ版です。
pzulw

3
2019年3月5日-WorkManager 1.0.0安定版リリース。
phnmnn

interserviceやJobIntentServiceを使用する代わりにWorkManagerを使用する必要があります
sivaBE35

1
WorkManager is intended for tasks that are deferrable—that is, not required to run immediately...最も簡単かもしれませんが、私のアプリには、ユーザーのリクエストをすぐに実行するバックグラウンドサービスが必要です。
誰か

タスクをすぐに完了する必要がある場合は、フォアグラウンドサービスを使用する必要があります。ユーザーに通知が表示され、あなたが仕事をしていることがわかります。何を使うかを決めるのに助けが必要な場合は、ドキュメントをチェックしてください。彼らはバックグラウンド処理のためのかなり良いガイドを持っています。 developer.android.com/guide/background
TALE

4

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());
   }
  }

次に、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;
  }
 }

その後、ServiceReceiver.javaという名前のBroadCastレシーバークラス

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);
 }
}

サービスとレシーバークラスコードでマニフェストファイルを更新する

<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>

main_intentランチャーを、デフォルトで作成されるmainActivity.javaファイルに残し、 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());
  }
 }

うーん!フォアグラウンドサービスなしでバックグラウンドサービスが開始します


2

8.0でコードを実行している場合、アプリケーションはクラッシュします。したがって、フォアグラウンドでサービスを開始します。8.0未満の場合、これを使用します。

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
context.startService(serviceIntent);

上記または8.0の場合、これを使用します。

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
ContextCompat.startForegroundService(context, serviceIntent );

ユーザーがサービスが実行されていることを認識する必要がある場合にのみ、フォアグラウンドサービスを使用することをお勧めします。典型的な例は、バックグラウンドで音楽を再生する場合です。他にも意味のあるケースがありますが、すべてのサービスをフォアグラウンドサービスに変換するだけではいけません。バックグラウンドでいくつかの作業を行うだけで実行が保証される場合は、サービスを変換してGoogleのアーキテクチャコンポーネントのWorkManagerを使用することを検討してください。
TALE

startForegroundServiceには許可が必要java.lang.SecurityException: Permission Denial: startForeground from pid=13708, uid=10087 requires android.permission.FOREGROUND_SERVICEです。それ以外の場合は許可が必要です。stackoverflow.com/a/52382711/550471で
誰かが

1

Firebaseメッセージングのプッシュ通知を統合している場合、

バックグラウンド実行制限のため、android O(Android 8.0)の新規/更新のfirebaseメッセージング依存関係を追加します。

compile 'com.google.firebase:firebase-messaging:11.4.0'

必要に応じて、Google Play ServicesとGoogleリポジトリをアップグレードします。

更新:

 compile 'com.google.firebase:firebase-messaging:11.4.2'

0

使用startForegroundService()の代わりにstartService() して作成することを忘れないでくださいstartForeground(1,new Notification());サービス開始5秒以内にあなたのサービスで。


2
新しいNotificaction()がAndroid 8.1から機能しないようです。通知用のチャネルを作成する必要があります:stackoverflow.com/a/47533338/1048087
Prizoff

0

この回答に対する物議を醸す投票のため(この編集の時点で+ 4 / -4)、最初に他の回答を調べて、これを最後の手段としてのみ使用してください。これをrootとして実行するネットワーキングアプリに1回だけ使用しましこのソリューションは通常の状況では使用すべきではないという一般的な意見に同意します。

以下の元の答え:

他の答えはすべて正解ですが、これを回避する別の方法は、ユーザーにアプリのバッテリーの最適化を無効にするよう依頼することです(アプリがシステム関連でない限り、これは通常良い考えではありません)。Google Playでアプリを禁止せずに、バッテリーの最適化をオプトアウトするようリクエストする方法については、この回答を参照してください。

次の方法で、クラッシュを防ぐためにレシーバーでバッテリーの最適化がオフになっているかどうかも確認する必要があります。

if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
        ?.isIgnoringBatteryOptimizations(packageName) != false) {
    startService(Intent(context, MyService::class.java))
} // else calling startService will result in crash

1
可能な限り多くのバッテリーを使用してフリーパスを許可するようユーザーに依頼することは、良い解決策ではありません。コードをよりバッテリーにやさしいソリューションに変換することを検討してください。あなたのユーザーはあなたに感謝します。
TALE 2018年

5
@TALE JobSchedulerなどを使用して、すべてのバックグラウンドサービスをバッテリーフレンドリーにすることができるわけではありません。一部のアプリは、通常の同期アプリケーションよりも低いレベルで動作する必要があります。これが機能しない場合の代替ソリューションです。
Mygod

-17

onStartCommandでは使用しないでください。

return START_NOT_STICKY

それを次のように変更してください:

return START_STICKY

そしてそれはうまくいきます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.