---
# System prepended metadata

title: 多執行緒的幾種方法
tags: [執行緒]

---

多執行緒的幾種方法
===

因連結中範例的程式碼無法直接複製執行，故有重新調整一下程式碼再貼上來：

* `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: `執行緒`