因為 Android UI 不是 Thread safe,因此所有的 UI 操作都得透過 Main Thread,而當費時的運算過程或者結果不會影響到 UI 的話,那只要起一個 Thread 去處理就可以了,但是當過程或結果需要與 UI 互動時,就得用上 AsyncTask。
AsyncTask 會起一個背景執行的 Thread,並提供前中後三個 callback 供開發者使用。
至於 AsyncTask 與同樣可以更新 UI 的 Handler + Worker Thread 的差異,改天再做研究。
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="onClick" android:text="開始計算" /> <TextView android:id="@+id/tv" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>Calculator
public class Calculator extends AsyncTask<List<Integer>, Integer, Integer> { private static final String TAG = "Calculator"; private Context ctx; private TextView tv; private Integer result; private Integer progress; public Calculator(Context ctx) { this.setContext(ctx); } public void setContext(Context ctx) { // 用來更新 UI this.ctx = ctx; this.tv = (TextView) ((Activity) this.ctx).findViewById(R.id.tv); this.progressUI(); } @Override protected Integer doInBackground(List<Integer>... params) { LogUtils.thread("執行計算..."); // 在 Worker Thread 裡執行,所以不可更新 UI this.result = 0; Integer p; for (int i = 0; i < params[0].size(); i++) { p = params[0].get(i); this.result += p.intValue(); LogUtils.thread("計算過程..." + this.result); // 更新進度 this.publishProgress(100 * (i + 1) / params[0].size()); try { Thread.sleep(1000); } catch (InterruptedException e) { } } LogUtils.thread("執行計算完成..." + this.result); return this.result; } @Override protected void onPreExecute() { super.onPreExecute(); LogUtils.thread("計算前..."); // 在 Main Thread 裡執行,可更新 UI this.progress = 0; } @Override protected void onPostExecute(Integer result) { super.onPostExecute(result); this.result = result; LogUtils.thread("計算結果..." + this.result); // 在 Main Thread 裡執行,可更新 UI this.syncUI(); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); this.progress = values[0]; LogUtils.thread("更新進度..." + this.progress); // 在 Main Thread 裡執行,可更新 UI this.progressUI(); } public void syncUI() { LogUtils.thread("更新 UI..."); this.tv.setText("計算結果..." + this.result); } private void progressUI() { LogUtils.thread("更新進度..."); this.tv.setText("目前進度..." + this.progress); } }AsyncTaskActivity
public class AsyncTaskActivity extends Activity { private static final String TAG = "AsyncTaskActivity"; private Calculator task; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 重新連線 this.task = (Calculator) this.getLastNonConfigurationInstance(); if (this.task != null) { Log.d(TAG, "與 AsyncTask 重新連線..."); this.task.setContext(this); // 在 Activity 重起過程中已計算完畢,所以要呼叫 AsyncTask 來更新 UI if (this.task.getStatus() == AsyncTask.Status.FINISHED) { this.task.syncUI(); } } } @Override public Object onRetainNonConfigurationInstance() { // 保留 Activity 與 AsyncTask 的關聯 // 供 Activity 因故重起後,可以找回已在執行的 AsyncTask return this.task; } public void onClick(View v) { if (this.task != null) { AsyncTask.Status status = this.task.getStatus(); Log.d(TAG, "目前狀態:" + status); if (status != AsyncTask.Status.FINISHED) { Log.d(TAG, "前一計算未完成,不可重起計算"); return; } Log.d(TAG, "前一計算已完成,可重起計算"); } Log.d(TAG, "新計算..."); this.task = new Calculator(this); List<Integer> params = new ArrayList<Integer>(); for (int i = 0; i < 10; i++) { params.add(i); } // 只能呼叫一次 execute(...) this.task.execute(params); // 可以手動中斷 // this.task.cancel(true); // 判斷是否終止 // if (this.task.isCancelled()){ } // 也可以取得計算結果 // Integer result = this.task.get(); } }LogUtils
public class LogUtils { private static final String TAG = "LogUtils"; public static final void thread() { LogUtils.thread(null); } public static final void thread(String msg) { Thread t = Thread.currentThread(); Log.d(TAG, "<" + t.getName() + ">id: " + t.getId() + ", Priority: " + t.getPriority() + ", Group: " + t.getThreadGroup().getName() + (msg != null ? ", Msg: " + msg : "")); } }相關文章
原來Android有內建這個東西,我之前都用Thread自己去寫、去調...好像還有一個ActitivyThread的API,我改天也來研究看看
回覆刪除原來還有這個API可以使用啊,我之前都是用Thread自己寫、自己修runtime error...
回覆刪除