web-dev-qa-db-ja.com

Androidアプリが閉じられるとサービスが停止する

次のように、メインのAndroidアクティビティからサービスを開始しています。

final Context context = base.getApplicationContext();
final Intent intent = new Intent(context, MyService.class);
startService(intent);

最近のアプリのリストからスワイプしてアクティビティページを閉じると、サービスの実行が停止し、しばらくしてから再起動します。アプリの要件のため、通知で永続サービスを使用できません。サービスを再起動またはシャットダウンせず、アプリの終了時に実行し続けるにはどうすればよいですか?

69
Bam

私は同じ状況にありますが、これまでのところ、アプリが閉じられたときにサービスが閉じられることを学びました。それらは1つのスレッドにあるため、閉じられないようにサービスを別のスレッドに置く必要がありますアラームマネージャーを使用してサービスを維持する例を次に示します。例 http://www.vogella.com/articles/AndroidServices/article.html これにより、通知にサービスが表示されなくなります。

最後に、私が行ったすべての調査の後、長期実行サービスの最良の選択は startForeground() であることに気付くようになりました。 。

41
aljuaid86

これ が役立つ場合があります。間違っているかもしれませんが、これはonStartCommand()メソッドでSTART_STICKYを返すことに関連しているように思えます。代わりにSTART_NOT_STICKYを返すことにより、サービスが再度呼び出されるのを回避できます。

12
artex

あなたのMainifestでこのようなサービスを提供する

 <service
            Android:name=".sys.service.youservice"
            Android:exported="true"
        Android:process=":ServiceProcess" />

サービスはServiceProcessという名前の他のプロセスで実行されます


あなたのサービスを決して死なせたくない場合:

  1. onStartCommand()return START_STICKY

  2. onDestroy()-> startself

  3. デーモンサービスを作成する

  4. jin-> Native Deamonプロセスを作成します。githubでいくつかのオープンソースプロジェクトを見つけることができます

  5. startForeground()、通知なしでstartForegroundを実行する方法があります

12
alanban

アプリを閉じたときにサービスを開始できない主な問題は、Android OS(一部のOS)がリソース最適化のためにサービスを強制終了することです。サービスを再起動できない場合は、アラームマネージャーを呼び出してこのように受信機を起動します。ここにコード全体があります。このコードはサービスを維持します。

マニフェストは、

         <service
            Android:name=".BackgroundService"
            Android:description="@string/app_name"
            Android:enabled="true"
            Android:label="Notification" />
        <receiver Android:name="AlarmReceiver">
            <intent-filter>
                <action Android:name="REFRESH_THIS" />
            </intent-filter>
        </receiver>

この方法でメインアクティビティでアラームマネージャーを起動し、

String alarm = Context.ALARM_SERVICE;
        AlarmManager am = (AlarmManager) getSystemService(alarm);

        Intent intent = new Intent("REFRESH_THIS");
        PendingIntent pi = PendingIntent.getBroadcast(this, 123456789, intent, 0);

        int type = AlarmManager.RTC_WAKEUP;
        long interval = 1000 * 50;

        am.setInexactRepeating(type, System.currentTimeMillis(), interval, pi);

これは受信者を呼び出し、受信者は

public class AlarmReceiver extends BroadcastReceiver {
    Context context;

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;

        System.out.println("Alarma Reciver Called");

        if (isMyServiceRunning(this.context, BackgroundService.class)) {
            System.out.println("alredy running no need to start again");
        } else {
            Intent background = new Intent(context, BackgroundService.class);
            context.startService(background);
        }
    }

    public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);

        if (services != null) {
            for (int i = 0; i < services.size(); i++) {
                if ((serviceClass.getName()).equals(services.get(i).service.getClassName()) && services.get(i).pid != 0) {
                    return true;
                }
            }
        }
        return false;
    }
}

そして、このAlaramレシーバーは、Androidアプリが開かれ、アプリが閉じられたときに1回呼び出します。SOサービスは次のようになります。

public class BackgroundService extends Service {
    private String LOG_TAG = null;

    @Override
    public void onCreate() {
        super.onCreate();
        LOG_TAG = "app_name";
        Log.i(LOG_TAG, "service created");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(LOG_TAG, "In onStartCommand");
        //ur actual code
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Wont be called as service is not bound
        Log.i(LOG_TAG, "In onBind");
        return null;
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Log.i(LOG_TAG, "In onTaskRemoved");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(LOG_TAG, "In onDestroyed");
    }
}
3
Ajith K P

サービスは時々非常に複雑です。

アクティビティ(またはプロセス)からサービスを開始すると、サービスは基本的に同じプロセス上にあります。

開発者メモからの引用

Serviceクラスに関するほとんどの混乱は、実際にはそうではないものを中心に展開しています。

