データの取込み等の時間のかかる処理をメインスレッドで処理を実行してしまうと

処理が完了するまで操作も画面更新も受け付けないために、ユーザーからはフリーズしているように見えてしまいます。

 

そこで今回の記事では、時間のかかる処理をワーカースレッドで実行し、その間の処理の進み具合をプログレスバーを出してユーザーに処理をしている事をわかる様な実装について紹介していきたいと思います。

 

実際の動作については以下です。

 

 

これから紹介するのは

 

CSV取込み確認ダイアログ

プログレスバーで進捗表示

CSV取込み完了ダイアログ

 

の部分を抜粋して紹介します。

 

1.プログレスバーダイアログのレイアウト定義

まず、プログレスバーの表示レイアウトを以下のように定義します。

ポイントは、styleの属性にprogressBarStyleHorizontalを指定しているところです。


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ProgressBar
        android:id="@+id/ProgressBarHorizontal"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_margin="20dp"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:min="0"
        android:max="100"
        tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

2.ダイアログのOKボタンでワーカースレッド処理開始の実装

ダイアログのPositiveButton押下で非同期処理のワーカースレッドを起動する処理を以下のように書きます。
ポイントは以下2つ。
・プログレスバーのレイアウトをアラートダイアログに設定
・ワーカースレッドで処理するクラスをインスタンス化してsubmitする

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        Button button = (Button) findViewById(R.id.bgButton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                execImportDatabase();
            }
        });
    }

    private void execImportDatabase()
    {
        new AlertDialog.Builder(this)
                .setTitle("CSV取込")
                .setMessage("すべでのデータは上書きされます。実行しますか?")
                .setPositiveButton(R.string.execute, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // 取込み中のプログレスバーを表示する
                        progressAlertDialog = new AlertDialog.Builder(context)
                                .setTitle(R.string.csv_input_processing)
                                .setView(activity.getLayoutInflater().inflate(R.layout.alert_progressbar, null))
                                .create();
                        progressAlertDialog.show();
                        progressAlertDialog.setCancelable(false);
                        progressAlertDialog.setCanceledOnTouchOutside(false);
                        progressBar = progressAlertDialog.findViewById(R.id.ProgressBarHorizontal);

                        // ワーカースレッドで取込みを開始する
                        Looper mainLooper = Looper.getMainLooper();
                        Handler handler = HandlerCompat.createAsync(mainLooper);
                        BackgroundTask backgroundTask = new BackgroundTask(handler);
                        ExecutorService executorService  = Executors.newSingleThreadExecutor();
                        executorService.submit(backgroundTask);
                    }
                })
                .setNegativeButton(R.string.cancel, null)
                .show();
    }

3.ワーカースレッドの処理と完了後のダイアログ表示の実装

ワーカースレッドの処理は以下のように書きます。
ポイントは3つ。
・Runnableを継承したクラスでrunメソッドを実装する
・setProgressで処理の途中経過のパーセンテージを設定する
・処理完了後にPostExecutorでダイアログを表示

    private class BackgroundTask implements Runnable {
        private List<Map<String, String>> dataList; // 既に100件のデータが入っている状態
        private final android.os.Handler _handler;

        public BackgroundTask(android.os.Handler handler) {
            _handler = handler;
        }

        @WorkerThread
        @Override
        public void run() {
            int countUnit = dataList.size() / 20;
            int progressCount = 1;
            // 結果が全て取り出せたらデータを登録していく
            for (Map data : dataList) {
                // ここで1件ずつ処理 ※サンプルではログ出力
                Log.d("EXEC", data);

                // 5パーセントずつ表示を更新する
                if (countUnit > 0) {
                    if (progressCount % countUnit == 0) {
                        // プログレスバーに状態を反映
                        progressBar.setProgress((progressCount / countUnit) * 5);
                    }
                    progressCount++;
                }
            }
            // 完了後の処理
            PostExecutor postExecutor = new PostExecutor();
            _handler.post(postExecutor);
        }
    }

    private class PostExecutor implements Runnable {
        @UiThread
        @Override
        public void run() {
            // プログレスバーを閉じる
            progressAlertDialog.dismiss();
            // 完了した事を伝えるダイアログを出す
            new AlertDialog.Builder(context)
                    .setTitle("CSV取込")
                    .setMessage("CSVファイルを取込みました。")
                    .setPositiveButton(R.string.ok, null)
                    .show();
        }
    }

以上で、実装は完了です。
Androidアプリ、画面ロック付きのパスワード管理「パスワードメモ」をバージョン2.2.0として公開しましたが、アップデート機能としてCSV取込み機能があり、そこで上記の処理を実装しています。
このアップデートによりパスワードをバックアップしたり復元したりできるようになったので、ぜひ使って見てください!