Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 深入了解android中的消息機制Handler

深入了解android中的消息機制Handler

編輯:關於Android編程

什麼是Handler?
handler是Android給我們提供用來更新UI的一套機制,也是一套消息處理機制.
我們可以使用它發送消息,也可以通過它處理消息.

我們為什麼要使用Handler?
Android在設計的時候,就封裝了一套消息創建,傳遞,處理機制,如果不遵循這樣的機制,就沒有辦法更新UI,而且還會拋出異常信息.

例如:大家都知道,更新UI的操作一般都是放在main線程中,當我們需要在子線程中更新UI時,就需要使用到了Handler,雖然在子線程更新Ui的方法有好幾種,但內部實現原理基本都是通過Handler發送消息處理的,不要著急,下面會提到.

Handler的使用:

sendMessage()方法的使用:
package com.hnthgys.mytext;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private TextView tv;
    //創建main線程的Handler
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            tv.setText("我是通過handler發送消息更新的");
        }
    };

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

        tv = (TextView) findViewById(R.id.textview);
        //開啟子線程
        new Thread(){
            @Override
            public void run() {
                super.run();
                //例如此處我們正在執行一個耗時操作,執行完畢後發送消息更新ui

                //發送一個空消息
                handler.sendEmptyMessage(0);

                //如果此處我們需要使用執行完耗時操作的數據,可以這樣寫
                //Message msg = handler.obtainMessage();
                //msg.obj = "數據";
                //handler.sendMessage(msg);

            }
        }.start();


    }
}

sendEmptyMessage(); 此方法和sendMessage使用一致,區別就是發送一個空消息.

sendMessageDelayed(); 發送一個延時執行的消息

post(Runnable);該方法可以在子線程中更新UI,該方法運行在main線程中

removeCallbacksAndMessages();移除回調和消息;例如:我們在使Handler輪播一些圖片時,想讓它停止輪播,就可以使用這個方法.

android為什麼要設計只能通過handler來更新UI呢?

最根本的的目的就是解決多線程並發問題.假設如果在一個activity當中,有多個線程去更新UI,並且都沒有加鎖機制,那麼會產生什麼樣子的問題呢? 更新界面錯誤.

你可能會說,我可以使用加鎖的多線程啊,如果對更新UI的操作都進行加鎖處理的話,應用程序的性能會大大下降.
處於對以上問題的考慮,Android給我們提供了一套更新UI的機制,我們只需要遵循這樣的機制就可以了.
根本不用關心多線程的問題,所以更新UI的操作,都是在主線程的消息隊列當中去輪詢處理的.

Handler的原理是什麼呢?
一,Handler封裝了消息的發送,(主要包括消息發送給誰)
Looper(Handler內部自己的Looper)
1,內部包含一個消息隊列,也就是MessageQueue,所有的Handler發送消息
都走向這個消息隊列.
2,Looper.loop()方法,就是一個死循環,不斷的從MessageQueue中取消息,
如果有消息就處理消息,沒有消息就阻塞.

二,MessageQueue,就是一個消息隊列,可以添加消息,並處理消息.

三,Handler,內部會跟Lopper進行關聯,也就是說在Handler的
內部可以找到Looper,找到了Lopper也就找到 了MessageQueue,
在Handler中發送消息,其實就是向MessageQueue隊列中發送消息.

Handler原理總結:
Handler負責發送消息,Loooper負責接收Handler發送的消息,
並直接把消息回傳給Handler自己.
MessageQueue就是一個存儲消息的容器.

Handler使用中遇到的問題:
在非UI線程中更新UI,拋出的異常:
這裡寫圖片描述
在子線程創建Handler,拋出的異常:
這裡寫圖片描述
注意:當需要在子線程中創建Handler時,需要先創建一個Looper,因為子線程中沒有LoZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcGVyttTP8zwvcD4NCjxwPjxzdHJvbmc+SGFuZGxlclRocmVhZNPWysfKssO0Pzwvc3Ryb25nPjwvcD4NCjxwPrWxztLDx8/ytLS9qNK7uPbT68/fs8zP4LnYtcRIYW5kbGVyyrEsztLDx7/J0tTKudPDSGFuZGxlclRocmVhZCzAtL3ivva24M/fs8y1xLKit6LOysziLjxiciAvPg0KPGltZyBhbHQ9"這裡寫圖片描述" src="/uploadfile/Collfiles/20160405/20160405093938387.png" title="\" />

子線程與主線程如何互發消息:

主線程Handler向子線程發送消息(偽代碼)
這裡寫圖片描述 子線程Handler向主線程發送消息(偽代碼)
這裡寫圖片描述

Android在子線程中更新UI的幾種方式:
使用圖片吧,以前做的筆記,看著感覺更加清晰..
這裡寫圖片描述

非UI線程真的不能更新UI嗎?
答案是能,對.你沒有看錯,非UI線程也能更新UI.可能你會覺得我在扯淡,下面看一段代碼:

package com.hnthgys.mytext;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private TextView tv;
    //創建main線程的Handler
    private Handler handler = new Handler();

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

        tv = (TextView) findViewById(R.id.textview);
        //開啟子線程
        new Thread(){
            @Override
            public void run() {
                super.run();
                //SystemClock.sleep(100);
                tv.setText("我是在子線程中更新的UI");
            }
        }.start();


    }
}

我把布局代碼也貼出來,





    


執行效果圖:
這裡寫圖片描述

可能看到這,你已經目瞪口呆了,這怎麼可能,fuck,這完全顛覆了啊…..

主要原因:
當我們在更新UI時,Android中的ViewRootImpl類中的checkThread()方法會檢查當前更新UI所在的線程,如圖

3937    void More ...checkThread() {
            //檢查執行更新UI所在的線程
3938        if (mThread != Thread.currentThread()) {
                //如果不在UI線程,就會拋出下面的異常,大家應該很眼熟吧
3939            throw new CalledFromWrongThreadException(
3940                    "Only the original thread that                 created a view hierarchy can touch its views.");
3941        }
3942    }

查看系統源碼後,你會發現,ViewRootImpl類會在 Activity的onResume()方法執行完成後才初始化,這也就解釋了上面代碼能運行的原因了,但是,你發現沒有,我們在子線程中沒有做任何的耗時操作,如果我在子線程中添加這句代碼:

SystemClock.sleep(100);

那麼系統將會拋出異常:”Only the original thread that created a view hierarchy can touch its views.”
不能在非UI線程中更新UI.
那麼問題來了,如果ViewRootImpl類沒有初始化完成,那麼view視圖是如何顯示出來的呢???我也正在解決中…….

另外,當我們在子線程中獲取到ViewRoot,我們可以調用addView()方法在子線程中更新UI,這其中的詳情就靠大家去探索了…..

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved