Android アプリケーション プロセスのキープアライブ ソリューションの実践

  1. フォアグラウンド サービス: アプリケーションを実行しているサービスをフォアグラウンド サービスとして設定して、アプリケーションがバックグラウンドで実行されていることをユーザーに知らせます。システムはそのサービスに特定の優先順位を与え、システムによって強制終了される可能性を減らします。ただし、フォアグラウンド サービスを使用して維持しても、ユーザーの通知バーの大部分を占有することはできません。そうしないと、ユーザーがイライラしてアプリケーションをアンインストールする可能性があります。

  2. JobScheduler: Android 5.0から導入されたタスクのスケジュール方法。アプリケーションのタスク実行時間を柔軟に調整し、タスク実行の効率と安定性を向上させることができます。

  3. AlarmManager: アプリケーションがバックグラウンドでシステムによって強制終了されないように、バックグラウンドで定期的にアプリケーションのサービスまたはブロードキャストを開始できます。しかし同時に、AlarmManager の過度の使用を避け、バックグラウンドでのアプリケーションの電力消費とリソース占有を削減することにも注意する必要があります。

  4. デュアルプロセス保護: 2 つのプロセスを開くことにより、アプリケーションはシステム内で 2 つの異なるプロセスを実行して相互を保護し、一方のプロセスがシステムによって破壊されると、もう一方のプロセスがアプリケーションを再び起動するようにします。ただし、デュアルプロセスデーモンによりアプリケーションのメモリ使用量が高くなるため、一部の携帯電話メーカーはこの機能を無効にしている場合があります。

  5. キープアライブ フレームワーク: 市場にはサードパーティのキープアライブ フレームワークがいくつかあり、CPU 使用率やウェイクアップ ロックなどの技術的手段を通じてアプリケーションのアクティビティを維持できます。ただし、これらのフレームワークでは、追加のリソース消費と電力消費が発生する可能性があることに注意してください。

上記は、Android アプリケーション プロセスを維持するための主なソリューションです。もちろん、さまざまなアプリケーションのキープアライブ ポリシーは、アプリケーションの特性や開発ニーズによって異なる場合があります。

1. フロントサービス

1. AndroidManifest.xml ファイルに、ネットワーク ステータスとフォアグラウンド サービスにアクセスするための権限を追加します。

复制<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

2. バックグラウンド サービスを作成し、Service に Service クラスを継承させ、その onBind() メソッドと onStartCommand() メソッドを実装して、サービスをフォアグラウンドで実行し続け、サービスが破棄されたときに再度開始します。

复制public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Notification notification = new NotificationCompat.Builder(this, "CHANNEL_ID")
                .setContentTitle("App is running in background")
                .setContentText("Tap to open")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0))
                .build();

        startForeground(1, notification);

        return START_STICKY;
    }

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

        Toast.makeText(this, "Service destroyed", Toast.LENGTH_SHORT).show();

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

3. MainActivity クラスでサービスを開始します。

复制public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

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

PendingIntent は、フォアグラウンド サービスの通知バーで MainActivity を開始するために使用されます。これにより、ユーザーが通知バーをクリックしたときにアプリケーションを開くことができます。サービスが破壊された場合、別のIntentオブジェクトを起動することでサービスを再起動することでキープアライブ機能を実現します。サービスはプログラムの終了後も実行し続けるため、stopService() メソッドまたは stopForeground() メソッドを呼び出してサービスを停止する必要があることに注意してください。そうしないと、バックグラウンドで実行が継続され、システムの負荷が増大する可能性があります。重荷。

