日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android消息机制基本原理和使用

發布時間:2024/9/21 Android 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android消息机制基本原理和使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在Android開發過程中,我們常常遇到子線程更新UI的需求,例如在子線程進行耗時較長的下載,等下載完成之后,再去更新UI,提示用戶下載完成,直接在子線程里更新UI,會得到報錯提示:Only the original thread that created a view hierarchy can touch its views

Android老手知道這是怎么回事,并且知道解決方案,新手只能去網上找答案,網上的答案告訴我們報錯是因為子線程不能直接更新UI線程,也就是主線程的控件,必須通過Android消息機制來更新。略微遺憾的是,網上的答案要么僅僅是羅列了可用方案的代碼片段,不知道背后的原理是什么,要么是陷入源碼分析的細節中,看完之后的感覺作者早就走遠了,我們還在源代碼細節中暈頭轉向。
為此,決定自己寫一篇,如果能用通俗語言說明白,表明自己也會了。

2.為什么其他線程不能直接訪問UI線程?

要回答這個問題,可以反過來想一想,如果子線程能夠訪問UI線程會怎樣?Android的UI線程是非線程安全,非線程安全是***不對數據進行加鎖保護,多線程訪問數據時,導致出現數據不一致或者數據污染情況***,例如兩個線程同時設置同一個UIView的背景顏色,那么很有可能渲染顯示的是顏色A,而此時在UIView邏輯上的背景顏色屬性為B,因此假如子線程能直接修改UI控件的話,會導致出現不可預期結果,那么UI線程為什么不設置成線程安全呢?要設置成線程安全需要加鎖,即當一個線程訪問該類的某個數據時,會對這個數據進行保護,其他線程不能對其訪問,直到該線程讀取完之后,其他線程才可以使用。但這樣做會降低執行效率,如果一個子線程長時間占據著,其他線程只能干等,而UI界面直接面對用戶,是Android的門面,首先要保證響應快,越快越好,因此UI線程只能是非線程安全。

既然是非線程安全,又不想讓子線程隨便修改,只能禁止子線程直接訪問UI線程的控件。

既然子線程不能直接訪問UI,那怎么實現更新UI呢?這就用到了Android消息機制。

3.Android消息機制

什么是Android消息機制?說白了是跨線程傳遞信息機制,注意這里是跨線程,不是跨進程,Android里跨進程通信使用Binder,跨線程傳遞消息使用消息機制,為什么跨線程通信不使用Binder?

我們知道不同進程內存空間是隔離的,而一個進程里不同線程共享內存空間,用日常生活來理解就是,不同進程好比是不同的房子,一個進程是一間房子,而一個進程里包含不同線程,這些線程就像是不同的人,例如你,你父母,你老婆孩子,這些家庭成員同在一個房子里,也就是共享內存空間。

兩個進程兩間房子,相互之間通信使用電話溝通,而同在一個房間里的不同線程還使用電話溝通效率降低了,這也是為什么不同線程之間不用Binder的原因。

你可能會問,既然家庭成員同在一間房里時,直接面對面喊話不就行了嗎,為什么還設計消息機制來溝通,不還是降低效率了嗎?這又回到上節里說的,如果直接喊話就響應,讓子線程直接訪問UI線程,會導致混亂,這可以由日常生活的例子來理解,假設由你掌控遙控器,你老婆想看電影頻道,你小孩想看動畫頻道,你父母想看戲劇頻道,如果他們同時提需求,你到底是按哪個頻道?為了解決這個混亂問題,你可以設計一個消息機制來應付。

你可以在桌面上放著一個傳票叉,誰想看什么節目就把需求寫在便箋,然后插到傳票叉上,這樣有個先后順序,如果叉上有便箋,你取出來,看是寫了什么,例如老婆便箋寫著看10分鐘電影,她先把便箋插到叉上,小朋友便箋寫著看動畫1分鐘,也插到叉上,這樣你先取出小朋友的便箋,然后給他看一分鐘動畫,一分鐘后,再取出你老婆的便箋,換臺到電影頻道。這樣雖然效率不高,但保證順序不亂,當然傳票叉是后來居上機制,這么設計容易挨打。

Android消息機制也是類似操作。

