web-dev-qa-db-ja.com

Android:アプリが終了してもサービスを実行し続ける

アプリが強制終了された場合でも、IntentServiceをバックグラウンドで実行したままにします。そして「殺された」とはホームボタンを長押しする-> 実行中のすべてのアプリを見る-> アプリを脇にスワイプする-> アプリの強制終了 OR 長い間戻るボタンを押す-> アプリの強制終了

私のコードは次のようになります。私のMainActivityで:

Intent intent = new Intent(this, MyService.class);
this.startService(intent);

私のMyServiceで:

public class MyService extends IntentService {

@Override
protected void onHandleIntent(Intent intent) {
    System.out.println("MyService started");
    run();
}

private void run() {
    while (true){
        System.out.println("MyService still running");
        doSomething();
        waitSomeTime();
    }
}

}

アプリがopenのときにサービスが実行されていることがわかります。 アプリを最小化するホームボタンを使用して、まだ実行中です。私がアプリを閉じるバックボタンで実行しているときはまだ実行中です。しかし、上記のように殺すと停止します。これをどうやって解決しますか?

37
user2078872

サービスがアプリによって開始された場合、実際にはサービスはメインプロセスで実行されています。アプリが強制終了されると、サービスも停止します。できることは、次のようにサービスのonTaskRemovedメソッドからブロードキャストを送信することです。

 Intent intent = new Intent("com.Android.ServiceStopped");
 sendBroadcast(intent);

そして、再びサービスを開始する放送受信機を持っています。私はそれを試しました。サービスはすべての種類のキルから再起動します。

30
BhendiGawaar

すべての答えはcorrectのように見えるので、ここでcompleteの答えを出しましょう。

まず、あなたがしようとしていることを行う最も簡単な方法は、app killedが手動で定義されているときにAndroidでBroadcastを起動し、カスタムBroadcastReceiverは、それに続くサービスの再起動をトリガーします。

次に、コードに飛び込みましょう。


YourService.Javaでサービスを作成します

onCreate()メソッドに注意してください。このメソッドでは、Android Oreoより大きいBuildバージョンに対してforeground serviceを開始します。これは、最近導入された厳格な通知ポリシーにより、正しく表示するために独自の通知チャネルを定義する必要があります。

this.sendBroadcast(broadcastIntent);メソッドのonDestroy()は、アクション名"restartservice"で非同期的にブロードキャストを送信するステートメントです。これを後でサービスを再起動するトリガーとして使用します。

ここでは、単純なタイマータスクを定義しました。このタスクは、Log1秒ごとにカウンター値を出力し、印刷するたびに自身をインクリメントします。

public class YourService extends Service {
public int counter=0;

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
            startMyOwnForeground();
        else
            startForeground(1, new Notification());
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private void startMyOwnForeground()
    {
        String NOTIFICATION_CHANNEL_ID = "example.permanence";
        String channelName = "Background Service";
        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);

        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        assert manager != null;
        manager.createNotificationChannel(chan);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setContentTitle("App is running in background")
                .setPriority(NotificationManager.IMPORTANCE_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        startForeground(2, notification);
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        startTimer();
        return START_STICKY;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        stoptimertask();

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("restartservice");
        broadcastIntent.setClass(this, Restarter.class);
        this.sendBroadcast(broadcastIntent);
    }



    private Timer timer;
    private TimerTask timerTask;
    public void startTimer() {
        timer = new Timer();
        timerTask = new TimerTask() {
            public void run() {
                Log.i("Count", "=========  "+ (counter++));
            }
        };
        timer.schedule(timerTask, 1000, 1000); //
    }

    public void stoptimertask() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

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

Restarter.Javaでカスタム定義されたブロードキャストに応答するブロードキャストレシーバーを作成します

"restartservice"で定義したアクション名YourService.Javaのブロードキャストは、サービスを再起動するメソッドをトリガーするようになりました。これは、AndroidでBroadcastReceiverを使用して行われます。

BroadcastReceiverの組み込みonRecieve()メソッドをオーバーライドして、サービスを再起動するステートメントを追加します。 startService()は、意図したとおりに動作しません以上Android Oreo 8.1。アプリが起動すると、厳格なバックグラウンドポリシーが再起動後すぐにサービスを終了します。殺された。したがって、上位バージョンにはstartForegroundService()を使用し、継続的な通知を表示してサービスを実行し続けます。

public class Restarter extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("Broadcast Listened", "Service tried to stop");
        Toast.makeText(context, "Service restarted", Toast.LENGTH_SHORT).show();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(new Intent(context, YourService.class));
        } else {
            context.startService(new Intent(context, YourService.class));
        }
    }
}

アプリの開始時にサービスを呼び出すMainActivity.Javaを定義します。

