AsyncTaskを使ってみる。
AsynkTaskは、画像ロードなど、何か重い処理をさせたいとき、アプリが重くなったり止まったりしないように、
非同期処理の別のタスクとして仕事させておくというものです。
AsynkTaskは、「タイマーとかスレッドのテスト」でテストしてみたThreadとは違い、
setTextでテキスト変更など、UI(画面)にアクセスする場合に、
いちいちHandler#post()でrun()の中にsetTextを入れ子にしなくてもいいらしいという、便利そうなものです。
タイマーでカウントさせつつ、別途にAsynkTaskでカウントを開始させるテスト。
「AsynkTask」ボタンを押すとカウントを始めます。
ボタンを何度も押すと、「Progress」の値がおかしくなります。
AsynkTaskが複数同時起動され、カウントが並行処理されたためです。
「AsyncTaskRunCount1」が、現在実行中のAsynkTaskの数です。
そもそもAsynkTaskから外部の変数を参照するのは、あまりよろしくないコードだと思いますが、
とりあえず「注意しないとこういう不具合が出るよ」というテストということで。
複数同時起動させないためには、ToAsyncTask1()の、「実行中ならリターン」のコードを有効にします。
MainActivity.java
package com.example.test33;
import java.util.Timer;
import java.util.TimerTask;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MainActivity extends Activity {
TextView text1;
TextView text2;
TextView text3;
TextView text4;
Timer timer1 = new Timer();
static int TimerCount1 = 0;
private AsyncTask mAsyncTask1;
static int AsyncTaskCount1=0;
static int AsyncTaskRunCount1=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout1 = new LinearLayout(this);
setContentView(layout1);
layout1.setOrientation(LinearLayout.VERTICAL); //縦に並ぶ
text1 = new TextView(this);
layout1.addView(text1);
text2 = new TextView(this);
layout1.addView(text2);
text3 = new TextView(this);
layout1.addView(text3);
text4 = new TextView(this);
layout1.addView(text4);
Button button1 = new Button(this);
button1.setText("AsyncTask");
layout1.addView(button1);
button1.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
//AsyncTaskでカウントをはじめる
ToAsyncTask1(TimerCount1);
}
});
//タイマータスクで一定時間おきにカウントする
ToTimerTask1();
}
private void ToTimerTask1(){
timer1.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
text1.setText("TimerCount1 / AsyncTaskRunCount1= "
+ String.valueOf(TimerCount1 ) + " / " + String.valueOf(AsyncTaskRunCount1));
}
});
TimerCount1 += 1;
}
}, 0, 1000);
}
public void ToAsyncTask1(int count1){
/*
// 実行中ならリターン
if (mAsyncTask1 != null && mAsyncTask1.getStatus() == AsyncTask.Status.RUNNING) {
Log.d("TAG_ASYNCTASK", "return - " + String.valueOf(AsyncTaskCount1));
return;
}
*/
mAsyncTask1 = new AsyncTaskTest1(count1);
//executeの引き数は、doInBackgroundのParamsの値//配列にできる
mAsyncTask1.execute(100L,1000L,5000L,0L);
}
//-----------------------------------------------------------------------------------
public class AsyncTaskTest1 extends AsyncTask{
int AsyncTaskStartCount1=0;
int AsyncTaskStopCount1=0;
//コンストラクタ
public AsyncTaskTest1(int count1){
AsyncTaskStartCount1=count1;
AsyncTaskRunCount1++;
}
@Override
protected void onPreExecute() {
//タスク開始
Log.d("TAG_ASYNCTASK", "onPreExecute");
text3.setText( "AsyncTask 起動タイミング = "+String.valueOf(AsyncTaskStartCount1));
}
@Override
protected String doInBackground(Long... params) {
//タスク実行
Log.d("TAG_ASYNCTASK", "doInBackground");
//paramsは、mAsyncTask1.executeの引数
for(int i=0;i
| |
AsyncTaskの使い方。
AsyncTaskを使うには、はじめに3つのパラメータを設定する必要があります。
AsyncTaskの内部で使用されるパラメータの型です。
Params | executeの引数 | doInBackgroundのparamsに渡される | 可変なので配列として受け取れる |
Progress | publishProgressの引数 | onProgressUpdateのvaluesに渡される | 可変なので配列として受け取れる |
Result | doInBackgroundの戻り値 | onPostExecuteのresultに渡される | |
Params、Progress、Resultの型は、任意に設定できます。
実際にコードに記述する場合は、型を書きます。
AsyncTaskTest1の例では、Long, Integer, Stringの3つの型を定義しています。
値を渡す必要がなければ、Voidを使います。
AsyncTaskのクラスを作る。
はじめに、AsyncTaskを継承したクラスを作ります。
AsyncTaskのパラメータとして設定したLong, Integer, Stringの型は、
doInBackground(),onProgressUpdate(),onPostExecute()から渡されます。
記述するときは、決めたパラメータと違う型を書くとエラーになるので注意しましょう。
public class AsyncTaskTest1 extends AsyncTask{
//コンストラクタ
public AsyncTaskTest1(int count1){
}
@Override
protected void onPreExecute() {
//タスク開始
}
@Override
protected String doInBackground(Long... params) {
//タスク実行
//paramsは、executeの引数
int values = 0;
String result = null;
publishProgress(values);
//処理が終了したとき、onPostExecuteのresultに渡す
return result;
}
@Override
protected void onProgressUpdate(Integer... values) {
//valuesは、publishProgressの引数
//進捗状況が得られるので、
//プログレスバーなどにvaluesの値を入れる
}
@Override
protected void onPostExecute(String result) {
//処理が終了した
//resultは、doInBackgroundからの戻り値
}
}
| |
AsyncTaskTest1の呼び出し方。
private AsyncTask mAsyncTask1;
..........
mAsyncTask1 = new AsyncTaskTest1(count1);
//executeの引き数は、doInBackgroundのParamsの値//配列にできる
mAsyncTask1.execute(100L,1000L,5000L,0L);
| |
次のようにも、書けます。
new AsyncTaskTest1(count1).execute(100L);
| |
次のようにも、書けます。
new AsyncTask() {
.....................
@Override
protected Void doInBackground(Void... params) {
.....................
}
.....................
}.execute(100L);
| |
count1は、コンストラクタに渡されます。
executeの引数が、doInBackground()に渡されます。
doInBackground()で実行する処理を書きますが、進捗状況をUIに知らせようと、ここにsetTextなんかを書くとエラーになります。
プログレスバーなどを出して、進捗状況を知らせる場合、
doInBackground()の中で、publishProgress()を呼び出します。
するとonProgressUpdate()が呼ばれますので、そこでUIの操作ができます。
プログレスダイアログを出す。
AsyncTaskから、プログレスダイアログを出すテスト。
スピナーでくるくる、進捗不確定モード、プログレスバー。
以下を追加修正する。
import android.app.ProgressDialog;
import android.content.Context;
................
public void ToAsyncTask1(int count1){
mAsyncTask1 = new AsyncTaskTest1(this,count1);
//executeの引き数は、doInBackgroundのParamsの値//配列にできる
mAsyncTask1.execute(100L,1000L,5000L,0L);
}
................
public class AsyncTaskTest1 extends AsyncTask{
int AsyncTaskStartCount1=0;
int AsyncTaskStopCount1=0;
ProgressDialog dialog;
Context context;
//コンストラクタ
public AsyncTaskTest1(Context context,int count1){
this.context = context;
AsyncTaskStartCount1=count1;
AsyncTaskRunCount1++;
}
@Override
protected void onPreExecute() {
//タスク開始
Log.d("TAG_ASYNCTASK", "onPreExecute");
text3.setText( "AsyncTask 起動タイミング = "+String.valueOf(AsyncTaskStartCount1));
//ダイアログを出す
dialog = new ProgressDialog(context);
dialog.setTitle("おまちください");
dialog.setMessage("実行中");
//dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);//くるくる
//dialog.setIndeterminate(true);//進捗不確定モード
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//プログレスバー
dialog.setMax(100);
dialog.setProgress(0);
dialog.show();
}
@Override
protected String doInBackground(Long... params) {
//タスク実行
Log.d("TAG_ASYNCTASK", "doInBackground");
//paramsは、mAsyncTask1.executeの引数
for(int i=0;i
| |
「戻るキー」でキャンセルさせる。
「戻るキー」でキャンセルさせるために、リスナーを登録する。
以下のように、ダイアログに、setOnCancelListenerを登録すると、
「戻るキー」で、ダイアログが閉じるようになる。
import android.content.DialogInterface.OnCancelListener;
................
protected void onPreExecute() {
//ダイアログを出す
dialog = new ProgressDialog(context);
dialog.setCancelable(true);
dialog.setOnCancelListener(new OnCancelListener(){
@Override
public void onCancel(DialogInterface dialog) {
// TODO 自動生成されたメソッド・スタブ
Log.d("TAG_ASYNCTASK", "onCancel");
cancel(true);
}
});
}
| |
この場合、ダイアログが閉じても、AsynkTaskは続行されたままになる。
AsynkTaskを止める場合は、cancel(true)を使う。
しかし、上記のようにonCancel(DialogInterface dialog) {}の中から、直接AsynkTaskのcancel(true)を呼び出そうとするとエラーになる。
そこで、AsyncTaskTest1に、OnCancelListenerを、implements する。
AsyncTaskTest1から直接onCancel(DialogInterface dialog) を呼び出し、その中で、AsynkTaskのcancel(true)を呼び出せるようにする。
cancel(true)が実行されると、AsyncTaskのonCancelled()が呼び出される。
が、何もしなければ、doInBackground()で処理は実行されたままになるので、
doInBackground()の処理を中断させるコードを書かなければいけない。
isCancelled()で、キャンセルされたか知ることができるので、以下のように書いてみる。
import android.content.DialogInterface.OnCancelListener;
................
public class AsyncTaskTest1 extends AsyncTask implements OnCancelListener{
int AsyncTaskStartCount1=0;
int AsyncTaskStopCount1=0;
ProgressDialog dialog;
Context context;
//コンストラクタ
public AsyncTaskTest1(Context context,int count1){
this.context = context;
AsyncTaskStartCount1=count1;
AsyncTaskRunCount1++;
}
@Override
protected void onPreExecute() {
//タスク開始
Log.d("TAG_ASYNCTASK", "onPreExecute");
text3.setText( "AsyncTask 起動タイミング = "+String.valueOf(AsyncTaskStartCount1));
//ダイアログを出す
dialog = new ProgressDialog(context);
dialog.setTitle("おまちください");
dialog.setMessage("実行中");
//dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);//くるくる
//dialog.setIndeterminate(true);//進捗不確定モード
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//プログレスバー
dialog.setMax(100);
dialog.setProgress(0);
//ダイアログをキャンセルできるようにする
dialog.setCancelable(true);
dialog.setOnCancelListener(this);
dialog.show();
}
@Override
protected String doInBackground(Long... params) {
//タスク実行
Log.d("TAG_ASYNCTASK", "doInBackground");
//paramsは、mAsyncTask1.executeの引数
for(int i=0;i
| |
ログを見ると、onCancell、isCancelled、onCancelledの順番で呼ばれて、中断できたことになる。
onCancel
isCancelled
onCancelled
cancel(false)にすると、onCancelledが、isCancelledよりも先に呼ばれる場合もあるように見える?
場合によって使いわければいいということかも知れない。
AsyncTaskのクラスを作らずに記述する。
AsyncTaskTest1クラスを作らずに、次のようにも、書けます。
「implements OnCancelListener」は、不要です。
特に問題なければ、使い勝手によって見やすいコードを書けばいいと思います。
import android.content.DialogInterface.OnCancelListener;
............................
public void ToAsyncTask1(int count1){
new AsyncTask() {
int AsyncTaskStartCount1=0;
int AsyncTaskStopCount1=0;
ProgressDialog dialog;
Context context;
@Override
protected void onPreExecute() {
//タスク開始
Log.d("TAG_ASYNCTASK", "onPreExecute");
context = MainActivity.this;
AsyncTaskStartCount1=TimerCount1;
AsyncTaskRunCount1++;
text3.setText( "AsyncTask 起動タイミング = "+String.valueOf(AsyncTaskStartCount1));
//ダイアログを出す
dialog = new ProgressDialog(context);
dialog.setTitle("おまちください");
dialog.setMessage("実行中");
//dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);//くるくる
//dialog.setIndeterminate(true);//進捗不確定モード
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//プログレスバー
dialog.setMax(100);
dialog.setProgress(0);
//ダイアログをキャンセルできるようにする
dialog.setCancelable(true);
dialog.setOnCancelListener(new OnCancelListener(){
@Override
public void onCancel(DialogInterface dialog) {
// TODO 自動生成されたメソッド・スタブ
//ダイアログをキャンセルしたときに呼ばれる
Log.d("TAG_ASYNCTASK", "onCancel");
//AsyncTaskをキャンセルする
cancel(true);
}
});
dialog.show();
}
@Override
protected String doInBackground(Long... params) {
//タスク実行
Log.d("TAG_ASYNCTASK", "doInBackground");
//paramsは、mAsyncTask1.executeの引数
for(int i=0;i
| |
端末を回転させたときの対応。
端末を回転させると、Activityが破棄されて再生成されるので、いろいろとエラーが起きます。
まず、ダイアログが消えます。
処理が終わったとき、onPostExecuteで、ダイアログが出ていたら消すという処理をしていますが、
しかし、「if(dialog != null && dialog.isShowing())」では、ダイアログは出たままという判定をされて、
「dialog.dismiss();」でエラーになってしまいます。
ダイアログフラグメントにすれば大丈夫なのでしょうが、とりあえず、
処理中にダイアログが消えたら、もういっかいダイアログを出してみるという処理をすることにします。
ダイアログのインスタンスを、「static ProgressDialog dialog;」として、フィールドで設定。
onPause()で、ダイアログが出てたら消す。
onStart()で、AsyncTaskCount1の値が処理中だったら、ダイアログを出す。
あと、TextViewはstaticにしてみます。
これでとりあえず、大丈夫っぽいです。
問題が起きたら、また対策しましょう。
//端末を回転させたときに、ダイアログが消えるので対策する
//MainActivityのフィールドで、ダイアログのインスタンスをstaticで作る
static ProgressDialog dialog;
....................
//AsyncTask内でインスタンスを作らない
//ProgressDialog dialog;
....................
@Override
protected void onPause() {
super.onPause();
//端末の回転対応
//ダイアログが出ていたら消す
if(dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
}
@Override
protected void onStart() {
super.onStart();
//端末の回転対応
//処理中なら、ダイアログを出す
if(AsyncTaskCount1%100!=0){
dialog.show();
}
}
| |
MainActivity.java
package com.example.test33;
import java.util.Timer;
import java.util.TimerTask;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.content.DialogInterface.OnCancelListener;
public class MainActivity extends Activity {
static TextView text1;
static TextView text2;
static TextView text3;
static TextView text4;
Timer timer1 = new Timer();
static int TimerCount1 = 0;
//private AsyncTask mAsyncTask1;
static int AsyncTaskCount1=0;
static int AsyncTaskRunCount1=0;
//端末を回転させたときに、ダイアログが消えるので対策する
//MainActivityのフィールドで、ダイアログのインスタンスをstaticで作る
static ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout1 = new LinearLayout(this);
setContentView(layout1);
layout1.setOrientation(LinearLayout.VERTICAL); //縦に並ぶ
text1 = new TextView(this);
layout1.addView(text1);
text2 = new TextView(this);
layout1.addView(text2);
text3 = new TextView(this);
layout1.addView(text3);
text4 = new TextView(this);
layout1.addView(text4);
Button button1 = new Button(this);
button1.setText("AsyncTask");
layout1.addView(button1);
button1.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
//AsyncTaskでカウントをはじめる
ToAsyncTask1(TimerCount1);
}
});
//タイマータスクで一定時間おきにカウントする
ToTimerTask1();
}
@Override
protected void onPause() {
super.onPause();
//端末の回転対応
//ダイアログが出ていたら消す
if(dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
}
@Override
protected void onStart() {
super.onStart();
//端末の回転対応
//処理中なら、ダイアログを出す
if(AsyncTaskCount1%100!=0){
dialog.show();
}
}
private void ToTimerTask1(){
timer1.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
text1.setText("TimerCount1 / AsyncTaskRunCount1= "
+ String.valueOf(TimerCount1 ) + " / " + String.valueOf(AsyncTaskRunCount1));
}
});
TimerCount1 += 1;
}
}, 0, 1000);
}
public void ToAsyncTask1(int count1){
new AsyncTask() {
int AsyncTaskStartCount1=0;
int AsyncTaskStopCount1=0;
//端末を回転させたときに、ダイアログが消えるので対策する
//AsyncTask内でインスタンスを作らない
//ProgressDialog dialog;
Context context;
@Override
protected void onPreExecute() {
//タスク開始
Log.d("TAG_ASYNCTASK", "onPreExecute");
context = MainActivity.this;
AsyncTaskStartCount1=TimerCount1;
AsyncTaskRunCount1++;
text3.setText( "AsyncTask 起動タイミング = "+String.valueOf(AsyncTaskStartCount1));
//ダイアログを出す
dialog = new ProgressDialog(context);
dialog.setTitle("おまちください");
dialog.setMessage("実行中");
//dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);//くるくる
//dialog.setIndeterminate(true);//進捗不確定モード
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//プログレスバー
dialog.setMax(100);
dialog.setProgress(0);
//ダイアログをキャンセルできるようにする
dialog.setCancelable(true);
dialog.setOnCancelListener(new OnCancelListener(){
@Override
public void onCancel(DialogInterface dialog) {
// TODO 自動生成されたメソッド・スタブ
//ダイアログをキャンセルしたときに呼ばれる
Log.d("TAG_ASYNCTASK", "onCancel");
//AsyncTaskをキャンセルする
cancel(true);
}
});
dialog.show();
}
@Override
protected String doInBackground(Long... params) {
//タスク実行
Log.d("TAG_ASYNCTASK", "doInBackground");
//paramsは、mAsyncTask1.executeの引数
for(int i=0;i
| |