多執行緒的幾種方法 === 因連結中範例的程式碼無法直接複製執行,故有重新調整一下程式碼再貼上來: * `activity_main.xml` ```xml= <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/btANR" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onANRClick" android:text="ANR Click" /> <TextView android:id="@+id/tvANR" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ANR Click" /> <Button android:id="@+id/btUiThread" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onUiThreadClick" android:text="UI Thread Click" /> <TextView android:id="@+id/tvUiThread" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="UI Thread"/> <Button android:id="@+id/btViewPost" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onViewPostClick" android:text="View Post Click" /> <TextView android:id="@+id/tvViewPost" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View Post"/> <Button android:id="@+id/btHandler" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onHandlerClick" android:text="Handler Click" /> <TextView android:id="@+id/tvHandler" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Handler"/> <Button android:id="@+id/btAsyncTask" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="onAsyncTaskClick" android:text="AsyncTask Click" /> <ProgressBar android:id="@+id/progressBar" style="@android:style/Widget.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" android:progress="0" /> <TextView android:id="@+id/tvAsyncTask" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="AsyncTask" /> </LinearLayout> ``` * `MainActivity.java` ```java= package com.example.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.app.ProgressDialog; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView tvANR; private TextView tvUiThread; private TextView tvViewPost; private TextView tvHandler; private ProgressBar progressBar; private TextView tvAsyncTask; private static final String TAG = "MainActivity"; private Thread myThread; private MyAsyncTask myAsyncTask; private WorkThread workThread; private MyHandler myHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvANR = (TextView) findViewById(R.id.tvANR); tvUiThread = (TextView) findViewById(R.id.tvUiThread); tvViewPost = (TextView) findViewById(R.id.tvViewPost); tvHandler = (TextView) findViewById(R.id.tvHandler); progressBar = (ProgressBar) findViewById(R.id.progressBar); tvAsyncTask = (TextView) findViewById(R.id.tvAsyncTask); } // // 第一種:不使用執行緒,造成ANR(Application is Not Responding) // /* Android will display the ANR dialog in the following conditions: 1. No response to an input event within 5 seconds. 2. A BroadcastReceiver hasn't finished executing within 10 seconds. */ public void onANRClick(View view) { Log.d(TAG, "onANRClick"); long count = 0; while (true) { count++; tvANR.setText(String.valueOf(count)); } } // // 第二種:使用子執行緒來更新 UI 畫面,此按鈕按下後程式會直接 Crash,因為只有主執行緒可以更新 UI 畫面 // // only UI thread (main thread) can touch views public void onUiThreadClick(View view) { Log.d(TAG, "onUiThreadClick"); new Thread(new Runnable() { @Override public void run() { for (int count = 0; count < 10000; count++) { tvUiThread.setText(String.valueOf(count)); } } }).start(); } // // 第三種:使用子執行緒來更新 UI 畫面,透過 tvViewPost.post(new Runnable(){…}) 的方式,將其導回主執行緒來處理 // /* post() causes the Runnable to be added to the message queue. The runnable will be run on the user interface thread. */ public void onViewPostClick(View view) { Log.d(TAG, "onViewPostClick"); if (myThread == null) { myThread = new Thread(new Runnable() { int count; @Override public void run() { for (count = 0; count < 10000; count++) { try { Thread.sleep(100); } catch (InterruptedException e) { Log.e(TAG, e.toString()); } tvViewPost.post(new Runnable() { @Override public void run() { tvViewPost.setText(String.valueOf(count)); } }); } } }); myThread.start(); } } // // 第四種:搭配 Thread 與 Handler 物件 // 透過 Thread 物件的 handler.obtainMessage 方法來觸發 Handler 物件中的 handleMessage() 來更新 UI。 // public void onHandlerClick(View view) { Log.d(TAG, "onHandlerClick"); if (myHandler == null) { // 將要更新的元件(tvHandler)傳入 myHandler = new MyHandler(tvHandler); } if (workThread == null) { workThread = new WorkThread(myHandler, 10000); workThread.start(); } } // // 第五種:透過繼承 AsyncTask 類別,我們可以定義在 Thread 執行前、執行期間以及執行完成後,可以做甚麼事 // public void onAsyncTaskClick(View view) { Log.d(TAG, "onAsyncTaskClick"); if (myAsyncTask == null) { myAsyncTask = new MyAsyncTask(); myAsyncTask.execute(100); } } // 附帶的三個參數(Integer, Integer, String),分別對應到三個名為 doInBackground()、onProgressUpdate()、onPostExecute() 的 override 方法之傳入參數 private class MyAsyncTask extends AsyncTask<Integer, Integer, String> { private ProgressDialog progressDialog; @Override protected void onPreExecute() { progressDialog = new ProgressDialog(MainActivity.this); progressDialog.setMessage("Loading..."); progressDialog.show(); } @Override protected String doInBackground(Integer... params) { int count = params[0]; for (int i = 0; i <= count; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { Log.e(TAG, e.toString()); } // publishProgress(i) 會呼叫 onProgressUpdate() 方法,並將 i 參數代入 publishProgress(i); } // 最後 return 的值,亦會自動代入 onPostExecute() 的參數並呼叫。 return "Task completed!!"; } @Override protected void onProgressUpdate(Integer... values) { int progress = values[0]; if (progressDialog.isShowing()) { progressDialog.cancel(); } progressBar.setProgress(progress); tvAsyncTask.setText(String.valueOf(progress)); } @Override protected void onPostExecute(String result) { progressBar.setVisibility(View.GONE); tvAsyncTask.setText(result); } } } ``` * 第四種方式所使用的類別:`MyHandler.java` ```java= package com.example.myapplication; import android.os.Handler; import android.os.Message; import android.widget.TextView; public class MyHandler extends Handler { private TextView textView; public MyHandler(TextView textView) { this.textView = textView; } @Override public void handleMessage(Message message) { switch (message.what) { case Common.MESSAGE_NORMAL: switch (message.arg1) { case Common.TASK_ADD: int sum = message.arg2; String worker = message.obj.toString(); String text = worker + ", sum = " + String.valueOf(sum); textView.setText(text); break; } break; case Common.MESSAGE_ERROR: textView.setText(message.obj.toString()); break; } } } ``` * 第四種方式所使用的類別:`WorkThread.java` ```java= package com.example.myapplication; import android.os.Handler; import android.util.Log; public class WorkThread extends Thread { private static final String TAG = "WorkThread"; private static final String WORKER = "John"; private int count; private Handler handler; public WorkThread(Handler handler, int count) { this.handler = handler; this.count = count; } @Override public void run() { for (int sum = 1; sum <= count; sum++) { try { // 定義一延遲,用以防止 UI 更新過快 Thread.sleep(100); } catch (InterruptedException e) { Log.e(TAG, e.toString()); handler.obtainMessage( Common.MESSAGE_ERROR, WORKER + " got " + e.toString() ).sendToTarget(); } handler.obtainMessage( Common.MESSAGE_NORMAL, Common.TASK_ADD, sum, WORKER ).sendToTarget(); } } } ``` * 第四種方式所使用的類別:`Common.java` 定義 handler 在處理時需要用到的對應代碼 ```java= package com.example.myapplication; public class Common { public final static int MESSAGE_NORMAL = 0; public final static int MESSAGE_ERROR = 1; public final static int TASK_ADD = 0; public final static int TASK_SUBTRACT = 1; } ``` ## Ref. [『Multi – Threads』- 實現多執行緒的三種方式 (Post、Handler、AsyncTask)](https://xnfood.com.tw/multi-threads/) ###### tags: `執行緒`