タイマーとかスレッドのテスト


2014年4月22日現在、私が理解している内容です。
間違っている場合があります。


タイマーを使ってみる

数値をカウントするため、Timerを使ってみようと下のように記述してみます。


package com.example.test20; import java.util.Timer; import java.util.TimerTask; import android.os.Bundle; import android.app.Activity; import android.util.Log; import android.widget.TextView; public class MainActivity extends Activity { int count1=0; TextView text1; Timer timer1 = new Timer(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); text1 = new TextView(this); text1.setTextSize(100.0f); timer1.schedule(new TimerTask() { @Override public void run() { count1 += 1; Log.d("TAG_TIMER",String.valueOf(count1)); //text1.setText("Timer " + String.valueOf(count1)); } }, 0, 10); text1.setText("Timer " + String.valueOf(count1)); setContentView(text1); } @Override protected void onPause() { super.onPause(); //タイマー停止 timer1.cancel(); timer1.purge(); timer1 = null; } }


ログでは、カウントされている様子が出力されますが……。




コード中の「text1.setText("Timer " + String.valueOf(count1));」を、有効にすると、たちまちエラーになります。




そこで、次のように修正してみます。


package com.example.test20; import java.util.Timer; import java.util.TimerTask; import android.os.Bundle; import android.os.Handler; import android.app.Activity; import android.util.Log; import android.widget.TextView; public class MainActivity extends Activity { int count1=0; TextView text1; Timer timer1 = new Timer(); Handler handler1 = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); text1 = new TextView(this); text1.setTextSize(100.0f); timer1.schedule(new TimerTask() { @Override public void run() { count1 += 1; Log.d("TAG_TIMER",String.valueOf(count1)); handler1.post(new Runnable(){ public void run() { text1.setText("Timer " + String.valueOf(count1)); } }); } }, 0, 10); text1.setText("Timer " + String.valueOf(count1)); setContentView(text1); } @Override protected void onPause() { super.onPause(); //タイマー停止 timer1.cancel(); timer1.purge(); timer1 = null; } }





こんどは、無事に、動きました。

面倒くさいですが、タイマーを使って画面を書き換えるような処理をする場合、
次のように、「handler1.post(new Runnable(){ run(){ 〜」を追加して、text1.setTextを入れ子にしないと、うまく動いてくれません。
androidとは、そういうものなのです。


timer1.schedule(new TimerTask() { @Override public void run() { count1 += 1; Log.d("TAG_TIMER",String.valueOf(count1)); handler1.post(new Runnable(){ public void run() { text1.setText("Timer " + String.valueOf(count1)); } }); } }, 0, 10);

一度、他のスレッドに処理を投げて、タイミングを見計らって元スレッドのビューにポストしなければいけないという感じでしょうか。
タイマーのような、誰にも邪魔されず黙々とカウントが行われるような処理は、
マルチスレッドで非同期処理の動作が都合が良いわけですが、
直接、UIへの書き換え命令は処理できない仕様のようです。
そこで、「handler1.post(new Runnable(){ run(){ 〜」を使って、UIにアクセスさせようという仕組みのようです。




スレッドを使ってみる

例えば、ファイル読み込みなどで、処理に手間取っていると、その間、アプリが止まってしまいますので、
別スレッドに処理を投げておきます。

ボタンを押すとカウントするようなコードを次のようにして書いても、
数値が1づつカウントされるような、期待通りの結果になりません。
「invalidate()」で更新できるかな? と、思いましたが無理でした。残念。

というわけで、このままでは、
ボタンを押すと、しばらく待った後で、次の処理に移る感じのもっさり感あふれる動作になりますです。


package com.example.test21; import android.os.Bundle; import android.app.Activity; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class MainActivity extends Activity { int count1=0; TextView text1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout layout1 = new LinearLayout(this); setContentView(layout1); text1 = new TextView(this); text1.setTextSize(100.0f); layout1.addView(text1); Button button1 = new Button(this); button1.setText("ファイルロード"); layout1.addView(button1); button1.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { for(count1=0;count1<100000;count1++){ text1.setText("LOAD " + String.valueOf(count1)); text1.invalidate();//すぐに再描画できない } text1.setText("次の処理に移ります"); } }); } }


  


そこで、別クラスのスレッドを作って、そっちに重い処理を任せるという感じです。
この場合、ロードしながら、すぐに次の処理に移ることができます。


package com.example.test21; import android.os.Bundle; import android.os.Handler; import android.app.Activity; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class MainActivity extends Activity { int count1=0; TextView text1; TextView text2; Handler handler1 = new Handler(); FileLoadThread1 FLThred1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout layout1 = new LinearLayout(this); layout1.setOrientation(LinearLayout.VERTICAL); //縦に並ぶ setContentView(layout1); Button button1 = new Button(this); button1.setText("ファイルロード"); layout1.addView(button1); text2 = new TextView(this); text2.setTextSize(30.0f); layout1.addView(text2); text1 = new TextView(this); text1.setTextSize(30.0f); layout1.addView(text1); button1.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { count1 = 0; //重い処理は別スレッドに任せる FLThred1 = new FileLoadThread1(); FLThred1.start(); text1.setText("次の処理に移ります"); } }); } private class FileLoadThread1 extends Thread { @Override public void run(){ while (count1<=1000) { count1 += 1; Log.d("TAG_THREAD",String.valueOf(count1)); //ここで何か重い処理をする //すこし待つ try{Thread.sleep(100); }catch(InterruptedException e){} //画面に経過を表示する仕事はhandlerに任せる handler1.post(new Runnable(){ @Override public void run() { if(count1>=1000){ text2.setText("ロード終了"); } else{ text2.setText("ロード中 " + String.valueOf(count1)); } } }); } } } @Override protected void onPause() { super.onPause(); //Thread停止 count1 = 1000; FLThred1 = null; } }




ファイルをロードしながら、次の処理を始められます。




scheduleAtFixedRateを使ってみる

以下、ストップウォッチ的なテストです。

「scheduleAtFixedRate」は、現在の時刻とカウントを同期してくれます。
「schedule」と比較すると、若干の誤差が見て取れます。
「schedule」は、1ずつ増えるなら1ずつ増えるようにカウントしていきますが、
「scheduleAtFixedRate」は、時間の経過と同期してカウントが増えていくようになります。
時計やストップウォッチには、「scheduleAtFixedRate」のほうが適しているわけですね。

「runOnUiThread」は、「handler1.post」の代わりになるものですが、名前からして使えそうな感じがあります。
使い込んでないのでよくわかりませんが。

実行させて観察すると、「schedule」の中では、「runOnUiThread」と、「handler1.post」では、誤差があるようです。
「scheduleAtFixedRate」の中では、時刻と同期しようとするので誤差はないようです。





package com.example.test22; import java.util.Date; import java.util.Timer; import java.util.TimerTask; //これをインポートする import android.os.Bundle; import android.os.Handler; import android.app.Activity; import android.graphics.Color; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import android.view.View.OnClickListener; public class MainActivity extends Activity { private TextView textView1; private TextView textView2; private TextView textView3; private TextView textView4; private TextView textView5; private TextView textView6; private TextView textView7; LinearLayout layout; Handler handler1 = new Handler(); Handler handler2 = new Handler(); Handler handler3 = new Handler(); private Timer timer1=null; private Timer timer2=null; private Timer timer3=null; private Timer timer4=null; private Timer timer5=null; private int count1 = 0; private int count2 = 0; private int count3 = 0; private int count4 = 0; private int count5 = 0; private int count6 = 0; private TimerThread1 tmThred1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); layout =new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); //縦に並ぶ //layout.setOrientation(LinearLayout.HORIZONTAL); //横に並ぶ Button bt1 = new Button(this); bt1.setText("START"); layout.addView(bt1); Button bt2 = new Button(this); bt2.setText("STOP"); layout.addView(bt2); textView7 = new TextView(this); textView7.setTextSize(20.0f); textView7.setTextColor(Color.argb(255, 255, 0,0)); layout.addView(textView7); textView5 = new TextView(this); textView5.setTextSize(20.0f); textView5.setTextColor(Color.argb(255, 0, 0, 255)); layout.addView(textView5); textView6 = new TextView(this); textView6.setTextSize(30.0f); textView6.setTextColor(Color.argb(255, 0, 0,255)); layout.addView(textView6); TextView text1 = new TextView(this); text1.setTextSize(15.0f); text1.setTextColor(Color.argb(255, 0, 0,0)); layout.addView(text1); text1.setText("schedule"); textView1 = new TextView(this); textView1.setTextSize(20.0f); textView1.setTextColor(Color.argb(255, 0,255, 0)); layout.addView(textView1); textView2 = new TextView(this); textView2.setTextSize(20.0f); textView2.setTextColor(Color.argb(255, 0,255, 0)); layout.addView(textView2); TextView text2 = new TextView(this); text2.setTextSize(15.0f); text2.setTextColor(Color.argb(255, 0, 0,0)); layout.addView(text2); text2.setText("scheduleAtFixedRate"); textView3 = new TextView(this); textView3.setTextSize(20.0f); textView3.setTextColor(Color.argb(255, 0, 255,0)); layout.addView(textView3); textView4 = new TextView(this); textView4.setTextSize(20.0f); textView4.setTextColor(Color.argb(255, 0, 255,0)); layout.addView(textView4); TextView text3 = new TextView(this); text3.setTextSize(15.0f); text3.setTextColor(Color.argb(255, 0, 0,0)); layout.addView(text3); text3.setText("scheduleAtFixedRateは時刻と同期"); setContentView(layout); //-------------------------------------------- //import java.util.TimerTask;をインポートすること timer5 = new Timer(); timer5.scheduleAtFixedRate(new TimerTask() { @Override public void run() { count5 += 1; runOnUiThread(new Runnable() { @Override public void run() { textView5.setText("scheduleAtFixedRate " + String.valueOf(count5)); textView6.setText((new Date()).toLocaleString()); } }); } },0,1000); //------------------------------------------------------------------- //ボタンのリスナー bt1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(timer1 == null) { timer1 = new Timer(); timer1.schedule(new TimerTask() { @Override public void run() { count1 += 1; runOnUiThread(new Runnable() { public void run() { textView1.setText("runOnUiThread " + String.valueOf(count1)); } }); } }, 0, 5); } //---------------------------------------- if(timer2 == null) { timer2 = new Timer(); timer2.schedule(new TimerTask() { @Override public void run() { count2 += 1; handler1.post(new Runnable(){ public void run() { textView2.setText("handler.post " + String.valueOf(count2)); } }); } }, 0, 5); } //---------------------------------------- if(timer3 == null) { timer3 = new Timer(); timer3.scheduleAtFixedRate(new TimerTask() { @Override public void run() { count3 += 1; runOnUiThread(new Runnable() { @Override public void run() { textView3.setText("runOnUiThread " + String.valueOf(count3)); } }); } },0,5); } //---------------------------------------- if(timer4 == null) { timer4 = new Timer(); timer4.scheduleAtFixedRate(new TimerTask() { @Override public void run() { count4 += 1; handler1.post(new Runnable(){ @Override public void run() { textView4.setText("handler.post " + String.valueOf(count4)); } }); } },0,5); } //------------------------ count6=0; tmThred1 = new TimerThread1(); tmThred1.start(); } }); //ボタンのリスナー bt2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(timer1 != null) { timer1.cancel(); timer1.purge(); timer1 = null; } if(timer2 != null) { timer2.cancel(); timer2.purge(); timer2 = null; } if(timer3 != null) { timer3.cancel(); timer3.purge(); timer3 = null; } if(timer4 != null) { timer4.cancel(); timer4.purge(); timer4 = null; } if(tmThred1 != null) { //スレッドをストップさせる count6=100; } } }); //------------------------------------------------------------------- tmThred1 = new TimerThread1(); tmThred1.start(); } //--------------------------------------------------------------------- private class TimerThread1 extends Thread { @Override public void run(){ while (count6 <= 100) { // 少し待つ try{Thread.sleep(100); }catch(InterruptedException e){} count6 += 1; handler3.post(new Runnable(){ @Override public void run() { if(count6>=100) textView7.setText("スレッド終了"); else textView7.setText("Thread + handler.post " + String.valueOf(count6)); } }); } } } }


















戻る