0909Android基礎多線程編程

Android多線程編程

  有一些耗時操作如果在主線程中進行會阻塞主線程,所以需要將這類操作放到子線程中操作。
  Android的UI操作必須在主線程中進行,否則會出現異常。但是有時,我們必須在子線程中執行一些耗時任務,然後根據任務的執行結果來更新UI,這裏就用到了一套異步消息處理機制。

異步消息處理機制

這裏寫圖片描述

異步處理四個部分

  Android中的異步處理消息機制主要由四部分組成。
  

  • Message
      Message是在線程之間傳遞的消息,他可以在內部攜帶少量的信息,用於在不同線程之間交換數據。

  • Handler
      處理者,用於發送和處理消息。發送消息通常調用Handler的sendMessage()方法,發出的消息最終會傳遞到handlerMessage()方法中

  • MessageQueue
      MessageQueue是消息隊列的意思,主要用於存放所有通過Handler發送的消息,這部分消息會一直存在於消息隊列中,等待被處理。每個線程中只有一個MessageQueue對象。
  • Looper
      Looper是每個線程中的MessageQueue的管家,調用loop()方法後,就會進入到一個無限循環中,每當發現MessageQueue的一條消息,就會將它取出,並傳遞到Handler的handlerMessage()方法中。每個線程中也只有一個Looper對象。

異步處理整體流程

  首先在主線程中創建一個Handler對象,並重寫handlerMessage方法。然後當子線程中需要進行UI操作時,就創建一個Message對象,並通過handler將這條消息發出去。之後這條消息會被添加到MessageQueue中等待被處理,Looper一直嘗試從MessageQueue中取出待處理消息,最後分發會Handler的handleMessage方法中。由於Handler是在主線程中進行的,所以可以進行UI操作。

異步處理Demo

線程中:

new Thread(new Runnable() {
                    @Override
                    public void run() {
                        count=60;
                        while (count>0){
                            count--;
//               沒有Message對象的話再創建一個,有的話用之前的對象
                            Message msg = handler.obtainMessage()
//                            Message msg=new Message();
//                            給這條信息加一個標誌
                            msg.what=TIME_DESK;
//                            將int型的count轉換成string類型的值
                            msg.obj=count+"秒";
                            handler.sendMessage(msg);
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();

活動中(主線程中)

private Handler handler=new Handler(){

      @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
            case TIME_DESK:
                String time= (String) msg.obj;
                mBtnTimeDesk.setText(time);
                break;
        }
    }
};  

這裏寫圖片描述

通過sendEmptyMessage簡化代碼,效果和上面的一樣
按鍵監聽器中

             count = 60;
//                發送空消息,僅僅用來傳遞,和延時
                handler.sendEmptyMessageDelayed(TIME_DESK,100);

主線程中

    private Handler handler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case TIME_DESK:
//                    String time= (String) msg.obj;
                    count--;
                    if (count >= 0) {
                        mBtnTimeDesk.setText(count + "秒");
//                    形成循環,延時0.1秒
                        handler.sendEmptyMessageDelayed(TIME_DESK,100);
                    }
                    break;
            }
        }
    };

從子線程往主線程發送消息Demo

  主線程給子線程發消息,在子線程中寫handler並且其中必須有一個
Looper隊列。子線程給主線程發消息時主線程自帶looper,不需要再加
兩個按鍵,按鍵一啓動子線程,按鍵二將消息傳入到子線程的handler中。

public class MainActivity extends Activity implements View.OnClickListener {
    private Button mBtnTimeDesk;
    private int count;
    private static final int TIME_DESK = 0x23;
    private Button mBtnSendMessage;
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtnTimeDesk = (Button) findViewById(R.id.btn_timedesk);
        mBtnSendMessage = (Button) findViewById(R.id.btn_sendmessage);

        mBtnTimeDesk.setOnClickListener(this);
        mBtnSendMessage.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_timedesk:
                MyThread thread=new MyThread();
                thread.start();

                break;
            case R.id.btn_sendmessage:
                handler.sendEmptyMessage(0);
                break;
            default:
                break;
        }

    }
    class MyThread extends Thread{
        @Override
        public void run() {
            Looper.prepare();
            handler=new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    Log.d("子線程收到信息", "(~ ̄▽ ̄)~");
                }
            };
            Looper.loop();
        }
    }
}

  先點擊btn_timedesk(啓動線程)再點擊sendMessage發送信息
  不能夠放在一個按鍵中:如果放到一個鍵中

