web-dev-qa-db-ja.com

バックグラウンドサービスを継続的に実行する

最小バージョン19のSDKバージョン27をターゲットにし、バックグラウンドで継続的に実行されるサービスを取得しようとしています。別のサービス開始オプションを試しましたが、それでもアプリで強制終了されました。 BroadcastReceiverを使用して、サービスが停止したときにサービスを開始しようとしましたが、アプリがバックグラウンドにあり、サービスを開始できないというエラーが表示されたため、JobSchedulerを使用してみましたが、同じエラーが発生しました。これはどのように行われるのですか?たとえば、歩数計アプリを作成している場合、どうすればバックグラウンドで実行し続けることができますか?

15
CaseyB

OreoリリースではAndroid defined バックグラウンドサービスの制限

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

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

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

フォアグラウンドサービスを作成します。サービスの実行中にユーザーへの通知を配置します。 この回答を参照 (他にもたくさんあります)

ここで、サービスの通知が必要ない場合はどうでしょう。解決策はそのためです。

サービスを開始する定期的なタスクを作成できます。サービスは作業を行い、それ自体を停止します。これにより、アプリはバッテリーの消耗とはみなされません。

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

  • それぞれの長所と短所を伝える代わりに。私はあなたに最高を伝えます。 作業マネージャーが最適定期的なタスクのソリューション。 Android Architecture Component で導入されました。
  • Job-Scheduler(21を超えるAPIのみ)とは異なり、すべてのバージョンで機能します。
  • また、 Doze-Standby mode の後に作業を開始します。
  • デバイスの起動後にサービスをスケジュールするために Android Boot Receiver を作成します。

Work-Managerで永久に実行されるサービスを作成しました。これは完全に機能しています。

26
Khemraj

Android 8.0以降、多くのバックグラウンドサービスの制限が導入されました。

2つのソリューション:

  1. タスクと実行タイミングを完全に制御する必要がある場合は、Foreground Serviceを選択する必要があります。長所:アプリは動作していると見なされ、OSがアプリを強制終了してリソースを解放する可能性は低くなります。短所:ユーザーには常にフォアグラウンド通知が表示されます。

  2. 定期的にタスクをスケジュールする必要がある場合、Work Manager(Google I/O 18で導入)が最適なソリューションです。このコンポーネントは、可能な限り最適なスケジューラー(Jobscheduler、JobDispatcher、AlarmManager ..)を選択します。ワークマネージャーAPIは、保証された実行が必要なタスクに対してのみ有用であり、遅延可能であることに注意してください。参照: Android Dev Documentation

2

BroadcastRecieverを使用してbackgrouundサービスを継続的に実行できますが、killされる場合、古いサービスインスタンスを自動的に再インスタンス化します。サービスが強制的に停止すると、onDestroy()メソッドを呼び出します。サービスを破棄して再起動します。以下のメソッドcom.Android.appはBroadcastRecieverを拡張する受信者クラスのカスタムアクションです

public void onDestroy() {
    try {
        myTimer.cancel();
        timerTask.cancel();
    } catch (Exception e) {
        e.printStackTrace();
    }
    Intent intent = new Intent("com.Android.app");
    intent.putExtra("valueone", "tostoreagain");
    sendBroadcast(intent);
}

およびonReceiveメソッド

 @Override
public void onReceive(Context context, Intent intent) {
    Log.i("Service Stoped", "call service again");
    context.startService(new Intent(context, ServiceCheckWork.class));
}

デバイスが再起動された場合、レシーバーがキャッチするonBootCompletedアクションがあります

SdkVersion "O"をターゲットにしている場合

MainActivity.JavaでgetPendingIntent()を定義します

private PendingIntent getPendingIntent() {
  Intent intent = new Intent(this, YourBroadcastReceiver.class);
 intent.setAction(YourBroadcastReceiver.ACTION_PROCESS_UPDATES);
 return PendingIntent.getBroadcast(this, 0, intent, 
  PendingIntent.FLAG_UPDATE_CURRENT);
 }

