アプリケーションの起動時に、アプリはネットワークタスクを実行するために必要なサービスを開始します。 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をスローするようになりました。
" 許可されていない状況では " - それは実際にはどういう意味ですか?そしてそれをどう修正するか。自分のサービスを「フォアグラウンド」に設定したくない
許可された状況は、バックグラウンドサービスがAndroid O以前と同じように動作する一時的なホワイトリストです。
特定の状況下では、バックグラウンドアプリが一時的なホワイトリストに数分間配置されます。アプリがホワイトリストに登録されている間は、制限なくサービスを起動でき、バックグラウンドサービスの実行も許可されています。次のようなユーザーに表示されるタスクを処理すると、アプリはホワイトリストに配置されます。
- 優先度の高いFirebase Cloud Messaging(FCM)メッセージを処理します。
- SMS/MMSメッセージなどのブロードキャストを受信しています。
- 通知からPendingIntentを実行します。
- VPNアプリがフォアグラウンドに昇格する前にVpnServiceを起動する。
ソース: https://developer.Android.com/about/versions/oreo/background.html
つまり、バックグラウンドサービスがホワイトリストの要件を満たしていない場合は、新しい JobScheduler を使用する必要があります。基本的にはバックグラウンドサービスと同じですが、バックグラウンドで継続的に実行されるのではなく定期的に呼び出されます。
IntentServiceを使用している場合は、JobIntentServiceに変更できます。下記の@ kosevの 回答を参照してください 。
私は解決策を得ました。 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
を解決することを願っています
最善の方法は 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());
IntentService
を拡張してサービスがバックグラウンドスレッドで実行されている場合は、IntentService
をAndroidサポートライブラリの一部として提供されるJobIntentService
に置き換えることができます。
JobIntentService
を使用する利点は、O以前のデバイスとO以上のデバイスではIntentService
として動作し、ジョブとしてディスパッチすることです。
JobScheduler
は定期的またはオンデマンドの仕事にも使用できます。ただし、JobScheduler
APIはAPI 21からのみ使用可能であるため、下位互換性を確実に処理してください。
ええ、それはあなたがもはやAPI 26上でバックグラウンドでサービスを開始することができないからです。だからあなたはAPI 26上でForegroundServiceを開始することができます。
あなたが使用する必要があります
ContextCompat.startForegroundService(...)
リークの処理中に通知を投稿します。
firebaseリリースノート から、Android Oのサポートは最初に10.2.1でリリースされたと述べています(ただし、最新バージョンの使用を推奨します)。
android O用の新しいfirebaseメッセージングの依存関係を追加してください
compile 'com.google.firebase:firebase-messaging:11.6.2'
必要に応じて、Google PlayサービスとGoogleリポジトリをアップグレードします。
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);
簡単です!ワーカーを設定する方法はたくさんあります。それは定期的な仕事をサポートします、そして、あなたがそれを必要とするならば、あなたは連鎖のような複雑なことさえすることができます。お役に立てれば。
@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()
}
}
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'
コードを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 );
startForegroundService()
の代わりにstartService()
を使用し、サービスを開始してから5秒以内にstartForeground(1,new Notification());
をサービスに作成することを忘れないでください。
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 !!フォアグラウンドサービスなしでバックグラウンドサービスが開始されます
ユーザーエクスペリエンスを向上させるために、Android 8.0(APIレベル26)では、バックグラウンドでの実行中にアプリが実行できることに制限が課されています。
それでもサービスを実行する必要がある場合は、フォアグラウンドサービスを使用できます。
バックグラウンドサービスの制限事項: アプリがアイドル状態の間は、バックグラウンドサービスの使用に制限があります。これは、ユーザーにとってより目立つ前景サービスには適用されません。
それで フォアグラウンドサービス を作ることができます。あなたのサービスが実行されているときあなたは 通知をuser に表示する必要があるでしょう。 この回答を見てください (他にもたくさんあります)
場合の解決策 -
あなたは定期的な仕事をすることができます。これによってあなたのアプリはバッテリーの消耗とは見なされません。
アラームマネージャ 、 ジョブスケジューラ 、 /Evernote-Jobs または ワークマネージャ を使用して定期的なタスクを使用できます。
Work-Managerを使って永遠にサービスを実行することをテストしました。
アプリがバックグラウンドで動作しているときに、以前に何らかの意図が正常に機能していたとしたら、Android 8以降ではこれは不可能です。アプリがバックグラウンドにあるときに何らかの処理をしなければならないという意図だけを指す。
以下の手順に従う必要があります。
JobIntentService
の代わりにIntentService
を使用することです。JobIntentService
を拡張するクラスは - onHandleWork(@NonNull Intent intent)
メソッドを実装し、onHandleWork
メソッドを呼び出すメソッドの下に置く必要があります。
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, xyz.class, 123, work);
}
意図が定義されているクラスから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) {
...
...
}
}
com.Android.support:support-compat
はJobIntentService
に必要です - 私は26.1.0 V
を使います。
最も重要なのは、Firebaseライブラリのバージョンが少なくとも10.2.1
にあることを確認することです。10.2.0
に問題がありました。
あなたの積荷目録には、Serviceクラスに対する以下の許可が必要です。
service Android:name=".B"
Android:exported="false"
Android:permission="Android.permission.BIND_JOB_SERVICE"
お役に立てれば。
私はこれを回避する最善の方法は実行時にビルドバージョンをチェックすることであり、それに応じて、それがApiレベル21より小さければ、startService()を使い続けます。 しかし、それより高い場合は、ジョブスケジューラを使用する必要があります。 作業管理者も使用できると思いますが、まだベータ段階です