web-dev-qa-db-ja.com

最小Androidハイエンド携帯電話でフォアグラウンドサービスが強制終了

ユーザーがルート(場所/ GPS)を記録できるアプリを作成しようとしています。画面がオフの場合でも場所が記録されるようにするために、場所の記録用に_foreground service_を作成しました。 _Room Database_を使用してサービスに挿入される_Dagger2_に場所を保存します。

ただし、このサービスはAndroidによって強制終了されます。これはもちろん良くありません。メモリ不足の警告を購読することはできますが、30秒後に強制終了されるサービスの根本的な問題は解決しませんAndroid 8.0を実行している最新のハイエンド携帯電話での分数

「Hello world」アクティビティとサービスのみを含む最小限のプロジェクトを作成しました: https://github.com/RandomStuffAndCode/AndroidForegroundService

サービスはApplicationクラスで開始され、ルートロギングはBinderを介して開始されます。

_// Application
@Override
public void onCreate() {
    super.onCreate();
    mComponent = DaggerAppComponent.builder()
            .appModule(new AppModule(this))
            .build();

    Intent startBackgroundIntent = new Intent();
    startBackgroundIntent.setClass(this, LocationService.class);
    startService(startBackgroundIntent);
}

// Binding activity
bindService(new Intent(this, LocationService.class), mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
// mConnection starts the route logging through `Binder` once connected. The binder calls startForeground()
_

おそらく_BIND_AUTO_CREATE_フラグは必要ありません。サービスが停止しないようにさまざまなフラグをテストしてきました。これまでのところ運はありません。

プロファイラーを使用すると、メモリリークがあるようには見えませんが、メモリ使用量は約35MBで安定しています:

profiler

_adb Shell dumpsys activity processes > tmp.txt_を使用すると、_foregroundServices=true_と私のサービスがLRUリストの8番目にリストされていることを確認できます。

Proc # 3: prcp F/S/FGS trm: 0 31592:com.example.foregroundserviceexample/u0a93 (fg-service)

殺されないように信頼できるフォアグラウンドサービスを作成することはできないようです。それで、私たちは何ができますか?まあ...

  1. サービスを別のプロセスに入れて、Android UI/Activitiesを強制終了させ、サービスをそのままにしてみます。おそらく役立つでしょうが、保証のようには見えません。
  2. のサービスで永続化everythingルームデータベース。すべての変数、すべてのカスタムクラス、変更のたびに、_START_STICKY_でサービスを開始します。これは一種の無駄で、非常に美しいコードにはなりませんが、おそらくうまくいくでしょう...いくらか。 Androidを強制終了してからサービスを再作成するのにかかる時間に応じて、場所の大部分が失われる可能性があります。

これは本当にAndroidのバックグラウンドで何かをする現在の状態ですか?もっと良い方法はありませんか?

編集:バッテリー最適化のためにアプリをホワイトリストに登録(無効化)しても、私のサービスが強制終了されることはありません

編集:Context.startForegroundService()を使用してサービスを開始しても状況は改善されません

編集:したがって、これは確かにsomeデバイスでのみ発生しますが、一貫して発生します。膨大な数のユーザーをサポートしないか、本当にいコードを書くかを選択する必要があると思います。驚くばかり。

23
user1202032

私はそれが遅れていることを知っていますが、これは誰かを助けるかもしれません。私も、さまざまなメーカーのOSに殺されることなく、フォアグラウンドサービスを維持するという同じ問題に直面しました。中国の製造元のOSのほとんどは、例外リスト(バッテリー、クリーナーなど)に追加されて自動起動が許可されている場合でも、フォアグラウンドサービスを強制終了します。

この link を見つけて、サービスを維持するというこの長い時間の問題を解決しました。

必要なのは、フォアグラウンドサービスを別のプロセスで実行することだけです。それでおしまい。

それには、AndroidManifest.xmlのサービスにAndroid:processを追加します。

例えば:

<service Android:name=".YourService"
        Android:process=":yourProcessName" />

Android:processの詳細については、 docs を参照してください。

編集:SharedPreferencesは複数のプロセスで機能しません。この場合、IPC(プロセス間通信)メソッドを選択するか、ContentProvidersを使用して、プロセス間で使用されるデータを保存およびアクセスできます。docs から参照。

2
Joshua

これらを使用することをお勧めします:AlarmManagerPowerManagerWakeLockThreadWakefulBroadcastReceiverHandlerLooper

あなたはすでにそれらの「別々のプロセス」と他の微調整も使用していると思います。

Applicationクラスで:

MyApp.Java

import Android.app.AlarmManager;
import Android.app.Application;
import Android.app.PendingIntent;
import Android.content.Context;
import Android.content.Intent;
import Android.os.PowerManager;
import Android.util.Log; 

public final class MyApp extends Application{

public static PendingIntent pendingIntent = null;
public static Thread                infiniteRunningThread;
public static PowerManager          pm;
public static PowerManager.WakeLock wl;


@Override
public void onCreate(){
    try{
        Thread.setDefaultUncaughtExceptionHandler(
                (thread, e)->restartApp(this, "MyApp uncaughtException:", e));
    }catch(SecurityException e){
        restartApp(this, "MyApp uncaughtException SecurityException", e);
        e.printStackTrace();
    }
    pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    if(pm != null){
        wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TAG");
        wl.acquire(10 * 60 * 1000L /*10 minutes*/);
    }

    infiniteRunningThread = new Thread();

    super.onCreate();
}

public static void restartApp(Context ctx, String callerName, Throwable e){
    Log.w("TAG", "restartApp called from " + callerName);
    wl.release();
    if(pendingIntent == null){
        pendingIntent =
                PendingIntent.getActivity(ctx, 0,
                                          new Intent(ctx, ActivityMain.class), 0);
    }
    AlarmManager mgr = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
    if(mgr != null){
        mgr.set(AlarmManager.RTC_WAKEUP,
                System.currentTimeMillis() + 10, pendingIntent);
    }
    if(e != null){
        e.printStackTrace();
    }
    System.exit(2);
}
}

そして、あなたのサービスで:

ServiceTrackerTest.Java

import Android.app.Service;
import Android.content.Context;
import Android.content.Intent;
import Android.graphics.BitmapFactory;
import Android.os.Handler;
import Android.os.IBinder;
import Android.os.Looper;
import Android.os.PowerManager;
import Android.support.v4.app.NotificationCompat;
import Android.support.v4.content.WakefulBroadcastReceiver;

public class ServiceTrackerTest extends Service{

private static final int SERVICE_ID = 2018;
private static PowerManager.WakeLock wl;

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

@Override
public void onCreate(){
    super.onCreate();
    try{
        Thread.setDefaultUncaughtExceptionHandler(
                (thread, e)->MyApp.restartApp(this,
                                              "called from ServiceTracker onCreate "
                                              + "uncaughtException:", e));
    }catch(SecurityException e){
        MyApp.restartApp(this,
                         "called from ServiceTracker onCreate uncaughtException "
                         + "SecurityException", e);
        e.printStackTrace();
    }
    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    if(pm != null){
        wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TAG");
        wl.acquire(10 * 60 * 1000L /*10 minutes*/);
    }


    Handler h = new Handler();
    h.postDelayed(()->{

        MyApp.infiniteRunningThread = new Thread(()->{
            try{
                Thread.setDefaultUncaughtExceptionHandler(
                        (thread, e)->MyApp.restartApp(this,
                                                      "called from ServiceTracker onCreate "
                                                      + "uncaughtException "
                                                      + "infiniteRunningThread:", e));
            }catch(SecurityException e){
                MyApp.restartApp(this,
                                 "called from ServiceTracker onCreate uncaughtException "
                                 + "SecurityException "
                                 + "infiniteRunningThread", e);
                e.printStackTrace();
            }

            Looper.prepare();
            infiniteRunning();
            Looper.loop();
        });
        MyApp.infiniteRunningThread.start();
    }, 5000);
}

@Override
public void onDestroy(){
    wl.release();
    MyApp.restartApp(this, "ServiceTracker onDestroy", null);
}

@SuppressWarnings("deprecation")
@Override
public int onStartCommand(Intent intent, int flags, int startId){
    if(intent != null){
        try{
            WakefulBroadcastReceiver.completeWakefulIntent(intent);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    startForeground(SERVICE_ID, getNotificationBuilder().build());
    return START_STICKY;
}


private void infiniteRunning(){
    //do your stuff here
    Handler h = new Handler();
    h.postDelayed(this::infiniteRunning, 300000);//5 minutes interval
}

@SuppressWarnings("deprecation")
private NotificationCompat.Builder getNotificationBuilder(){
    return new NotificationCompat.Builder(this)
                   .setContentIntent(MyApp.pendingIntent)
                   .setContentText(getString(R.string.notification_text))
                   .setContentTitle(getString(R.string.app_name))
                   .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                                                              R.drawable.ic_launcher))
                   .setSmallIcon(R.drawable.ic_stat_tracking_service);
}

}

「非推奨」などを無視し、選択の余地がないときに使用します。コードは明確で、説明する必要がないと思います。回避策の提案と解決策についてだけです。

0
M D P