Android
 Computer >> コンピューター >  >> プログラミング >> Android

AndroidのrunOnUiThreadの使い方を解説!ワーカースレッドからUIを安全に更新する方法

runOnUiThread()とは?

具体的な実装例に入る前に、まずAndroidにおけるrunOnUiThread()の役割について理解しておきましょう。

Androidアプリでは、UIの描画やユーザー操作への応答はすべて「メインスレッド(UIスレッド)」が担当しています。しかし、メインスレッドで重い処理を実行したり、長時間のタスクを処理したりすると、UIの応答が遅延し、最悪の場合はANR(Application Not Responding)——つまり「アプリが応答していません」というエラーが発生してしまいます。

そこで活躍するのがrunOnUiThread()です。このメソッドを使うことで、ワーカースレッド(バックグラウンドスレッド)で時間のかかる処理を実行しつつ、その結果だけをメインスレッド上でUIに反映させることができます。これにより、アプリの操作性を損なうことなく、スムーズな画面更新が可能になります。

本記事では、runOnUiThread()の基本的な使い方を、実際のコード例とともに段階的に解説します。

実装手順

ステップ1:プロジェクトの作成

Android Studioを起動し、「File → New Project」から新規プロジェクトを作成します。必要な項目をすべて入力して、プロジェクトの雛形を生成してください。

ステップ2:レイアウトファイルの作成

次に、res/layout/activity_main.xml に以下のコードを追加します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:id="@+id/parent"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:gravity="center"
    android:orientation="vertical">
    <Button
        android:id="@+id/runOn"
        android:text="Run"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/text"
        android:textSize="20sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

このレイアウトでは、ボタンを1つとテキストビューを1つ配置しています。ボタンをタップすると、テキストビューの表示内容が更新される仕組みです。

ステップ3:MainActivity.java の実装

続いて、src/MainActivity.java に以下のコードを記述します。

package com.example.andy.myapplication;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    int i = 0;
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView textView = findViewById(R.id.text);
        final Button runOn = findViewById(R.id.runOn);
        runOn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread() {
                    public void run() {
                        while (i++ < 1000) {
                            try {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        textView.setText("#" + i);
                                    }
                                });
                                Thread.sleep(300);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }.start();
            }
        });
    }
}

コードのポイント解説

上記コードの中核となる部分がこちらです。ボタンがクリックされると、新しいスレッドが起動し、その中でrunOnUiThread()を使ってテキストビューを更新します。

new Thread() {
    public void run() {
        while (i++ < 1000) {
            try {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("#" + i);
                    }
                });
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}.start();

処理の流れを整理すると以下のようになります。

  • ボタンをタップすると、新しいワーカースレッドが開始される
  • ワーカースレッド内でカウンター変数 i を1000回までインクリメントしながらループ処理を行う
  • 各ループ内でrunOnUiThread()を呼び出し、テキストビューの更新処理(textView.setText())をメインスレッドに依頼する
  • Thread.sleep(300)によって300ミリ秒ごとに表示が更新されていく

このように、UIを直接操作する処理だけをrunOnUiThread()経由でメインスレッドに渡すことで、ワーカースレッドでの長時間処理とUI更新を両立できます。

動作確認

それでは、実際にアプリを実行してみましょう。実機のAndroidスマートフォンをPCに接続していることを前提に説明します。Android Studioでプロジェクト内のアクティビティファイルを開き、ツールバーの「Run」アイコンをクリックします。デバイス選択画面で接続したスマートフォンを選択すると、端末にアプリが起動します。

起動直後は、ボタンと空のテキストビューが表示された初期画面が現れます。

AndroidのrunOnUiThreadの使い方を解説!ワーカースレッドからUIを安全に更新する方法

ここで「Run」ボタンをタップすると、ワーカースレッドの処理が開始され、テキストビューに「#1」「#2」「#3」…とカウンターの値が約0.3秒ごとに順次更新されていきます。

AndroidのrunOnUiThreadの使い方を解説!ワーカースレッドからUIを安全に更新する方法

まとめ

runOnUiThread()を使えば、ワーカースレッドで行ったバックグラウンド処理の結果を、メインスレッド上で安全にUIへ反映できます。AndroidではUIコンポーネントへの直接アクセスはメインスレッドからのみ許可されているため、スレッド処理とUI更新を組み合わせる際には必須のテクニックです。ぜひ本記事のサンプルコードを参考に、実際のアプリ開発に活かしてみてください。

  1. 【Android】スナックバー(Snackbar)の使い方をステップごとに解説

    このチュートリアルでは、Androidアプリでスナックバー(Snackbar)を使用する方法を、サンプルコードとともに段階的に解説します。スナックバーは画面下部に短いメッセージを一時的に表示できるUIコンポーネントで、Toastとは異なり「再試行(RETRY)」などのアクションボタンを組み込めるのが大きな特徴です。 手順1:新規プロジェクトを作成する Android Studioを起動し、メニューから「File」→「New Project」を選択します。必要な項目をすべて入力して、新しいプロジェクトを作成しましょう。 手順2:レイアウトファイル(activity_main.xml)を編集する

  2. 【Android】NavigationViewの実装方法をステップごとに解説

    この記事では、AndroidアプリでNavigationView(ナビゲーションビュー)を使用してドロワーメニューを実装する方法を、ステップごとに詳しく解説します。ハンバーガーアイコンから開閉できるサイドメニューは、多くのアプリで採用されている定番のUIです。ステップ1:新しいプロジェクトを作成するAndroid Studioを開き、File → New Project を選択して、必要な情報を入力し新しいプロジェクトを作成します。テンプレートには「Navigation Drawer Activity」を選ぶと、後の作業がスムーズになります。ステップ2:activity_main.xml にコ