私はAsyncTask
について読んでいて、以下の簡単なプログラムを試してみました。しかし,うまくいかないようです。どうすれば動くようになりますか?
public class AsyncTaskActivity extends Activity {
Button btn;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener((OnClickListener) this);
}
public void onClick(View view){
new LongOperation().execute("");
}
private class LongOperation extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
for(int i=0;i<5;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
return null;
}
@Override
protected void onPostExecute(String result) {
}
@Override
protected void onPreExecute() {
}
@Override
protected void onProgressUpdate(Void... values) {
}
}
}
バックグラウンド処理で5秒後にラベルを変更しようとしているだけです。
これは私の main.xml です。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="false"
android:max="10"
android:padding="10dip">
</ProgressBar>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Progress" >
</Button>
<TextView android:id="@+id/output"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Replace"/>
</LinearLayout>
別のスレッドを使ってGUIにアクセスしようとしていますね。これは一般的には良い方法ではありません。
AsyncTaskは、doInBackground()
内のすべてを別のスレッド内で実行しますが、そのスレッドはビューがあるGUIにアクセスできません。
preExecute()とpostExecute()を使えば、この新しいスレッドで重い処理が行われる前と後にGUIにアクセスすることができ、長い処理の結果をpostExecute()に渡して処理の結果を表示することもできます。
以下の行では、後でTextViewを更新しています。
TextView txt = findViewById(R.id.output);
txt.setText("Executed");
を onPostExecute()
に入れてください。
そうすると、doInBackground
が完了した後、TextViewのテキストが更新されるのがわかります。
EDIT: あなたのonClickリスナーが、どのViewが選択されたかをチェックしていないことに気づきました。これを行う最も簡単な方法はswitch文であることがわかります。混乱を避けるために、すべての提案を含む完全なクラスを以下に編集しました。
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings.System;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.view.View.OnClickListener;
public class AsyncTaskActivity extends Activity implements OnClickListener {
Button btn;
AsyncTask<?, ?, ?> runningTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = findViewById(R.id.button1);
// because we implement OnClickListener we only have to pass "this"
// (much easier)
btn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
// detect the view that was "clicked"
switch (view.getId()) {
case R.id.button1:
if (runningTask != null) runningTask.cancel(true);
runningTask = new LongOperation();
runningTask.execute();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// cancel running task(s) to avoid memory leaks
if (runningTask != null) runningTask.cancel(true);
}
private final class LongOperation extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// we were cancelled, stop sleeping!
}
}
return "Executed";
}
@Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed"); // txt.setText(result);
// might want to change "executed" for the returned string passed
// into onPostExecute() but that is upto you
}
}
}
正常に実行されていると思いますが、バックグラウンドのスレッドでUI要素を変更しようとしているので、それはできません。
呼び出しとAsyncTaskを以下のように修正してください。
呼び出しクラスについて
注:個人的には、AsyncTaskを拡張したクラスではなく、AsyncTaskのスレッドを実行する場所ではどこでもonPostExecute()
を使うことをお勧めします。特に、AsyncTaskが複数の場所で必要とされ、微妙に異なる結果を処理する場合には、コードが読みやすくなると思います。
new LongThread() {
@Override public void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText(result);
}
}.execute("");
**ロングスレッドクラス(AsyncTask を拡張):***。
@Override
protected String doInBackground(String... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Executed";
}
この2本の線を動かす。
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
をAsyncTask'のdoInBackground
メソッドから外し、onPostExecute
メソッドの中に入れます。あなたの AsyncTask
は以下のようになるはずです。
private class LongOperation extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
try {
Thread.sleep(5000); // no need for a loop
} catch (InterruptedException e) {
Log.e("LongOperation", "Interrupted", e);
return "Interrupted";
}
return "Executed";
}
@Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText(result);
}
}