2、デュアルプロセスガーディアン

  • 1. デーモン サービス クラス DaemonService を作成します。

    复制public class DaemonService extends Service {
        private final static String TAG = DaemonService.class.getSimpleName();
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            Log.i(TAG, "onCreate: DaemonService created.");
        }
    
        @Override
        public void onTaskRemoved(Intent rootIntent) {
            super.onTaskRemoved(rootIntent);
    
            Log.i(TAG, "onTaskRemoved: App is killed.");
    
            // 在应用被杀死前,启动 ProtectService 来尝试拉活
            Intent intent = new Intent(getApplicationContext(), ProtectService.class);
            startService(intent);
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    

    2. ライブ サービス クラス ProtectService を作成します。

    复制public class ProtectService extends Service {
        private final static String TAG = ProtectService.class.getSimpleName();
        private final static int NOTIFY_ID = 1;
        private final static String CHANNEL_NAME = "keep_alive";
        private final static String CHANNEL_DESCRIPTION = "keep_alive";
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            Log.i(TAG, "onCreate: ProtectService created.");
    
            startForeground(NOTIFY_ID, getNotification());
    
            startService(new Intent(getApplicationContext(), DaemonService.class));
        }
    
        private Notification getNotification() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    
                NotificationChannel channel = new NotificationChannel(CHANNEL_NAME, CHANNEL_DESCRIPTION, NotificationManager.IMPORTANCE_HIGH);
                notificationManager.createNotificationChannel(channel);
    
                Notification.Builder builder = new Notification.Builder(this, CHANNEL_NAME);
                builder.setSmallIcon(R.mipmap.ic_launcher);
                builder.setContentTitle(getString(R.string.app_name));
                builder.setContentText(getString(R.string.text_keep_alive));
    
                return builder.build();
            } else {
                NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
                builder.setSmallIcon(R.mipmap.ic_launcher);
                builder.setContentTitle(getString(R.string.app_name));
                builder.setContentText(getString(R.string.text_keep_alive));
    
                return builder.build();
            }
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    

    3. MainActivity でガーディアン サービスとプル サービスを開始します。

    复制public class MainActivity extends AppCompatActivity {
        private final static String TAG = MainActivity.class.getSimpleName();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Log.i(TAG, "onCreate: MainActivity created.");
    
            startService(new Intent(getApplicationContext(), DaemonService.class));
            startService(new Intent(getApplicationContext(), ProtectService.class));
        }
    }
    

    アプリケーションがシステムによって強制終了された場合は、それをリアルタイムでキャプチャし、ProtectService を呼び出してアプリケーションを起動し、再度フォアグラウンドで実行させて、バックグラウンドで強制終了されることがほとんどないようにします。同時に、フォアグラウンド サービスがオンになっているため、フォアグラウンドとバックグラウンドを頻繁に切り替えると、ユーザーの通知バーに多数の「常駐通知」が表示される可能性があるため、注意が必要です。

  • 上記機能に携帯電話の起動機能を追加
  • 以下は、上記のソリューションに基づいて携帯電話の起動機能を追加する完全なコード例です。

    1. 携帯電話のブート ブロードキャストを監視する BroadcastReceiver クラスを作成し、ガーディアン サービスを開始してサービスをプルするための Intent オブジェクトを作成します。

    复制public class BootReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
                Intent service = new Intent(context, DaemonService.class);
                context.startService(service);
    
                Intent protect = new Intent(context, ProtectService.class);
                context.startService(protect);
            }
        }
    }
    

    2. AndroidManifest.xml ファイルに権限と BroadcastReceiver を登録します。

    复制<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    
    <receiver
        android:name=".BootReceiver"
        android:enabled="true"
        android:exported="true"
        android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    

    3. MainActivity クラスで、デーモン サービスとライブ サービスが実行されているかどうかを確認し、実行されていない場合は手動で開始します。

    复制public class MainActivity extends AppCompatActivity {
        private final static String TAG = MainActivity.class.getSimpleName();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Log.i(TAG, "onCreate: MainActivity created.");
    
            if (!isServiceRunning(this, DaemonService.class.getName())) {
                startService(new Intent(getApplicationContext(), DaemonService.class));
            }
    
            if (!isServiceRunning(this, ProtectService.class.getName())) {
                startService(new Intent(getApplicationContext(), ProtectService.class));
            }
        }
    
        private boolean isServiceRunning(Context context, String serviceName) {
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningServiceInfo> list = am.getRunningServices(Integer.MAX_VALUE);
    
            for (ActivityManager.RunningServiceInfo info : list) {
                if (serviceName.equals(info.service.getClassName())) {
                    return true;
                }
            }
    
            return false;
        }
    }
    

    上記は、携帯電話スタートアップ機能を追加するための完全なコード例です。これにより、デバイスの再起動後にサービスが自動的に開始され、アプリケーションの実行状態がバックグラウンドで維持されます。

三、ジョブスケジューラー

1. JobService クラスを作成し、その onStartJob() メソッドと onStopJob() メソッドを実装します。

复制public class MyJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.i(TAG, "onStartJob");

        // 在此添加需要执行的任务
        // ...

        jobFinished(params, false);

        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return true;
    }
}

2. JobService と必要な権限を AndroidManifest.xml ファイルに登録します。

