Android Threading Tutorial and Examples (アンドロイド スレッド チュートリアルとサンプル)

これはアンドロイドのスレッディングのチュートリアルです。スレッド(thread)とは何か、また、スレッド(Thread)クラスの抽象化について説明します。

Thread とは何ですか?

スレッド」とは、あるプロセスの中に存在する実行経路のことです。

1つのプロセスは1つまたは複数の thread を持つことができます。

シングルスレッド**プロセスは1つのスレッドを持ち、マルチスレッド**プロセスは複数のスレッド`を持ちます。

シングルスレッド`プロセスでは、命令の実行の流れは一つだけですが、マルチスレッド“プロセスでは、複数の命令セットが同時に実行されます。

マルチスレッド」プロセスでは、同時に実行される部分があり、各部分は独立して所定のタスクを行うことができます。

つまり「マルチスレッド」とは、1つのプログラムの中で複数のアクティビティが同時に進行するようにプログラムを書くことができる仕組みなのです。

特に今日のマルチコアデバイスの環境では、開発者は複数のリソースからのデータを組み合わせたり、集約したりする同時実行ラインを作ることができるはずです。

しかし、実際には1つの実行コアしか持たないシステムでも、同時実行のような錯覚を起こすことができることにも注意しなければなりません。これは、さまざまな「スレッド」をインターリーブで実行することで実現しています。
しかし、実際には同時にタスクを実行しているように見えるように、高速に実行されます。

メインの「スレッド」について

通常、プロジェクトを実行すると、applicationのプロセスが始まります。まず、AndroidランタイムやDalvik仮想マシンのためのハウスキーピング用の「スレッド」があります。

それらとは別に、Androidシステムは「main」と呼ばれる実行用の「スレッド」を作成します。これがアプリケーションのメインの thread になります。

この thread は、UI thread` と呼ばれることもあります。

アプリケーションは他にも多くの thread を持つことができ、通常はバックグラウンド thread と呼ばれます。しかし、メインの thread は最も重要なものです。この thread は、Android コンポーネントやビューとのやりとりを担当します。この thread は、Android コンポーネントやビューとの対話を担当し、それらをレンダリングしたり、それらの状態を更新したりします。

この thread は、すべての Android コンポーネント([Activity](https://camposha.info/android/activity)、ServicesBroadcastReceiver`)がデフォルトで実行される場所であることを考えると、非常に重要です。

この thread は、ユーザーの入力イベントを処理したり、聞いたりする責任があります。このスレッドは非常に重要であるため、以下の方法で常に応答性を維持することが推奨されます。

    1. この threadinput/output (I/O) のような時間がかかりそうなタスクを実行しない。これらのタスクは、メインの thread を不定時間ブロックする可能性があるため、バックグラウンドの thread に負荷をかける必要があります。
    1. この thread で CPU 集中型のタスクを行わない。高価な計算やビデオエンコーディングなどのタスクがある場合は、それらもバックグラウンドの thread にオフロードする必要があります。

通常、この thread には Looper という機能が付属しています。ルーパーはMessageQueueを保持します。メッセージキューとは、ある作業単位を持つメッセージを順次実行するためのキューのことです。

メッセージがキューで処理できる状態になると、 Looper Thread はそのメッセージをキューから取り出します。そのメッセージは同期的に(逐次的に)対象のハンドラに転送されます。そのハンドラはメッセージの中ですでに指定されています。

その後、`ハンドラは仕事を始め、それを実行します。その作業が終わると、Looperthreadは、キューで利用可能な次のメッセージの処理を開始し、それを渡して実行させます。

このプロセスは逐次的であることがわかります。仮にHandlerがすぐに仕事を終えなかったとすると、Looperはキューの中の他の保留中のメッセージを処理するのを待っていることになります。

その場合、システムは「Application Not Responding(ANR)」ダイアログを表示します。このダイアログは、いくつかのアプリケーションで見たことがあるかもしれません。これは、アプリケーションがユーザーの入力に反応していないことを意味します。しかし、それは仕事をするのに忙しいのです。

ANRダイアログは、アプリが5秒以内にユーザーの入力に反応しない場合、ユーザーに表示されます。そして、システムはユーザーにアプリケーションを終了させるオプションを提供します。

このようなシナリオに遭遇するのは、メインの「スレッド」で集中的な作業を行おうとしたときです。つまり、例えばメインの thread で以下のような作業を行おうとした場合です。

  1. ネットワーク/ウェブサービス/インターネットにアクセスする
  2. ファイルシステムのリソースにアクセスする。
  3. 大量のデータを処理したり、複雑な数学計算をしてみる。

アクティビティやフラグメント、サービスなどに記述するコードのほとんどは、バックグラウンドの「スレッド」を明示的に作成しない限り、デフォルトではメインの「スレッド」で実行されます。

Android SDKはJava SDKのサブセットをベースにしている。Java SDKはApache Harmonyプロジェクトから派生したもので、以下のような低レベルの同時実行構造へのアクセスを提供します。

  1. java.lang.Thread`です。
    1. java.lang.Runnable.
    1. synchronizedvolatile キーワード。

Thread クラス

