LoginSignup
3
4

More than 1 year has passed since last update.

【Android】バックグラウンド処理をServiceからWorkManagerに移行してみた

Posted at

はじめに

皆さん、ごきげんよう!れぶです!
今回の記事は、バックグラウンド処理の実装についてです。

Android 8.0(Oreo)以降、バックグラウンド処理に対する厳しめな制限が導入されました。その結果、Service(特にバックグラウンドサービス)を使った実装をすると、動作が安定しません。詳しくは、こちらを参考にしてみてください。

なので、バックグラウンド処理を行う際には、Android Jetpack系のWorkManagerを使うことをGoogleは推奨しています。このWorkManagerは、電池寿命の向上・互換性の高さ・様々な条件や制約を付与できるなど、メリットが沢山あります。API level 14から機能します。

そのような背景から、今回はServiceWorkManagerを比較しながら、ボタンを押すと15分ごとにバックグラウンド処理を定期実行する実装方法を書いていきます。それでは参りましょう!!

この記事の対象者

  • 今までServiceを使ってバックグラウンド処理をしていたけど、WorkManagerを使った実装に移行したい方

開発環境

  • Mac
  • Android Studio 4.1.2
  • Java 8
  • compileSdkVersion 31
  • minSdkVersion 16

Serviceを使った実装

コードの主要部分がこちらになります。
Serviceを継承したクラスの中でTimerを使うことで、定期的なバックグラウンド処理を実現しています。

MainService.java
    private Timer timer;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

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

        //15分ごとに定期実行
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {

                //バックグラウンド処理
            }
        },0,900000);

        return START_NOT_STICKY;
    }

ボタンが選択されると、それぞれIntentでServiceの起動・停止を行います(フラグメント内)。

MainFragment.java
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        Button bt_start = view.findViewById(R.id.bt_start);
        Button bt_stop = view.findViewById(R.id.bt_stop);

        //startボタン選択時
        bt_start.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {

                //サービス起動
                Intent intent = new Intent(getActivity(),MainService.class);
                getActivity().startService(intent);
            }
        });

        //stopボタン選択時
        bt_stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //サービス停止
                Intent intent = new Intent(getActivity(),MainService.class);
                getActivity().stopService(intent);
            }
        });
    }

WorkManagerを使った実装

登場人物

以下の3つのオブジェクトが中心です。

オブジェクト 説明
Worker バックグラウンド処理を定義
WorkRequest 処理の実行条件やタイミングを記録。一度だけ処理を実行するOneTimeWorkRequestと定期的に処理を実行するPeriodicWorkRequest(今回はこっち)がある。
WorkManager Workの実行状況を監視

実装の流れ

❶ build.gradleファイルに依存関係を追加

build.gradle
dependencies {

    implementation "androidx.work:work-runtime:2.7.1"
}

現時点では、WorkManagerのバージョンは2.7.1です。適宜変更してください。

❷ バックグラウンド処理を定義

MainWorker.java
public class MainWorker extends Worker {

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

    @Override
    public Result doWork() {

        //バックグラウンド処理

        return Result.success();
    }   
}

Workerを継承したクラスのdoWorkメソッド内に、バックグラウンド処理を書きます。Serviceを継承したクラスのonStartCommandメソッド内に書いていた内容が、まさにこの部分に該当します。

❸ WorkRequestを作成

❹ WorkManagerに❸を追加or削除

MainFragment.java
        @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        Button bt_start = view.findViewById(R.id.bt_start);
        Button bt_stop = view.findViewById(R.id.bt_stop);

        //startボタン選択時
        bt_start.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.O)
            @Override
            public void onClick(View v) {

                //15分おきに定期実行を開始
                //Wi-Fiに繋がっている、かつ電池残量低下モードになっていない場合のみ
                Constraints constraints = new Constraints.Builder()
                        .setRequiredNetworkType(NetworkType.UNMETERED)
                        .setRequiresBatteryNotLow(true)
                        .build();
                PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(
                        MainWorker.class, Duration.ofMinutes(15)
                )
                        .setConstraints(constraints)
                        .addTag("タグ名")
                        .build();
                WorkManager.getInstance(getActivity()).enqueue(workRequest);
            }
        });

        //stopボタン選択時
        bt_stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //定期実行をキャンセル
                WorkManager.getInstance(getActivity()).cancelAllWorkByTag("タグ名");
            }
        });
    }

IntentでServiceの起動・停止を行なっていた箇所がここに該当します。startボタンで定期実行を開始し、stopボタンで停止しています(フラグメント内)。
PeriodicWorkRequestを作成する際にタグを追加しているのは、どの定期実行をキャンセルするかを識別するためです。

WorkManagerはServiceと違い、様々な条件や制約を付与することができます。今回のWorkManagerのコードでは、ConstraintsオブジェクトをWorkRequestに登録し、Wi-Fiとバッテリーモードの状態による制限を加えています。

備考

今回のサンプルはあくまで一例です。他にも、

  • 定期実行の間隔の変更(最小時間は15分)
  • 一回のみの処理実行
  • Workerへのデータの受け渡し
  • 充電や端末の状態などによる制約の付与

などなど、沢山の条件を設定することが可能です。

詳しくは、公式ドキュメントこちらの記事を参考にしてみてください。自身も非常に参考にさせて頂きました。

WorkManagerの使い所

公式ドキュメントで以下のように述べられています。これが基本原則です。

WorkManagerは、ユーザーが画面から移動した場合、アプリが終了した場合、デバイスが再起動された場合でも、確実に実行する必要がある処理を対象としています。次に例を示します。

  • ログやアナリティクスをバックエンドサービスに送信する
  • アプリデータをサーバーと定期的に同期する

何がなんでも裏で動作させたい処理にWorkManagerを使うことが推奨されています。
他にも、データのレポーティングや画像の保存、ツイートなどが挙げられます。

逆に非推奨な処理を以下にまとめました。

処理 代替策
ユーザーが画面を離れた時に、即時実行したタスクが終了する可能性がある処理 Kotlin Coroutine(Javaの場合は、Executorなど)
アラームやカレンダーなど、正確な時間どおりに実行する必要がある処理 AlarmManager

※ メディア再生にはForegroundServiceを使うことが推奨されていましたが、Android 12以降、ForegroundServiceの制限ができました。その代替策として、WorkManagerが挙げられていました。詳しくは、この記事をご覧ください。

おわりに

今回は、バックグラウンド処理をServiceからWorkManagerに変えて実装する方法を中心にまとめていきました。

基本は4ステップで出来るので、意外と簡単かなと思います。条件や制約次第で、多様なパターンのバックグラウンド処理の仕方があるので、各々試してみてください。その際には是非、適切な場面でWorkManagerを使えているかを意識してみてください!

なお、今回自身がこの記事を書くにあたり、ボタンを押すとバックグラウンド状態で通知が15分ごとに届くサンプルプログラムを作成しました。その際に書いたコードを以下のGitHubに載っけているので、参考の一つにしてみてください。

https://github.com/seiyaleb/BackGroundTrialApp

参考書籍・参考サイト

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4