复制<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application>
    ...

    <service
        android:name=".MyJobService"
        android:permission="android.permission.BIND_JOB_SERVICE" />

    ...
</application>

3. アプリケーションがキープアライブの実現を開始するときに、対応する JobScheduler を呼び出します。

复制public class MainActivity extends AppCompatActivity {
    private static final int JOB_ID = 100;

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

        JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);

        JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, new ComponentName(getPackageName(), MyJobService.class.getName()));
        builder.setPeriodic(10000);
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);

        if (scheduler.schedule(builder.build()) <= 0) {
            Log.e(TAG, "onCreate: JobScheduler failed.");
        }
    }
}

ここではキープアライブ時間を 10 秒に設定し、Wifi とモバイル ネットワークを区別しないようにネットワーク要求を設定します。アプリケーションがリサイクルされた後、JobScheduler はアプリケーションを再起動し、MyJobService クラスの onStartJob() メソッドを呼び出し、このメソッドで実行するタスクを追加してアプリケーションのキープアライブを完了します。

JobScheduler は、設定された時間通りにタスクを正確に実行できることを 100% 保証するものではなく、システムが何らかの理由でタスクの実行時間を遅らせたり、Android のバージョンによってサポート レベルが異なる場合があることに注意してください。

タスクを即時実行する必要がある場合は、startService() メソッドまたは ForegroundService を使用してタスクを実装し、JobScheduler と組み合わせて使用​​して、タスクがバックグラウンドで強制終了されないようにすることができます。

4. アラームマネージャー

1. 常駐プロセス Service を作成し、その onBind()、onStartCommand()、および onDestroy() メソッドを実装します。

复制public class KeepAliveService extends Service {
    private static final int NOTIFY_ID = 1001;
    private static final String CHANNEL_ID = "MY_CHANNEL_ID";
    private static final String CHANNEL_NAME = "MY_CHANNEL_NAME";

    private PendingIntent pendingIntent;

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand");

        Intent notificationIntent = new Intent(this, MainActivity.class);
        pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

        Notification notification = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);

            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            manager.createNotificationChannel(channel);

            notification = new Notification.Builder(this, CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText(getString(R.string.text_keep_alive))
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentIntent(pendingIntent)
                    .setAutoCancel(false)
                    .setCategory(NotificationCompat.CATEGORY_SERVICE)
                    .build();
        } else {
            notification = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText(getString(R.string.text_keep_alive))
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentIntent(pendingIntent)
                    .setAutoCancel(false)
                    .setPriority(NotificationCompat.PRIORITY_MIN)
                    .setCategory(NotificationCompat.CATEGORY_SERVICE)
                    .build();
        }

        startForeground(NOTIFY_ID, notification);

        // 定时发送广播
        AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        Intent alarmIntent = new Intent(getApplicationContext(), KeepAliveReceiver.class);
        pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, alarmIntent, 0);
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 30000, pendingIntent);

        return super.onStartCommand(intent, flags, startId);
    }

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

        Log.i(TAG, "onDestroy");

        AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        am.cancel(pendingIntent);

        stopForeground(true);
    }
}

2. ブロードキャスト リスナー KeepAliveReceiver を作成して、スケジュールされたブロードキャストを受信し、常駐サービスを再起動します。

复制public class KeepAliveReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent service = new Intent(context, KeepAliveService.class);
        context.startService(service);
    }
}

3. BroadcastReceiver と対応する権限を AndroidManifest.xml に登録します。

复制<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<receiver
    android:name=".KeepAliveReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="myAction" />
    </intent-filter>
</receiver>

<service
    android:name=".KeepAliveService"
    android:exported="false" />

ここでは、アプリのスケジュールされたタスクを監視するために BroadcastReceiver が登録されていることがわかります。同時に、BOOT COMPLETED 権限が追加され、デバイスの起動完了のブロードキャストを受信するためにBOOT COMPLETED ブロードキャスト リスナーが登録されています。

4. MainActivity の onCreate() メソッドで常駐サービスを開始します。

复制public class MainActivity extends AppCompatActivity {

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

        Intent service = new Intent(getApplicationContext(), KeepAliveService.class);
        startService(service);
    }
}

最後にMainActivityのonCreate()メソッドで常駐サービスを起動し、Androidアプリのプロセスキープアライブ機能を完了します。

5. キープアライブフレームワーク

アプリケーションがバックグラウンドに入ったり、長期間操作されなかった場合、Android システムのプロセス管理メカニズムは、アプリケーションによるリソースの長期占有によって引き起こされるシステムの不安定性やその他のアプリケーションの問題を回避するために、そのプロセスを強制終了します。応用。ただし、プッシュ アプリケーションや IM アプリケーションなどの一部のアプリケーションはバックグラウンドで接続し続ける必要があるため、アプリケーション プロセスを長時間存続させる必要があります。

この問題を解決するために、オープン ソース コミュニティでいくつかのキープアライブ フレームワークが登場しました。その主な機能は、さまざまな方法でアプリケーション プロセスを存続させ、アプリケーションがバックグラウンドで接続などのタスクを実行できるようにすることです。よく知られているキープアライブ フレームワークをいくつか紹介します。

  1. デーモン

デーモンは、Android プラットフォーム上のプロセス ガード フレームワークであり、アプリ ガード、プロセス キープ アライブ、サービス キープ アライブなどの複数のサブモジュールが含まれています。デーモンは、サービス モード、アラーム メカニズム、ジョブ スケジューラなど、さまざまなアプリケーション シナリオに応じてさまざまなキープアライブ メソッドを提供し、さまざまな Android バージョンでさまざまなキープアライブ メソッドをサポートできます。

GitHub: https://github.com/Leaking/daemon

  1. エックスポーズド

Xused は、APK をカスタマイズおよび変更するためのツールです。これを使用すると、ユーザーは APK ファイルを変更せずにアプリケーションの動作や外観を変更でき、アプリケーションを存続させるためにも使用できます。Xused はアプリ内モジュールを使用してキープアライブ機能を実装しますが、root 権限が必要です。

GitHub: https://github.com/rovo89/Xused

  1. Android-ジョブ

Android-Job は、Android システム上のジョブ スケジューリング フレームワークであり、JobScheduler API に基づくライブラリです。Android-Job を使用すると、非常に使いやすいジョブを作成および実行できます。このライブラリを使用すると、存続期間の短い非同期タスクや長時間実行される操作を作成するのが非常に便利になります。

GitHub: https://github.com/Evernote/android-job

  1. ジョブスケジューラCompat

JobSchedulerCompat は、JobScheduler API を利用して、Android 5.0 より前のバージョンの JobScheduler のすべての機能を実装します。JobScheduler の実装中に、JobSchedulerCompat はキープアライブなどのシナリオをサポートできます。

GitHub: https://github.com/evant/JobSchedulerCompat

  1. ワークマネージャー

WorkManager は、バックグラウンド スケジュール タスクを管理するために Google によって開始されたライブラリであり、API レベル 14 以上のデバイスと互換性があります。WorkManager は Android Jetpack の一部であり、WorkManager と組み合わせることで、作業スケジュールや長時間実行タスクを簡単に実装できます。

GitHub: https://github.com/android/architecture-components-samples/tree/master/WorkManagerSample

上記はよく知られたキープアライブ フレームワークの一部であり、それぞれに独自の特性と適用可能なシナリオがあり、特定の開発ニーズに応じて選択して使用できます。

6. ワークマネージャー

1. WorkManager ワーカー クラスを作成します。

复制public class KeepAliveWorker extends Worker {
    private static final String TAG = "KeepAliveWorker";

    public KeepAliveWorker(
            @NonNull Context context,
            @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        Log.i(TAG, "doWork");

        // 在此添加需要执行的任务
        // ...

        return Result.success();
    }
}

2. 時間指定タスクを作成し、実行のスケジュールを設定します。

复制PeriodicWorkRequest keepAliveWork =
        new PeriodicWorkRequest.Builder(KeepAliveWorker.class, 15, TimeUnit.MINUTES)
                .setConstraints(Constraints.NONE)
                .build();

WorkManager workManager = WorkManager.getInstance(context);
workManager.enqueueUniquePeriodicWork(
        "keep_alive_work",
        ExistingPeriodicWorkPolicy.KEEP,
        keepAliveWork
);

ここでは、15 分ごとに実行される定期タスクを作成しました。WorkManager スケジュールを通じてタスクを実行します。WorkPolicy が ExistingPeriodicWorkPolicy に設定されている場合、KEEP は、同じ名前のタスクが現在実行されている場合、タスクは変更されずに再起動されないことを意味します。

3. AndroidManifest.xml ファイルで必要な権限を宣言して適用します。

复制<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application>
    ...

    <receiver
        android:name=".KeepAliveReceiver"
        android:enabled="true"
        android:exported="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

    ...
</application>

デバイスの起動後にタスクを自動的に再開できるように、BOOT_COMPLETED ブロードキャストを受信するために AndroidManifest.xml に BroadcastReceiver を登録していることがわかります。

4. BOOT_COMPLETED ブロードキャストを受信する BroadcastReceiver を作成し、タスクを再起動します。

复制public class KeepAliveReceiver extends BroadcastReceiver {
    private static final String TAG = "KeepAliveReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(new Intent(context, KeepAliveService.class));
        } else {
            context.startService(new Intent(context, KeepAliveService.class));
        }
    }
}

5. 常駐サービスを作成して、アプリケーションがバックグラウンドで実行中に WorkManager を通じてタスクを定期的に実行できるようにします。

复制public class KeepAliveService extends Service {
    private static final int NOTIFY_ID = 1001;
    private static final String CHANNEL_ID = "MY_CHANNEL_ID";
    private static final String CHANNEL_NAME = "MY_CHANNEL_NAME";

    private PendingIntent pendingIntent;

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand");

        Intent notificationIntent = new Intent(this, MainActivity.class);
        pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

        Notification notification = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);

            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            manager.createNotificationChannel(channel);
 notification = new Notification.Builder(this, CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText(getString(R.string.text_keep_alive))
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentIntent(pendingIntent)
                    .setAutoCancel(false)
                    .setCategory(NotificationCompat.CATEGORY_SERVICE)
                    .build();
        } else {
            notification = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText(getString(R.string.text_keep_alive))
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentIntent(pendingIntent)
                    .setAutoCancel(false)
                    .setPriority(NotificationCompat.PRIORITY_MIN)
                    .setCategory(NotificationCompat.CATEGORY_SERVICE)
                    .build();
        }

        startForeground(NOTIFY_ID, notification);

        startWorkManager();

        return super.onStartCommand(intent, flags, startId);
    }

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

        Log.i(TAG, "onDestroy");

        stopWorkManager();

        stopForeground(true);

        Intent intent = new Intent(this, KeepAliveReceiver.class);
        intent.setAction("myAction");
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
        AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 2000, pendingIntent);
    }

    private void startWorkManager() {
        Log.i(TAG, "startWorkManager");

        PeriodicWorkRequest keepAliveWork =
                new PeriodicWorkRequest.Builder(KeepAliveWorker.class, 15, TimeUnit.MINUTES)
                        .setConstraints(Constraints.NONE)
                        .build();

        WorkManager workManager = WorkManager.getInstance(getApplicationContext());
        workManager.enqueueUniquePeriodicWork(
                "keep_alive_work",
                ExistingPeriodicWorkPolicy.KEEP,
                keepAliveWork
        );
    }

    private void stopWorkManager() {
        Log.i(TAG, "stopWorkManager");

        WorkManager workManager = WorkManager.getInstance(getApplicationContext());
        workManager.cancelUniqueWork("keep_alive_work");
    }
}

onStartCommand() メソッドで常駐フォアグラウンド サービスを (通知経由で) 開始し、startWorkManager() メソッドを呼び出して WorkManager のスケジュールされたタスクを開始しました。onDestroy() メソッドでは、stopWorkManager() を呼び出してタスクを閉じ、WorkManager プロセスの実行を停止します。

また、onDestroy() メソッドでは、AlarmManager を使用してスケジュールされたブロードキャストを開始していることにも注目してください。その目的は、WorkManager のスケジュールされたタスクがシステムによって強制的に強制終了され、存続させる効果を達成するときに、ブロードキャストを通じてサービスを再起動することです。 。

6. 最後に、MainActivity の onCreate() メソッドでサービスを開始します。

复制public class MainActivity extends AppCompatActivity {

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

        Intent service = new Intent(getApplicationContext(), KeepAliveService.class);
        startService(service);
    }
}

これで、WorkManager を使用して Android アプリケーション プロセスのキープアライブを実装する例が完了しました。

