Programming Style

メニュー

初心者がアプリ開発者になるためのプログラミング学習サイト

[Android] Thread:Androidのスレッドを知る

android-common-top

AndroidのスレッドにはAndroid固有のルールがあります。ここではAndroidのスレッドについて説明します。

 

広 告

 

目次

前提条件

  • なし

動作確認端末

  • Google Nexus 5 – 5.0.0 – API21(エミュレータ)

1. メインスレッド(UIスレッド)とは

Androidのシステムは、アプリを起動した時にそれを実行するためのスレッドを生成します。このスレッドはメインスレッドと呼ばれます。

 

Note : メインスレッド上でThread#currentThread()#getName()を実行して取得できるスレッド名は"main"です。

 

メインスレッドは、画面描画やユーザによる画面操作に関するイベントを処理するためのスレッドです。またandroid.widgetandroid.viewパッケージに含まれるコンポーネントで実行されるメソッドでもあります。メインスレッドは、UIに関するイベント全般で実行されるスレッドであるため、UIスレッドと呼ばれることがあります。

 

Androidシステムは、UIコンポーネントのオブジェクト(インスタンス)毎にスレッドを生成しません。すべてのUIコンポーネントは同じメインスレッド上でオブジェクト化されます。

 

つまりメインスレッドは、アプリ毎にシステムによって1つのみ生成されます。シングルスレッドで動作するということです。

 

Androidで画面描画や画面操作などUIに関連する処理はメインスレッド上でのみでしか実行することができません。別スレッド(ワーカスレッド)で実行した場合、android.view.ViewRootImpl$CalledFromWrongThreadExceptionが発生します。

 

2. メインスレッドで時間のかかる処理を実行した場合

処理時間のかかる処理(例えばネットワーク通信処理やデータベース操作処理)をメインスレッド上で実行すると、処理実行中に他の処理がブロックされます。つまり、UIに関する処理がすべてブロックされるため、処理実行中は画面が固まってしまいます。

 

メインスレッドが5秒以上ブロックされている場合、画面上にapplication not responding(ANR)と呼ばれるエラーダイアログがシステムによって呼ばれてしまいます。

 

android-android-thread_01

そのため、時間のかかる処理を実行する場合は、メインスレッド以外のスレッド(ワーカスレッド)を生成し、そちらで処理を実行する必要があります。

 

ここまで説明したことをまとめると以下の2点になります。

 

  • UIに関する処理はすべてメインスレッドのみしか実行できない
  • メインスレッドで時間のかかる処理を実行してはならない

 

3. ワーカスレッド生成して処理スレッドを分ける実装例を確認する

ワーカスレッドで処理を実行するためにはThreadクラスを使用します。以下にワーカスレッドで処理を実行するサンプルコードを示します。

 

// ①
new Thread(new Runnable() {
  @Override
  public void run() {
    // ②
    final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
    // ③
    mImageView.setImageBitmap(bitmap);
  }
}).start();

このコードは、①ワーカスレッドを生成し、②ワーカスレッド上でビットマップ画像をダウンロードし(処理時間のかかる処理)、③画面のImageViewに設定しているコードになります。

 

このコードは、時間のかかる処理(②)をワーカスレッド上で実装しているため、一見問題ないコードに見えます。しかし、このままこのコードを実行するとエラーになります。エラーとなる原因は③の処理にあります。「UIに関する処理はメインスレッドでのみ実行可能」というルールに違反しているからです。

 

このエラーを回避するために、AndroidではワーカスレッドからUIに関する処理を実行するための方法をいくつか用意しています。以下にそれを示します。

 

 

以下にView#post(Runnable)を使って先ほどのコードの修正例を示します。

 

// ①
new Thread(new Runnable() {
  @Override
  public void run() {
    // ②
    final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
    mImageView.post(new Runnable() {
      @Override
      public void run() {
        // ③
        mImageView.setImageBitmap(bitmap);
      }
    });
  }
}).start();

これで時間のかかる処理をワーカスレッドで処理し、結果をUIに反映させる処理をメインスレッドで処理しています。

 

今回はシンプルなコードでしたので問題ありませんでしたが、コードが長くなった場合、上記の実装方法だと処理が複雑になってします恐れがあります。その場合は、AsyncTaskという便利なクラスがあるので、そちらの使用を検討すると良いかと思います。

 

4. 参考URL

 

広 告