java.lang.Thread クラスは thread` を作成するための最も基本的な構造です。

また、最もよく使用されるクラスでもあります。このクラスは、Javaプログラムの中に新しい独立した実行行を作成します。

新しい thread を作成する一つの方法は、 java.lang.Thread クラスをサブクラス化または拡張することです。

public class MyThread extends Thread {
    public void run() {
        Log.d("Generic", "Our thread is running ...");
    }
}

これで、バックグラウンドタスクを run() メソッドの中で実行することができます。

しかし、その thread はまだ開始されていません。これを実現するためには、このクラスをインスタンス化し、明示的に thread を開始する必要があります。

    MyThread myThread = new MyThread();
    myTread.start();

start()メソッドは、Threadクラスに存在します。start()メソッドはThreadクラスに存在し、これを呼び出すことでシステムはプロセス内にthreadを作成し、run()メソッドを実行します。
run()メソッドを実行します。start()メソッドを呼び出すと、run()`が自動的に実行されます。

一般的な スレッド のメソッド

(a). Thread.currentThread()

このメソッドは呼び出し元の Thread 、つまり現在の Thread を返します。

(b). Thread.sleep(time)

このメソッドは、このメッセージを送信した Thread を指定された時間(ミリ秒またはナノ秒)だけスリープさせます。精度は保証されていませんので、Thread は要求された時間よりも多く、あるいは少なくスリープする可能性があります。

基本的には、指定された時間の間、現在の thread の実行を一時停止します。
指定された期間、現在の thread の実行を一時停止します。

(c). getContextClassLoader()

このメソッドは、この Thread のコンテキスト ClassLoader を返します。

(d). start()

このメソッドは、新しい Thread の実行を開始します。受信側の run() メソッドは、(start() を呼び出した Thread ではなく)受信側の Thread 自身によって呼び出されます。

(e). Thread.getName()および Thread.getId().

これらはそれぞれ nameTID を取得します。これらは主にデバッグ目的で使用します。

(f) Thread.isAlive()

isAlive()は、thread`が現在実行されているか、あるいは既にその仕事を終えたかをチェックします。
をチェックします。

(g) Thread.join()

join()は、現在の thread` をブロックし、アクセスされたスレッドが実行を終えるか、死ぬまで待ちます。
スレッドをブロックし、アクセスされたスレッドが実行を終了するか死ぬまで待ちます。

アプリの応答性を維持する方法

アプリの応答性を維持するための最良の方法は、長時間実行する処理を避けることではありません。むしろ、それらをメインスレッドからオフロードすることで
スレッドからオフロードして、別の スレッド がバックグラウンドで処理できるようにします。

メインの thread は、ユーザーインターフェースの更新をスムーズに処理し、ユーザーのインタラクションにタイムリーに対応し続けることができます。

通常、多くのアプリケーションに共通する典型的な操作があり、時間だけでなく、デバイスのリソースも大量に消費します。

その内容は以下の通りです。

  • ネットワーク(特にインターネット)へのアクセスと通信。
  • ファイルの入力と出力の操作。これらはローカルファイルシステム上で行われます。
  • 画像やビデオの処理。
  • 複雑な数学計算。
  • テキスト処理 – テキストの大きな塊を処理または分析しようとする。
  • データのエンコードとデコード。

Quick Threading Examples

1. 1. Thread クラスを使ったシンプルなタイマーの作成

これは、簡単なタイマーを実装する方法を示すクラスです。このクラスは何も表示せず、このようなアイデアを実装する方法を示すだけのクラスです。

import java.lang.Thread;

public class TimerClass extends Thread{

    boolean timeExpired;
    double mTime;
    /** Creates a new instance of TimerClass */
    public TimerClass(double time){
        mTime = time;
    }

    public void run(){

        timeExpired = true;
        mTime = mTime * 1000;
        double startTime = System.currentTimeMillis();
        double stopTime = startTime + mTime;
        while(System.currentTimeMillis() < stopTime && timeExpired == false){
              try {
                Thread.sleep(10);
            } catch (InterruptedException e){ }
        }

        timeExpired = true;

    }

    public boolean getTimeExpired(){
        return true;
    }

    public void cancel(){
        timeExpired = true;
    }

}

2. 完全に再利用可能な Thread ユーティリティクラス

import android.os.Looper;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Threading tools
 * </p>
 */
public class ThreadUtils {

    private final static ExecutorService sThreadPool = Executors.newCachedThreadPool();

    /**
     * Current thread
     */
    public static Thread currentThread() {
        return Thread.currentThread();
    }

    /**
     * Current process ID
     */
    public static long currentThreadId() {
        return Thread.currentThread().getId();
    }

    /**
     * Current process name
     */
    public static String currentThreadName() {
        return Thread.currentThread().getName();
    }

    /**
     * Determine if it is a UI thread
     */
    public static boolean isUiThread() {
        return Looper.myLooper() == Looper.getMainLooper();
    }

    /**
     * Runs on the UI thread
     */
    public static void runOnUiThread(Runnable action) {
        if (action == null) {
            return;
        }
        if (Looper.myLooper() == Looper.getMainLooper()) {
            action.run();
        } else {
            HandlerUtils.uiPost(action);
        }
    }

    /**
     * Runs in the background thread
     */
    public static void runOnBackgroundThread(Runnable action) {
        if(action==null){
            return;
        }
        if (Looper.myLooper() != Looper.getMainLooper()) {
            action.run();
        }else{
            sThreadPool.submit(action);
        }
    }

    /**
     * Run on asynchronous thread
     */
    public static void runOnAsyncThread(Runnable action) {
        if (action == null) {
            return;
        }
        sThreadPool.submit(action);
    }

    /**
     * Runs on the current thread
     */
    public static void runOnPostThread(Runnable action) {
        if(action==null){
            return;
        }
        action.run();
    }

    public static void backgroundToUi ( final Runnable background , final Runnable ui ) {
        runOnBackgroundThread(new Runnable() {
            @Override
            public void run() {
                background.run();
                runOnUiThread ( ui );
            }
        });
    }
}