ここで、個別のisMyServiceRunning()メソッドを定義して、バックグラウンドサービスの現在のステータスを確認します。サービスがnot実行中の場合、startService()を使用して開始します。

アプリはすでにフォアグラウンドで実行されているため、サービスがforeground serviceとして起動する必要はありません。

onDestroy()では、専用にstopService()を呼び出しているため、オーバーライドされたメソッドが呼び出されることに注意してください。これが行われなかった場合、YourService.Javaで変更されたonDestroy()メソッドを呼び出さずにアプリが強制終了された後、サービスは自動的に終了します

public class MainActivity extends AppCompatActivity {
    Intent mServiceIntent;
    private YourService mYourService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mYourService = new YourService();
        mServiceIntent = new Intent(this, mYourService.getClass());
        if (!isMyServiceRunning(mYourService.getClass())) {
            startService(mServiceIntent);
        }
    }

    private boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                Log.i ("Service status", "Running");
                return true;
            }
        }
        Log.i ("Service status", "Not running");
        return false;
    }


    @Override
    protected void onDestroy() {
        stopService(mServiceIntent);
        super.onDestroy();
    }
}

最後にAndroidManifest.xmlに登録します

上記の3つのクラスはすべて、AndroidManifest.xmlに個別に登録する必要があります。

アクション名intent-filter"restartservice"として定義していることに注意してください。ここでRestarter.Javareceiverとして登録されています。これにより、システムが特定のアクション名のブロードキャストを検出するたびに、カスタムBroadcastRecieverが呼び出されるようになります。

<application
    Android:allowBackup="true"
    Android:icon="@mipmap/ic_launcher"
    Android:label="@string/app_name"
    Android:supportsRtl="true"
    Android:theme="@style/AppTheme">

    <receiver
        Android:name="Restarter"
        Android:enabled="true"
        Android:exported="true">
        <intent-filter>
            <action Android:name="restartservice" />
        </intent-filter>
    </receiver>

    <activity Android:name="MainActivity">
        <intent-filter>
            <action Android:name="Android.intent.action.MAIN" />
            <category Android:name="Android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service
        Android:name="YourService"
        Android:enabled="true" >
    </service>
</application>

これで、アプリがタスクマネージャーから強制終了された場合、サービスが再起動されます。このサービスは、ユーザーがForce StopからApplication Settingsを実行しない限り、バックグラウンドで実行され続けます。

UPDATE:Dr.jacky に感謝します。上記の方法は、サービスのonDestroy()が呼び出された場合にのみ機能します。これはnotが特定の場合に該当する場合がありますが、私はそれを知りませんでした。ありがとう。

23
Sayan Sil

サービスに次のコードを追加します。

@Override
public void onTaskRemoved(Intent rootIntent){
    Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
    restartServiceIntent.setPackage(getPackageName());

    PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
    AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarmService.set(
    AlarmManager.ELAPSED_REALTIME,
    SystemClock.elapsedRealtime() + 1000,
    restartServicePendingIntent);

    super.onTaskRemoved(rootIntent);
 }
6
Iman Marashi

内部onstartコマンドput START_STICKY...このサービスは、あまりにも多くのタスクを実行しており、カーネルがそのためにそれを強制終了したい場合を除いて、強制終了しません...

@Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("LocalService", "Received start id " + startId + ": " + intent);
            // We want this service to continue running until it is explicitly
            // stopped, so return sticky.
            return START_STICKY;
        }
5
kiturk3

これは、IntentServiceを使用しようとしているためです。 API Docs の行は次のとおりです。

IntentServiceは次のことを行います。

すべての開始要求が処理された後にサービスを停止するため、stopSelf()を呼び出す必要はありません。

したがって、サービスを無期限に実行したい場合は、代わりにServiceクラスを拡張することをお勧めします。ただし、これはサービスが無期限に実行されることを保証するものではありません。優先度が低い場合、サービスはメモリ不足の状態でカーネルによって強制終了される可能性があります。したがって、2つのオプションがあります。
1)startForeground()メソッドを呼び出して、フォアグラウンドで実行し続けます。
2)強制終了された場合、サービスを再起動します。これは、ドキュメントが殺された後にサービスを再起動することについて話しているドキュメントの例の一部です

 public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the 
      // start ID so we know which request we're stopping when we finish the job 
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart 
      return START_STICKY;
  }  
4
Rishit Shah

Android:stopWithTask="false"inマニフェストを以下のように使用できます。これは、ユーザーがタスクリストからアプリを削除してアプリを強制終了しても、サービスが停止しないことを意味します。

 <service Android:name=".service.StickyService"
                  Android:stopWithTask="false"/>
1
Melbourne Lopes