多執行緒的幾種方法
===
因連結中範例的程式碼無法直接複製執行,故有重新調整一下程式碼再貼上來:
* `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: `執行緒`