web-dev-qa-db-ja.com

Android 8.0:Java.lang.IllegalStateException:サービスの開始を許可されていません

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

原因:Java.lang.IllegalStateException: service Intent { cmp = my.app.tt/com.my.service }の起動は許可されていません:アプリはバックグラウンドにありますuid UidRecord {90372b1 u0a136 CEMアイドルプロシージャ:1 seq(0,0,0)}

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

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

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

217
phnmnn

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

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

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

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

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

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

125
Murat Karagöz

私は解決策を得ました。 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です。

それがIllegalArgumentExceptionを解決することを願っています

140
Sagar Kacha

最善の方法は 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());
45
kosev

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

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

JobSchedulerは定期的またはオンデマンドの仕事にも使用できます。ただし、JobScheduler APIはAPI 21からのみ使用可能であるため、下位互換性を確実に処理してください。

26
Harini S

ええ、それはあなたがもはやAPI 26上でバックグラウンドでサービスを開始することができないからです。だからあなたはAPI 26上でForegroundServiceを開始することができます。

あなたが使用する必要があります 

ContextCompat.startForegroundService(...)

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

11
P.K

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

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

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

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

4
Dhaval Jivani

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

簡単です!ワーカーを設定する方法はたくさんあります。それは定期的な仕事をサポートします、そして、あなたがそれを必要とするならば、あなたは連鎖のような複雑なことさえすることができます。お役に立てれば。

2
TALE

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

私が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()
    }
}
2
Alex Shevelev

firebaseメッセージングのプッシュ通知が統合されている場合は、 

バックグラウンド実行制限 により、Android O(Android 8.0)用の新しい/更新firebaseメッセージング依存関係を追加.

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

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

更新:

 compile 'com.google.firebase:firebase-messaging:11.4.2'
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 );
2
Faizy

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

1
Arpit

JobSchedulerを使用した代替ソリューションでは、一定の時間間隔でバックグラウンドでサービスを開始できます。

最初にtil.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());
   }
  }

次に、TestJobService.Javaという名前のJobServiceクラスを作成します

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

マニフェストファイルをサービスおよび受信機のクラスコードで更新

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

デフォルトで作成されるmainActivity.Javaファイルへのmain_intentランチャーを残し、MainActivity.Javaに変更はありません

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

1
Anshul1507

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

ユーザーエクスペリエンスを向上させるために、Android 8.0(APIレベル26)では、バックグラウンドでの実行中にアプリが実行できることに制限が課されています。

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

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

それで フォアグラウンドサービス を作ることができます。あなたのサービスが実行されているときあなたは 通知をuser に表示する必要があるでしょう。 この回答を見てください (他にもたくさんあります)

場合の解決策 -

あなたはあなたのサービスのために通知を望みませんか?

あなたは定期的な仕事をすることができます。これによってあなたのアプリはバッテリーの消耗とは見なされません。

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

  • ワークマネージャが最適です 定期的なタスクの解決策。 Android Architecture Component で導入されました。 
  • Job-Scheduler(21を超えるAPI)とは異なり、すべてのバージョンで機能します。
  • Doze-Standbyモード の後にも動作を開始します。 
  • Android Boot Receiver を起動した後にサービスをスケジュールします。

Work-Managerを使って永遠にサービスを実行することをテストしました。

0
Khemraj

アプリがバックグラウンドで動作しているときに、以前に何らかの意図が正常に機能していたとしたら、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"
    

お役に立てれば。

0

私はこれを回避する最善の方法は実行時にビルドバージョンをチェックすることであり、それに応じて、それがApiレベル21より小さければ、startService()を使い続けます。 しかし、それより高い場合は、ジョブスケジューラを使用する必要があります。 作業管理者も使用できると思いますが、まだベータ段階です

0
Meisam