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

歡迎訪問 生活随笔!

生活随笔

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

Android

Android Handler消息传递机制

發布時間:2025/5/22 Android 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android Handler消息传递机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Android中只允許UI線程(也就是主線程)修改Activity里的UI組件。實際開發中,新啟動的線程需要周期性地改變界面組件的屬性值就需要借助Handler的消息傳遞機制。

Handler類

Handler類的主要作用:

  • 在新啟動的線程中發送消息
  • 在主線程中獲取、處理消息

Handler類包含如下方法用于發送、處理消息。

  • handleMessage(Message msg):處理消息的方法。該方法通常用于被重寫。
  • hasMessages(int what):檢查消息隊列中是否包含what屬性為指定值的消息。
  • hasMessages(int what,Object object):檢查消息隊列中是否包含what屬性為指定值且object屬性為指定對象的消息。
  • 多個重載的 Message obtainMessage():獲取消息。
  • sendEmptyMessage(int what):發送空消息。
  • sendEmptyMessageDelayed(int what,long delayMillis):指定多少毫秒之后發送空消
  • sendMessage(Message msg):立即發送消息。
  • sendMessageDelayed(Message msg,long delayMillis):指定多少毫秒之后發送消息。
  • 借助于上面這些方法,程序可以方便地利用Handler來進行消息傳遞。
    關于Handler的源碼解讀,可參考別人寫的《Android 多線程之 Handler 源碼分析》

    實例:自動輪播圖片

    本實例通過一個新線程來周期性的修改ImageView所顯示的圖片(因為不允許其他線程訪問Activity的界面組件,故在程序中發送消息通知系統更新ImageView組件,故不需要實例Looper),布局文件非常簡單,故直接給程序代碼:

    package com.example.testapp1.activity;import android.os.Bundle; import android.os.Handler; import android.os.Message;import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity;import com.example.testapp1.R; import com.example.testapp1.control.RoundImageView;import java.lang.ref.WeakReference; import java.util.Timer; import java.util.TimerTask;public class NextActivity extends AppCompatActivity {private RoundImageView imageShow;static class ImageHandler extends Handler {private WeakReference<NextActivity> nextActivityWeakReference;public ImageHandler(WeakReference<NextActivity> nextActivityWeakReference) {this.nextActivityWeakReference = nextActivityWeakReference;}private int[] imageIds = new int[]{R.drawable.a383f7735d8cd09fb81ff979b2f3d599, R.drawable.b6ab4abe4db592b27ea678345b0c3416, R.mipmap.head1, R.drawable.b6ab4abe4db592b27ea678345b0c3416};private int currentImageId = 0;@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);if (msg.what == 0x1233) {nextActivityWeakReference.get().imageShow.setImageResource(imageIds[currentImageId++ % imageIds.length]);}}}ImageHandler imageHandler = new ImageHandler(new WeakReference<>(this));@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.next);imageShow = findViewById(R.id.headImg);new Timer().schedule(new TimerTask() {@Overridepublic void run() {imageHandler.sendEmptyMessage(0x1233);}}, 0, 2000);} }

    上述代碼中,TimeTask對象的本質就是啟動一條新線程。

    Handler、Loop、MessageQueue的工作原理

    • Message: Handler接收和處理的消息對象。
    • Looper:每個線程只能擁有一個Looper。它的loop方法負責讀取 MessageQueue中的消息,讀到信息之后就把消息交給發送該消息的Handler進行處理。
    • MessageQueue:消息隊列,它采用先進先出的方式來管理Message。程序創建Looper對象時,會在它的構造器中創建MessageQueue對象。Looper的構造器源代碼如下:
    private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}

    該構造器使用了private修飾,表明程序員無法通過構造器創建Looper對象。從上面的代碼不難看出,程序在初始化Looper時會創建一個與之關聯的 MessagQueue,這個MessageOuee就負責管理消息。

    • Handler:它的作用有兩個,即發送消息和處理消息,程序使用Handler發送消息,由Handler發送的消息必須被送到指定的MessageQueue。也就是說,如果希望Handler正常工作,必須在當前線程中有一個MessageQueue;否則消息就沒有 MessageQueue進行保存了。不過MessageQueue是由Looper負責管理的,也就是說,如果希望Handler正常工作,必須在當前線程中有一個Looper對象。為了保證當前線程中有Looper對象,可以分如下兩種情況處理。
  • 在主UI線程中,系統已經初始化了一個Looper對象,因此程序直接創建Handler即可,然后就可通過Handler來發送消息、處理消息了。
  • 程序員自己啟動的子線程,必須自己創建一個Looper對象,并啟動它。創建 Looper對象調用它的prepare(方法即可。
    prepare()方法保證每個線程最多只有一個Looper對象。prepare()方法的源代碼如下:
  • public static void prepare() {prepare(true);}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}

    接下來調用Looper的靜態loop()方法來啟動它。loop()方法使用一個死循環不斷取出MessageQueue中的消息,并將取出的消息分給該消息對應的Handler進行處理。下面是Looper類的loop()方法的源代碼:

    /*** Run the message queue in this thread. Be sure to call* {@link #quit()} to end the loop.*/public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}if (me.mInLoop) {Slog.w(TAG, "Loop again would have the queued messages be executed"+ " before this one completed.");}me.mInLoop = true;final MessageQueue queue = me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();// Allow overriding a threshold with a system prop. e.g.// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'final int thresholdOverride =SystemProperties.getInt("log.looper."+ Process.myUid() + "."+ Thread.currentThread().getName()+ ".slow", 0);boolean slowDeliveryDetected = false;for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}// Make sure the observer won't change while processing a transaction.final Observer observer = sObserver;final long traceTag = me.mTraceTag;long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;if (thresholdOverride > 0) {slowDispatchThresholdMs = thresholdOverride;slowDeliveryThresholdMs = thresholdOverride;}final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);final boolean needStartTime = logSlowDelivery || logSlowDispatch;final boolean needEndTime = logSlowDispatch;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;final long dispatchEnd;Object token = null;if (observer != null) {token = observer.messageDispatchStarting();}long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);try {msg.target.dispatchMessage(msg);if (observer != null) {observer.messageDispatched(token, msg);}dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} catch (Exception exception) {if (observer != null) {observer.dispatchingThrewException(token, msg, exception);}throw exception;} finally {ThreadLocalWorkSource.restore(origWorkSource);if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logSlowDelivery) {if (slowDeliveryDetected) {if ((dispatchStart - msg.when) <= 10) {Slog.w(TAG, "Drained");slowDeliveryDetected = false;}} else {if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",msg)) {// Once we write a slow delivery log, suppress until the queue drains.slowDeliveryDetected = true;}}}if (logSlowDispatch) {showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);}msg.recycleUnchecked();}}

    歸納起來,Looper、MessageQueue、Handler各自的作用如下:

    • Looper:每個線程只有一個Looper,它負責管理MessageQueue,會不斷地從MessageQueag中取出消息,并將消息分給對應的Handler處理。
    • MessageQueue:由Looper負責管理。它采用先進先出的方式來管理Message。
    • Handler:它能把消息發送給 Looper管理的MessageQueue,并負責處理 Looper分給它的消息。

    在線程中使用Handler的步驟如下:

  • 調用Looper的 prepare()方法為當前線程創建Looper對象,創建Looper對象時,它的構造器會創建與之配套的MessageQueue。
  • 有了Looper之后,創建 Handler子類的實例,重寫 handleMessage(方法,該方法負責處理來自其他線程的消息。
  • 調用Looper的loopO方法啟動Looper。
  • 實例:使用新線程實現點擊圖片彈出圖片內容

    1.布局文件:

    <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/constraintlayout2"xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_gravity="center"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/imageView"android:layout_width="50dp"android:layout_height="50dp"tools:ignore="MissingConstraints"tools:src="@drawable/ic_launcher_foreground" /><TextViewandroid:id="@+id/textView3"android:layout_width="100dp"android:layout_height="50dp"android:gravity="center"android:visibility="gone"app:layout_constraintStart_toEndOf="@+id/imageView"app:layout_constraintTop_toTopOf="@+id/constraintlayout2"tools:text="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"tools:visibility="visible" /></androidx.constraintlayout.widget.ConstraintLayout>

    布局文件比較簡單,就是使用約束布局,在其中放入一個圖片控件和文本控件(不展示)。
    JAVA代碼:

    private ImageThread imageThread;class ImageHandler extends Handler {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);if(msg.what == 0x123){String imageText = msg.getData().getString("ImageText");Toast.makeText(mContext, imageText, Toast.LENGTH_LONG).show();}}}class ImageThread extends Thread {private Handler mHandler;@Overridepublic void run() {Looper.prepare();mHandler = new ImageHandler();Looper.loop();}}

    上述代碼定義了一個線程的子類和Handler的子類,在Android Studio的比較新的版本不能直接使用Handler類實例對象并重新handleMessage(已廢棄,舊版本可以),必須通過Handler子類實例對象
    在Activity的onCreate()或者Fragment的onCreateView()方法中加入以下代碼:啟動新線程,監聽圖片的點擊事件,向新線程中的Handler發送消息。

    imageView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Message msg = new Message();msg.what = 0x123;Bundle bundle = new Bundle();bundle.putString("ImageText", imageData.getImageText());msg.setData(bundle);imageThread.mHandler.sendMessage(msg);}});imageThread = new ImageThread();imageThread.start(); 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

    總結

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

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