サービスは独立したプロセスではありません。 Serviceオブジェクト自体は、独自のプロセスで実行されていることを意味しません。特に指定しない限り、それが属するアプリケーションと同じプロセスで実行されます。

サービスはスレッドではありません。メインスレッドから作業を行う手段ではありません(アプリケーションが応答しないエラーを回避するため)。

つまり、ユーザーが最近のタスクからアプリをスワイプすると、プロセスが削除されます(これにはすべてのアクティビティなどが含まれます)。次に、3つのシナリオを取り上げます。

Firstサービスの場所ないフォアグラウンド通知。

この場合、プロセスはサービスとともに強制終了されます。

Secondサービスの場所hasフォアグラウンド通知

この場合、サービスは強制終了されず、プロセスも強制終了されません

Thirdシナリオサービスにフォアグラウンド通知がない場合でも、アプリを閉じても引き続き実行できます。これを行うには、サービスを別のプロセスで実行します。 (ただし、うまくいかないかもしれないと言う人もいます。自分で試してみてください

以下の属性をマニフェストに含めることにより、別のプロセスでサービスを作成できます。

Android:process = ":yourService"

または

Android:process = "yourService"プロセス名は小文字で始まる必要があります。

開発者メモからの引用

この属性に割り当てられた名前がコロン(「:」)で始まる場合、アプリケーション専用の新しいプロセスが必要に応じて作成され、そのプロセスでサービスが実行されます。プロセス名が小文字文字で始まる場合、サービスはその名前のグローバルプロセスで実行されます(許可されている場合)。これにより、異なるアプリケーションのコンポーネントがプロセスを共有できるようになり、リソースの使用量が削減されます。

これは私が集めたものです、誰かが専門家である場合、私が間違っている場合は私を修正してください:)

3
mmmcho

Android Oからは、これが原因で長時間実行されるバックグラウンド操作にサービスを使用できません https://developer.Android.com/about/versions/oreo/background 。 Jobschedulerの実装では、Jobserviceがより良いオプションになります。

2
abhi.nalwade

これを試してください。バックグラウンドでサービスを実行し続けます。

BackServices.class

public class BackServices extends Service{

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
      // Let it continue running until it is stopped.
      Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
      return START_STICKY;
   }
   @Override
   public void onDestroy() {
      super.onDestroy();
      Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
   }
}

MainActivityonCreateでこのコード行をドロップします

startService(new Intent(getBaseContext(), BackServices.class));

これで、サービスはバックグラウンドで実行されたままになります。

2

プロセスが強制終了された場合にケースを処理するように、このコードをServiceクラスに追加する必要があります

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

サービスとアクティビティに同じプロセスを使用し、サービスのSTART_STICKYまたはSTART_REDELIVER_INTENTを使用することが、アプリケーションの再起動時にサービスを再起動できる唯一の方法です。これは、たとえばユーザーがアプリケーションを閉じたときに、最適化の理由で閉じます。中断することなく永続的に実行されるサービスを使用することはできません。これは仕様によるものであり、スマートフォンは長期間にわたって連続プロセスを実行するように作られていません。これは、バッテリー寿命が最優先事項であるという事実によるものです。任意の時点で停止することを処理するようにサービスを設計する必要があります。

1
stef

最善の解決策は、Androidの同期アダプターを使用してサービスを開始することです。同期アダプターを作成し、開始サービスをon.form内のonPerformSyncメソッドで呼び出します。同期アカウントを作成するには、このリンクを参照してください https://developer.Android.com/training/sync-adapters/index.html

SyncAdapterを使用する理由回答:以前はアプリコンテキストを使用してサービスを開始していたためです。そのため、アプリプロセスが強制終了されるたびに(リソースが不足してタスクマネージャーまたはOSから削除されると)、その時点でサービスも削除されます。 SyncAdapterはアプリケーションスレッドでは機能しません。したがって、内部で呼び出すと、サービスは削除されません。削除するコードを記述しない限り、サービスは削除されません。

1
Nishanth S Babu

インテントサービスの実行が簡単になります。アプリケーション内にスレッドを作成するサービスですが、それはまだアプリケーション内にあります。

0
Seb
<service Android:name=".Service2"
            Android:process="@string/app_name"
            Android:exported="true"
            Android:isolatedProcess="true"
            />

これをマニフェストで宣言します。プロセスにカスタム名を付け、そのプロセスを分離してエクスポートします。

0
Nishanth S Babu

IntentServiceを使用しないのはなぜですか?

IntentServiceはメインスレッドとは別に新しいスレッドを開き、そこで動作します。そのようにアプリを閉じると効果はありません。

IntentServiceがonHandleIntent()を実行し、サービスが終了したら、ニーズに合っているかどうかを確認してください。 http://developer.Android.com/reference/Android/app/IntentService.html

0
sharon gur