■スレッドの作成


スレッドを作成するとメインのアプリケーションのコードとは別に新たにコードが実行されます。

public class MainActivity extends AppCompatActivity {
    Thread th;
    int i=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        th=new Thread(new test());
        th.start();
    }

   private class test implements Runnable {
        @Override
        public void run() {
            while(true) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {}
                i++;
            }
        }
    }
}

Threadのインスタンスを生成してstartをすると、対象のクラスのrunメソッドが新なスレッドで実行されます。
runメソッドがループしている場合には、画面を切り替えても、電源ボタンを押して画面の電源が暗くなっても、ずっと実行され続けます。
ただし、アプリケーション自体を終了させた場合はスレッドも終了します。



■ユーザーインターフェースのNGコード


画面関係のユーザーインターフェースはシングルスレッドモデルのため、マルチスレッドで扱うと異常な動作をします。
そもそも、スレッド内部でユーザーインターフェースを扱うと例外が送出されてアプリケーションが落ちます。

例外をキャッチするととりあえず動きますが、以下のコードは一見動いてるように見えますが異常な動作をします。

このプログラムでは、ボタンを押すとテキストビューが再描画されますが、テキストが更新しても自動的に再描画されません。

public class MainActivity extends AppCompatActivity {
    Thread th;
    int i=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        android.widget.Button button=(android.widget.Button)findViewById(R.id.button2);
        button.setOnClickListener(new button_listener());

        th=new Thread(new test());
        th.start();
    }

   private class test implements Runnable {
        @Override
        public void run() {
            while(true) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {}
                i++;
                try{
                    ((android.widget.TextView)findViewById(R.id.textView)).setText(""+i);
                }catch(Exception e){}
            }
        }
    }

    private class button_listener implements android.view.View.OnClickListener {
        public void onClick(android.view.View view) {
            ((android.widget.TextView)findViewById(R.id.textView)).invalidate();
        }
    }
}



■ユーザーインターフェースのOKコード


画面のユーザーインターフェースを扱う場合には、Runnableインターフェイスを適用したクラスのrun関数で、 各種ユーザーインターフェースの処理をして、そのクラスをHandlerを使い実行キューにスケジューリングします。

メインスレッドが実行キューから呼び出して実行することによりシングルスレッドモデルが守られてちゃんと動作します。
ただし、画面を横にするなど、新たにプログラムが実行されなおす場合には、UIとスレッドが切り離されてしまい、 古いスレッドを終了しないと新旧のスレッドが動作する事態になるため、古いスレッドを終了する方法が必要です。

以下のプログラムでは約1秒毎にテキストビューの値が更新されてゆきます。

public class MainActivity extends AppCompatActivity {
    Thread th;
    int i=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        th=new Thread(new test());
        th.start();
    }

   private class test implements Runnable {
       android.os.Handler handler=new android.os.Handler();
        @Override
        public void run() {
            while(true) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {}
                i++;
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        ((android.widget.TextView)findViewById(R.id.textView)).setText(""+i);
                    }
                });
            }
        }
    }
}



■スレッドを停止させる


run関数内を抜けるとスレッドは終了します。
関数内でのループを終了させるフラグを使って関数を抜けるようにしています。

スレッドを停止させることにより画面を横に向けるなどによるプログラムの再実行で UIとスレッドが切り離されてしまいスレッドだけでループし続けるのを防いでいます。

public class MainActivity extends AppCompatActivity {
    Thread th;
    boolean thread_run;
    int i=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        thread_run=true;
        th=new Thread(new test());
        th.start();
    }

    private class test implements Runnable {
       android.os.Handler handler=new android.os.Handler();
        @Override
        public void run() {
            while(thread_run) {

            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        thread_run=false;
    }
}



■スレッドとユーザーインターフェースを切り離させない


画面を横に向けるなどしてプログラムが再実行された場合にスレッドとUIが切り離されてしまい、 いくらスレッドが画面情報を更新しようとしても更新できません。
そこで、UIをstaticな変数に入れることによりメインのプログラムとスレッドのUIが共有されて、 プログラムが再実行されても画面情報が更新され続けます。

public class MainActivity extends AppCompatActivity {
    static Thread th=null;
    static android.widget.TextView tv;
    int i=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv=(android.widget.TextView)findViewById(R.id.textView);
        if(null==th) {
            th = new Thread(new test());
            th.start();
        }
    }

   private class test implements Runnable {
       android.os.Handler handler=new android.os.Handler();
        @Override
        public void run() {
            while(true) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {}
                i++;
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        tv.setText(""+i);
                    }
                });
            }
        }
    }
}



■なるべく短く書いてみる


無名クラスを使い変数を無くして、なるべく短くしてみました。
確かにThreadは立ち上がりますが、これはこれでどうなのかと思えてきます。
  new Thread(new Runnable(){
      @Override
      public void run() {

      }
  }).start();


▲トップページ > android