Android異步消息處理(一)--》Handler和多線程深入

不得不說大神的博客就是不一樣,如果對Handler一點都不瞭解,可以先先看一下我的這裏全面但是顯得臃腫的內容。如果基礎比較好,請參考大神的博客http://blog.csdn.net/lmj623565791/article/details/38377229

Android中的異步消息處理中Handler機制是一種很重要的機制

我們需要明白:

【1】Handler是什麼?怎麼用?爲什麼要用Handler

【2】android爲什麼要設計只能通過Handler機制更新UI呢

【3】Handler的原理是什麼?

【4】使用Handler遇到的問題

【5】如何實現一個與線程相關的Handler

【6】HandlerThread又是什麼呢

【7】如何在主線程給子線程發送消息呢

【8】android中更新UI的幾種方式

【9】非UI線程真的不能更新UI呢?

一.什麼是Handler,怎麼用,爲什麼要用?

我們需要明確以下幾點:

【1】Android更新UI(例如從網絡下下載圖片)不要阻塞UI線程

【2】不要在UI線程之外訪問Android UI工具包

因爲如果多個線程去更新UI,並且都沒有加鎖機制,會產生更新界面錯亂。而加鎖後性能又會下降。換一個思路:我們根本不用去關心多線程的問題,所有的更新UI操作,均在主線程的消息隊列中去輪詢處理。

Handler是android給我們提供的用來更新UI的一套機制,也是一套消息處理的機制,我們可以發送消息,也可以通過它處理消息。見下面一段例子,通過Handler發送一個消息,實現進行引導頁3S後進入主頁面。

static class SplashHandler extends Handler {
        WeakReference<SplashActivity> splashActivityWeakReference;
        SplashHandler(SplashActivity activity) {
            splashActivityWeakReference = new WeakReference<SplashActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            SplashActivity activity = splashActivityWeakReference.get();
            switch (msg.what) {
                case START_MAIN_ACTIVITY:
                    SharedPreferences settings = activity.getSharedPreferences(Config.PREFERENCES_FILE_NAME, MODE_PRIVATE);
                    isFirstRun = settings.getBoolean(FIRST_RUN, true);
                    if (isFirstRun) {
                        activity.startActivity(new Intent(activity, GuideActivity.class));
                    } else {
                        activity.startActivity(new Intent(activity, com.touch.zjzplayer.activity.MainActivity.class));
                    }
                    activity.finish();
                    break;
            }
        }
    }
在onCreate()方法中:

SplashHandler handler = new SplashHandler(this);
handler.sendEmptyMessageDelayed(START_MAIN_ACTIVITY, 3000);



實際上,我們的Activity的生命週期中,它是如何去根據不同的情況調用到onCreate(),onDestroy()等方法的呢?答案就是Handler機制,簡單的理解就是通過handler發送消息,然後進行相應的處理。

Android在設計的時候,就封裝了一套消息創建、傳遞、處理機制,如果不遵循這樣的機制的話,就沒有辦法更新UI信息的,就會拋出異常信息。其實消息處理機制也可以自己設計,但是比較複雜。

Handler有以下幾種發送消息的重要方法(其中的msg.what是消息對象Message msg的標識信息,通常爲一個整型常量)

sendEmptyMessageDelayed(int msg.what, long delayMillis);  //發送一個空消息

sendMessageAtTime(int msg.what, System.currentTimeMillis()+3000);  //在一個特定時間發送,該例子爲當前時間3s後,

sendMessageDelayed(int msg.what, 2000);  //延遲2s後執行,上一段代碼中有案例

post(Runable runable);  //執行一個線程  

postDelayed(Runable runable, long delayMillis);  //延遲特定時間後執行某線程

handler.removeCallbacks(Runable runable);  //移除線程。

handler.sendMessage(Message msg);  //可以指定msg.what  msg.obj(攜帶一個Object對象)   msg.arg1   msg.arg2 (這兩個參數可以攜帶一些整型數據)  

例如下面的圖片輪播  

<pre name="code" class="java">public class TestActivity extends Activity{
    private Handler handler = new Handler();
    private int[] imgs = {R.drawable.guide_pager_one, R.drawable.guide_pager_two, R.drawable.guide_pager_three};
    private int index;
    private ImageView img;
    private MyRunable myRunable = new MyRunable();
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        img = (ImageView) findViewById(R.id.img);
        handler.postDelayed(myRunable, 2000);
    }

    class MyRunable implements Runnable {
        @Override
        public void run() {
            img.setImageResource(imgs[index]);
            index++;
            index = index % 3;
            handler.postDelayed(myRunable, 2000);
        }
    }

    //點擊按鈕,關閉輪播
    public void closeRunnable(View view) {
        handler.removeCallbacks(myRunable);
    }
}
資源文件:

<pre name="code" class="html"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="關閉輪播"
        android:onClick="closeRunnable"/>

    <ImageView
        android:id="@+id/img"
        android:layout_width="100dp"
        android:layout_height="200dp" />

</LinearLayout>
還有一種用法是攔截不合法的消息,在Handler構造函數中有一個是Handler handler = new Handler(Callback callback); 例如下面代碼:

public class TestActivity extends Activity{
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Toast.makeText(TestActivity.this, ""+1, Toast.LENGTH_SHORT).show();
            return false;  //false表示沒有攔截,true表示已經攔截,就不會執行下面的handleMessage()方法
        }
    }){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Toast.makeText(TestActivity.this, ""+2, Toast.LENGTH_SHORT).show();
        }
    };
    private int[] imgs = {R.drawable.guide_pager_one, R.drawable.guide_pager_two, R.drawable.guide_pager_three};
    private int index;
    private ImageView img;
    private MyRunable myRunable = new MyRunable();
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        img = (ImageView) findViewById(R.id.img);
        handler.postDelayed(myRunable, 2000);
    }

    class MyRunable implements Runnable {
        @Override
        public void run() {
            img.setImageResource(imgs[index]);
            index++;
            index = index % 3;
            handler.postDelayed(myRunable, 2000);
        }
    }

    //點擊按鈕,關閉輪播
    public void closeRunnable(View view) {
        handler.removeCallbacks(myRunable);
        handler.sendEmptyMessage(0);
    }
}


二.Handler原理

我們要清楚Handler異步消息處理的四大部分

1.Message  它是在線程之間傳遞的信息,用於在不同線程之間交換數據。

2.MessageQueue  消息隊列,遵循先進先出原則,主要用於存放所有通過Handler發送的消息。每個線程中只會有一個MessageQueue對象。Android啓動程序會在創建Looper時,在UI線程創建一個MessageQueue。Handler發送消息就是將消息添加到MessageQueue中

3.Handler   用於發送和處理消息的。

4.Looper  是每個線程MessageQueue的管家,調用Looper的loop()方法後,就會進入死循環,每當發現MessageQueue中有消息,就將它取出在傳遞會Handler的handleMessage()方法進行處理。Android啓動程序會在創建Handler時,在UI線程創建一個Looper對象。每個線程中只有一個Looper對象。

總結:handler負責發送消息,將消息存儲到MessageQueue中,Looper負責從MessageQueue中取出Handler發送的消息,並直接把消息回傳給handler自己。

一個更形象的圖:小孩子相當於Handler,它發送要上廁所的消息給教授,教授接收到消息後做了個迴應“你去吧”,然後小孩子自己去完成上廁所的操作。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章