セブン、

  • デーモンはアプリケーションプロセスを生かし続けるアクセススキームを実現します
  • 以下は、デーモンを使用して Android アプリケーション プロセスのキープ アライブを実装する例です。

    1. build.gradle ファイルに依存関係をインポートします。

    复制repositories {
        maven { url "https://jitpack.io" }
    }
    
    dependencies {
        implementation 'com.github.Leaking:Daemon:2.1.13'
    }
    

    2. DaemonInnerService を継承するカスタム Service を作成します。DaemonInnerService は、デーモン ライブラリで提供される基本サービスで、常駐プロセスをバックグラウンドで起動するために使用されます。

    复制public class MyService extends DaemonInnerService {
        private static final String TAG = "MyService";
    
        // ...
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i(TAG, "onCreate");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i(TAG, "onStartCommand");
            startWork();
    
            return START_STICKY;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
    
            Log.i(TAG, "onDestroy");
            stopWork();
        }
    
        // ...
    }
    

    ここで作成した MyService は DaemonInnerService を継承し、onStartCommand() メソッドと onDestroy() メソッドをオーバーライドします。onStartCommand() で startWork() メソッドを呼び出していることに注意してください。このメソッドは、維持する必要があるいくつかのタスクを開始するために使用されます。同時に、onDestroy() メソッドで stopWork() メソッドを呼び出してキープアライブ タスクを停止します。

    3. AndroidManifest.xml ファイルにサービスを登録します。

    复制<service
        android:name=".MyService"
        android:enabled="true"
        android:exported="false" />
    
    <receiver
        android:name=".MyReceiver"
        android:priority="1000"
        android:exported="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.USER_PRESENT" />
        </intent-filter>
    </receiver>
    

    ここでは、BOOT COMPLETED および USER PRESENT ブロードキャストを受信するブロードキャスト レシーバー MyReceiver も登録し、デバイスが起動またはロック解除されたときにサービスを開始します。

    4. キープアライブ遅延タスク クラスを作成して、キープアライブ関連の操作を実行します。

    复制public class MyDelayService extends AbsWorkService {
        private static final String TAG = "MyDelayService";
    
        @Override
        public Boolean shouldStopService(Intent intent, int flags, int startId) {
            Log.i(TAG, "shouldStopService");
            return super.shouldStopService(intent, flags, startId);
        }
    
        @Override
        public void startWork(Intent intent, int flags, int startId) {
            Log.i(TAG, "startWork");
    
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
    _
    
        インテント サービス = new Intent(getApplicationContext(), MyService.class); 
                startService(サービス); 
                startDaemonService(MyDelayService.class); 
            }、 10000); 
        @Override public void stopWork(Intent 意図, int flags 
        , 
        int startId) { 
            Log.i(TAG, "stopWork"); 
            stopDaemonService(MyDelayService.class); 
        } 
        @NonNull 
        @Override 
        public Result onRunTask(Intent インテント、int フラグ、int startId) { 
            Log.i(TAG, "onRunTask"); 
            結果を返します。成功; 
        } 
    }
    
    
    
    
    MyDelayService では、MyService を手動で開始する必要がある startWork() メソッドを定義し、DaemonInnerService タイプが MyDelayService であるサービスを開始する必要があります。同時に、サービスの起動が完了した後に startWork() メソッドを呼び出せるようにするには、タスクを 10 秒後に実行する必要があることに注意してください。
    
    5. システム ブロードキャストを受信するブロードキャスト レシーバー MyReceiver を作成し、キープアライブ サービスを再起動します。
  • 复制public class MyReceiver extends AbsReceiver {
        private static final String TAG = "MyReceiver";
    
        @Override
        public void onReceive(Context context, Intent intent) {
            super.onReceive(context, intent);
    
            Log.i(TAG, "onReceive");
    
            boolean isScreenOn = isScreenOn(context);
    
            if (isScreenOn) {
                Intent service = new Intent(context, MyService.class);
                context.startService(service);
            }
        }
    }
    

    MyReceiverではブロードキャストの種類に応じてデバイスのロックが解除されているかどうかを判断し、その結果に応じてサービスを再起動します。ここでは、isScreenOn() メソッドを使用してデバイスのロックが解除されているかどうかを判断する必要があり、このメソッドは AbsReceiver で実装する必要があることに注意してください。

    6. 最後に、MainActivity でキープアライブ サービスを開始します。

    复制public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Intent service = new Intent(getApplicationContext(), MyService.class);
            startService(service);
        }
    }
    

    上記は、Daemon を使用して Android アプリケーション プロセスのキープアライブを実装する例です。デーモン ライブラリを使用すると、アプリケーション プロセスのキープアライブ機能を非常に簡単に実装でき、さまざまなシナリオでキープアライブをサポートし、

おすすめ

転載: blog.csdn.net/MYBOYER/article/details/129945121