我們先來看Android消息機制里的各個角色名稱。

  • Message

    用于傳遞消息的載體,對應于上述例子的便箋;

  • MessageQueue

    消息隊列,對應上述例子的傳票叉,家人看哪個頻道的需求寫到便箋上,然后插到傳票叉上,形成消息隊列。其實我們可以發現,正因為傳票叉的存在,便箋才有先后順序,你處理起來才不會亂,如果家人把便箋散放在桌子上,那和直接喊話沒什么區別。同理,正是MessageQueue的存在,才解決了UI線程能夠挨個處理每個子線程的而不錯亂的問題。需要注意的是,從傳票叉取出便箋的過程只能由你自己完成,其他人代勞還是會引發混亂,同理,消息隊列也只能由接收消息的線程來處理;

  • Looper

    有部電影英文名是《Looper》,只看英文名可能覺得這電影沒什么知名度,說中文名稱你應該就想起這部電影,這是由囧瑟夫主演的《環形使者》。那這部電影和Android消息機制里的Looper有啥關系?電影中殺手要干掉的目標是未來的自己,陷入死循環,消息機制里的Looper,是為了令程序進入無限循環,在這個循環里,不斷檢查MessageQueue是否有消息進來,如果有消息,則讀取出來,并傳遞到Handler的handleMessage()方法中。注意,每個線程中只會有一個Looper對象。用遙控器的例子來理解Looper的話,Looper操作你進入不停檢查傳票叉是否有便箋插進來的狀態;

  • Handler
    在遙控器的例子中,你代表主線程,你的家人代表子線程,他們只需要在便箋上寫上需求,再自己插到傳票叉上,你取出來,這就模擬了消息的跨線程傳遞。但在程序中,需要使用Handler類來完成往傳票叉上插便箋的過程,也就是子線程給主線程傳遞消息。在主線程中構建Handler類實例,子線程再調用這個Handler類的sendMessage()方法即可,為什么主線程構建的Handler類實例子線程能夠使用?因為它們在同一個進程里,同一個進程內不同線程共享資源的,主線程創建的實例子線程當然可以訪問到。說到這我們還可以這樣理解Handler,Handler相當于主線程分發給不同線程的專用傳話筒,子線程可以通過這個傳話筒聯絡主線程。

    我們可以使用一個示意圖將消息機制里的幾個角色的作用展示出來,如圖所示。
    其中,環形使者Looper啟動線程進入無限循環,不停查看MessageQueue是否有消息進來,有消息進來使用Handler取出,線程1實例化出Handler,給線程2引用,線程2有消息發送的話,則使用Handler的sendMessage方法,發送消息,發送的消息進入MessageQueue里。

4.Android消息機制使用示例

看了消息機制的基本原理,現在來看一下如何使用。

  • 首先實例化一個Handler實例,如代碼所示,我們看到,里面還重寫了handleMessage方法,顧名思義,handleMessage是處理消息的意思,在上面介紹Handler時,只說了Handler是子線程傳遞消息的渠道,其實忘記說了取出消息也是通過Handler。Handler取到消息后,根據消息的類型做相應的動作,這里只是簡單更新一下TextView的文字。另外,這里我們看不到Looper,MessageQueue,這些是幕后英雄,在分析源碼時會涉及到。
  • Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 0://完成主界面更新,拿到數據String data = (String)msg.obj;tshow.setText(data);break;default:break;}}};
  • 子線程調用Handler實例發送消息
  • private void UpdateTextView() { new Thread(new Runnable(){ @Override public void run() { Message msg =new Message(); msg.obj = "子線程更新數據";//可以是基本類型,可以是對象,可以是List、map等; //發送消息mHandler.sendMessage(msg); } }).start(); }

    然后,然后沒有然后了,使用消息機制就這么簡單,可以看出,因為使用簡單,開發者使用消息機制完成子線程更新主線程其實沒增加多少工作量。對于小白而言,雖然使用簡單,但了解其背后的原理還是有必要的,不然不知道為什么這么使用,我開始接觸消息機制時,只知道拷貝他人的代碼,不知道原理,導致每次用到的時候,都是拷貝代碼片段,然后替換成自己的變量,因為自己寫不知道從哪里入手。

    本文只是簡單理解消息機制的各個角色,以及它們之間如何配合,下一篇博客,我們分析消息機制的源代碼。

    最后附上Activity的源代碼。

    package com.test.threaddemo;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.TextView;public class MainActivity extends AppCompatActivity {private TextView tshow;private Button button;Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 0://完成主界面更新,拿到數據String data = (String)msg.obj;tshow.setText(data);break;default:break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tshow=findViewById(R.id.tvshow);button=findViewById(R.id.bClick);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {UpdateTextView();}});}@Overrideprotected void onResume() {super.onResume();}private void UpdateTextView() {new Thread(new Runnable(){@Overridepublic void run() {Message msg =new Message();msg.obj = "子線程更新數據";//可以是基本類型,可以是對象,可以是List、map等;//發送消息mHandler.sendMessage(msg);}}).start();} }

    總結

    以上是生活随笔為你收集整理的Android消息机制基本原理和使用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。