ここでは、BroadcastReceiverでPendingIntentを使用します。このBroadcastReceiverは、AndroidManifest.xmlで既に定義されています。 onReceive()メソッドを含むYourBroadcastReceiver.Javaクラスになりました:

 Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
   final String action = intent.getAction();
   if (ACTION_PROCESS_UPDATES.equals(action)) {
       NotificationResult result = NotificationResult.extractResult(intent);
       if (result != null) {
           List<Notification> notifications = result.getNotification();
           NotificationResultHelper notificationResultHelper = new 
   NotificationResultHelper(
                   context, notifications);
           // Save the notification data to SharedPreferences.
           notificationResultHelper.saveResults();
           // Show notification with the notification data.
           notificationResultHelper.showNotification();
           Log.i(TAG, 
NotificationResultHelper.getSavedNotificationResult(context));
       }
   }
 }
}
0
user10041244

私が提案する唯一の解決策は、Firebase Cloud Messagesを使用することです。またはフォアグラウンドサービス。

0
Vyacheslav

このための実用的なハックは、ほんの一瞬だけ表示されるフォアグラウンドサービスを開始し、バックグラウンドサービスを開始することです。バックグラウンドサービスでは、フォアグラウンドサービスを定期的に開始します。
例をあげる前に、これがあなたに向いている方法かどうかを本当に自問する必要があります。与えられた問題に対する他の解決策があるかもしれません(JobIntentServiceの使用など)。これはハックであり、しばらくパッチが適用される可能性があり、通常は使用しないことに注意してください(ただし、画面をオフにしてバッテリー節約を有効にしてテストしましたが、ずっと生き続けましたが、これはあなたを妨げる可能性があります居眠りからデバイス..再び、これは汚いハックです!)

例:

public class TemporaryForegroundService extends Service {
public static final int NOTIFICATION_ID = 666;
private static Notification notification;

@Override
public void onCreate() {
    super.onCreate();
    if(notification == null)
        notification = new NotificationCompat.Builder(this, NotificationChannels.importantChannel(this)).
                setSmallIcon(R.mipmap.ic_launcher).setContentTitle("The unseen blade").setContentText("If you see me, congrats to you.").build();
    startForeground(NOTIFICATION_ID, notification);
    startService(new Intent(this, PermanentBackgroundService.class));
    stopForeground(true);
    stopSelf();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return START_NOT_STICKY;
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}
}



public class PermanentBackgroundService extends Service {
    private Runnable keepAliveRunnable = new Runnable() {
        @Override
        public void run() {
            keepServiceAlive();
            if(handler != null) handler.postDelayed(this, 15*1000);
        }
    };
    private Handler handler;

    public void onCreate(){
        handler = new Handler();
        handler.postDelayed(keepAliveRunnable, 30* 1000);
    }

    public void onDestroy() {
        super.onDestroy();
        keepServiceAlive();
    }

    private void keepServiceAlive() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(new Intent(PermanentBackgroundService.this, TemporaryForegroundService .class));
        } else {
            startService(new Intent(PermanentBackgroundService.this, TemporaryForegroundService .class));
        }
    }
}
0
Ch4t4r

あなたが言うように:

BroadcastReceiverを使用して、サービスが停止したときにサービスを開始しようとしましたが、アプリがバックグラウンドにあり、サービスを開始できないというエラーが表示されました

あなたがバックグラウンドにいて、あなたがそのサービスがフォアグラウンドサービスでなければならないサービスを開始したいとき、オレオでこのコードを使用します:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(intent);
} else {
            context.startService(intent);
}

oreoでこのコードを使用する場合、onStartCommandで数秒間フォアグラウンドを開始します。それ以外の場合、サービスは応答しないと見なされ、ユーザーによって強制的に閉じることがあります(Android 8以上)

終了後にサービスを開始するためにBroadcastReceiverを使用する必要はありません。サービスのonStartCommandからSTART_STICKYまたはSTART_REDELIVER_INTENTを返すだけでサービスを再開できます閉まっている

0
Bahman