MyThread thread=new MyThread();
thread.start();
handler.sendEmptyMessage(0);

  handler可能會報空指針,因爲handler對象的初始化實在子線程進行的,如果子線程沒有new出來handler就調用handler.sendEmptyMessage(0)就會報空指針。
  子線程中public void handleMessage(Message msg)一直等待接收消息。子線程中接受消息要放在Looper.prepare()和Looper.loop()裏面實現循環。第一個例子中從子線程往主線程發送消息,因爲主線程會自動將handler添加到looper中,所以不用像子線程一樣手動添加。

AsyncTask

  AsyncTask的實現原理基於異步消息處理機制,只是Android幫我們做了很好的封裝而已。

創建類繼承AsyncTask

  AsyncTask是一個抽象類,所以如果想使用它,就必須創建一個子類去繼承他,繼承時要爲AsyncTask類指定三個泛型參數,這三個參數用途如下:

  • Params
      在執行AsyncTask時需要傳入的參數,可以用於後臺服務中使用。
  • Progress
      後臺任務執行時,如果需要在界面上顯示當前的進度,則使用這裏指定的泛型作爲進度單位。
  • Result
      當任務執行完畢後,如果需要對結果進行返回,則使用這裏指定的泛型作爲返回值類型。
      最簡單的自定義AsyncTask就可以這樣寫。
  class MyAsyncTask extends AsyncTask<String, String, String> {...}

  第一個String表示執行AsyncTask的時候不需要傳入參數給後臺任務。第二個String表示使用String類型數據作爲進度顯示單位。第三個String表示使用String類型數據來反饋執行結果。

複寫AsyncTask的方法

  經常需要重寫的方法有以下四個:

  • onPreExecute()
      這個方法會在後臺任務開始執行之前調用,用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。
  • doInBackground(Params…)
      這個方法中的所有代碼都會在子線程中運行,處理所有耗時任務。任務完成後通過return返回結果,如果第三個泛型參數是void,就不必返回執行結果。這個方法不可以進行UI操作。反饋當前任務的執行進度,可以調用publishProgress(Progress…)方法來完成。
  • onProgressUpdate(Progress…)
      後臺調用了publishProgress(Progress…)方法後,本方法會被調用,方法中的參數就是後臺任務傳遞過來的。本方法中可以對UI進行操作,利用參數中的數值就可以對界面元素進行相應的更新。
  • onPostExecute(Result)
      後臺執行完畢並且return語句返回時,本方法會被調用。返回的詩句會作爲參數傳遞到此方法中,可以利用返回值進行一些UI操作。

啓動AsyncTask

   啓動發法非常簡單。

new MyAsyncTask().execute();

AsyncTask的Demo

  本例實現點擊按鈕後進度條填滿的動作
佈局

<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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:gravity="center"
    tools:context=".MainActivity">

    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"
        android:progress="100"
        />
    <Button
        android:id="@+id/btn_start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="開始執行"/>
</LinearLayout>

活動

public class MainActivity extends Activity implements View.OnClickListener {
    private Button mBtnStart;
    private ProgressBar mProgerssBar;
    private int count;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnStart = (Button) findViewById(R.id.btn_start);
        mProgerssBar = (ProgressBar) findViewById(R.id.progress);

        mBtnStart.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_start:
                MyAsyncTask myAsyncTask=new MyAsyncTask();
                myAsyncTask.execute("");
                break;
            default:
                break;
        }
    }

    //  不必建立線程即可進行耗時操作
    class MyAsyncTask extends AsyncTask<String, String, String> {

        //        後臺操作
        @Override
        protected String doInBackground(String... params) {
            count = 0;
            while (count < 101) {
                count++;
                publishProgress(count+"");//將數值傳入中間操作
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            String s=count+"";
            return s;
        }

        //      中間操作
        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
//            values中存儲變量。變量爲publishProgress傳入的的參數
            int value=Integer.parseInt(values[0]);
            mProgerssBar.setProgress(value);
        }

        //      得到後臺操作返回的值
        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            mBtnStart.setText("完成操作");
        }
    }
}

效果
這裏寫圖片描述

AsyncTask小總結

  在doInBackground()方法中去執行具體的耗時任務,在onProgressUpdate()方法中進行UI操作,在onPostExcute()方法中去執行一些任務的收尾工作。

參考文獻:《第一行代碼》,作者郭霖

發佈了56 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章