• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • xamarin學習筆記A13(安卓Handler異步消息處理)

    標簽: android  android異步線程處理任務  安卓handler更新ui

    (每次學習一點xamarin就做個學習筆記和視頻來加深記憶鞏固知識)
    如有不正確的地方,請幫我指正。
    安卓異步消息處理簡介
    ??有時候需要執行一些耗時的操作,例如從遠程服務器讀取數據,讀取的時間的長度由很多因素決定,我們不希望主線程被阻塞程序無法進行其它工作,而且Android中只能在主線程進行UI操作,不能在子線程操作,如果要根據子線程執行的結果來更新UI時,這時就需要用到安卓異步消息處理機制。

    異步消息原理
    ??安卓異步消息處理主要有四個類:Message、Handler、Looper和MessageQueue
    我們直接打交道的是前兩個類,后面兩個類在內部自動處理的。

    說明
    Message 用于攜帶數據
    MessageQueue 是一個存放消息的隊列
    Looper 用于不斷的從隊列中取消息,交給消息關聯的Handler對象去處理
    Handler 用于處理消息

    異步消息處理圖

    ??如圖所示,首先在主線程中創建一個Handler實例對象并重寫了HandlerMessage方法,然后在子線程中執行run方法并創建Message對象攜帶好數據,通過消息對象關聯的hander對象的SendMessage方法將Message對象發送到消息隊列,Looper會自動去從隊列中取消息,取到后自動交給Handler的HandlerMessage方法處理。

    下面我提取了Android中這四個類的源碼的主要屬性和方法,配上個人加的注釋(如有注釋不對的地方,請大家指正)
    

    Message類

    //用于攜帶數據
    public final class Message implements Parcelable {
        public int what;//一個整型標識
        public int arg1;//可保存一個整數
        public int arg2;//可保存一個整數
        public Object obj;//可保存任意數據
    
        long when;//消息應該被處理的時間
        Handler target;//保存與它關聯的Handlder對象的引用
        Runnable callback;//保存處理消息的回調方法的引用
        Message next;//指向下一個消息,因為消息隊列是一個單鏈表的數據結構
    
        private static final Object sPoolSync = new Object();//用于同步鎖的對象
        private static Message sPool;//緩存對象
        private static int sPoolSize = 0;
        private static final int MAX_POOL_SIZE = 50;
    
        public Message() { }
        public static Message obtain() {//比上面構造方法效率高,因為用了緩存對象
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
    
        public long getWhen() {return when;}
        public void setTarget(Handler target) {this.target = target;}
        public Handler getTarget() {return target;}
        public Runnable getCallback() {return callback;}
        public void sendToTarget() {target.sendMessage(this);}
    }
    

    MessageQueue類

    //消息隊列(以Message的when值來排序的鏈表結構)
    public final class MessageQueue {
        private final boolean mQuitAllowed;
        Message mMessages;//鏈表中最前頭的Message對象(when值最小)
        private boolean mQuitting;
    
        // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
        private boolean mBlocked;
        //一些本地方法
        private native static long nativeInit();
        private native static void nativeDestroy(long ptr);
        private native void nativePollOnce(long ptr, int timeoutMillis);
        private native static void nativeWake(long ptr);
    
        MessageQueue(boolean quitAllowed) {
            mQuitAllowed = quitAllowed;
        }
    
        //從隊列中取消息
        Message next() {
            final long ptr = mPtr;
            if (ptr == 0) {return null;}
    
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {//以同步的方式取,確保同一時刻只有一個線程訪問
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {//如果消息關聯的Handlder對象沒有
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());//那么只取異步消息
                    }
                    if (msg != null) {
                        if (now < msg.when) {//如果沒到消息應該被處理的時間
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);// 計算一個超時時間來等待
                        } else {//到達消息應該被處理的時間就返回消息
                            mBlocked = false;
                            if (prevMsg != null) {prevMsg.next = msg.next;}//取出一個消息后整理好鏈表
                            else {mMessages = msg.next;}
    
                            msg.next = null;
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        nextPollTimeoutMillis = -1;
                    }
                }
                nextPollTimeoutMillis = 0;
            }
        }
    
        //添加一個新的消息到隊列中
        boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}
            if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}
    
            synchronized (this) {//以同步方式添加消息
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {//如果鏈表為空或者新來消息比隊列中所有消息的優先級更高
                    msg.next = p;//把原先鏈表中最前頭的消息放到新的消息后面
                    mMessages = msg;//設置新消息為鏈表中最前頭的消息
                    needWake = mBlocked;
                } else {
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;//從鏈表第二個開始找合適的位置(第一個已經在上面的when < p.when判斷過了)
                        if (p == null || when < p.when) {
                            break;//找到合適鏈表位置p后退出循環
                        }
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    msg.next = p;
                    prev.next = msg;//這兩行代碼是把新消息插到prev與p之間
                }
    
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    }
    

    Handler類

    //用于處理消息
    public class Handler {
        final Looper mLooper;   //保存著Looper對象的引用
        final MessageQueue mQueue;  //保存著Looper對象中MessageQueue對象的引用
        final Handler.Callback mCallback;  //保存著構造方法傳入的回調方法的引用
    
        public interface Callback { //一個接口,規定了消息處理的方法的規范
            public boolean handleMessage(Message msg);
        }
        public Handler() {
            this(null, false);
        } //構造方法
    
        public Handler(Handler.Callback callback, boolean async) {//構造方法
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
        public void handleMessage(Message msg)
        {
            //如果dispatchMessage方法中的msg.callback==null 或 mCallback==null
            //那么子類應該重寫此方法來處理消息
        }
    
        public void dispatchMessage(Message msg) {//分發消息給回調方法處理
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
        public final boolean sendMessage(Message msg) { //發送一個消息
            return sendMessageDelayed(msg, 0);
        }
        public final boolean sendMessageDelayed(Message msg, long delayMillis)//以指定的延遲時間發送消息
        {
            if (delayMillis < 0) {delayMillis = 0;}
            //當前時間加上延遲時間就是消息應該被處理的時間
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {//在指定的時間發送消息
            MessageQueue queue = mQueue;
            if (queue == null) { return false;}
            return enqueueMessage(queue, msg, uptimeMillis);
        }
        //將消息發送到消息隊列中
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            //通過MessageQueue對象的enqueueMessage方法把消息添加到消息隊列
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    }
    

    Looper類

    //用于不斷的從隊列中取消息,交給消息關聯的Handler對象去處理
    public final class Looper {
        static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//以鍵值對形式存儲(key:線程 value:此線程的Looper對象)
        private static Looper sMainLooper;
    
        final MessageQueue mQueue;//自己持有的消息隊列
        final Thread mThread;//自己所在的線程
    
        private Looper(boolean quitAllowed) {//私有構造方法
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    
        public static void prepare() {
            prepare(true);
        }
    
        private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("每個線程只能有一個Looper");
            }
            sThreadLocal.set(new Looper(quitAllowed));
        }
    
        public static void prepareMainLooper() {//準備好主Looper對象
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    
        public static Looper getMainLooper() {
            synchronized (Looper.class) {
                return sMainLooper;
            }
        }
    
        //開啟循環
        public static void loop() {
            final Looper me = myLooper();
            final MessageQueue queue = me.mQueue;
    
            for (;;) {
                Message msg = queue.next(); //從消息隊列中取出一個消息
                if (msg == null) {
                    return;
                }
    
                try {
                    msg.target.dispatchMessage(msg);//將消息交給關聯的Handlder對象去處理
                } finally {
                }
                msg.recycleUnchecked();
            }
        }
    
        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }//取出Looper
        public static @NonNull MessageQueue myQueue() {
            return myLooper().mQueue;
        }
        public boolean isCurrentThread() {return Thread.currentThread() == mThread;}
        public void quit() {mQueue.quit(false);}
        public @NonNull Thread getThread() {return mThread;}
        public @NonNull MessageQueue getQueue() {return mQueue;}
    }
    

    ??我們直接打交道的是Message類和Handlder類,那么MessageQueue和Looper類是怎么來的,這就要看ActivityThread類程序的入口方法main,貼上部分代碼如下:

    public final class ActivityThread {
        //..........省略其它代碼
    
        public static void main(String[] args) {
            //..........省略其它代碼
    
            Looper.prepareMainLooper();//創建好Looper對象,它構造方法里同時也創建了MessageQueue對象
    
            ActivityThread thread = new ActivityThread();//創建好主線程
            thread.attach(false);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
            Looper.loop(); //開啟無限循環,不斷從MessageQueue中取消息交給消息關聯的Handler處理
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    }
    

    ??大概了解了這四大類和它們的機制后,接下來就開始做個簡單的異步消息處理程序。

    //處理消息的類
        public class MyHandler : Handler
        {
            private Action<Message> _action;//指向一個回調方法的委托
            public MyHandler(Action<Message> action)
            {
                _action = action;//構造方法中傳入處理消息的回調方法
            }
            //重寫父類的HandleMessage方法
            public override void HandleMessage(Message msg)
            {
                //if (_action != null)
                //{
                //    _action.Invoke(msg);
                //}
                _action?.Invoke(msg); //C#6.0新增判斷空值的方式
            }
    }
    
    //模擬從服務器讀取數據(實現Java.Lang.IRunnable接口可讓線程使用)
        public class ReadDataTask : Java.Lang.Object, Java.Lang.IRunnable
        {
            private Handler _handler;
    
            public ReadDataTask(Handler handler)
            {
                _handler = handler;
            }
    
            public void Run()//子線程要運行的任務
            {
                try
                {
                    Java.Lang.Thread.Sleep(4000);// 模擬任務,比如從遠程服務器讀取數據
    
                    Message msg = _handler.ObtainMessage();//創建一個消息對象
                    msg.What = 123; //設置消息對象的標識
                    msg.Obj = "已讀取到數據";//消息對象攜帶一個字符串數據
    
                    _handler.SendMessage(msg);// 將消息對象發送到主線程的消息隊列(MessageQueue)中
                }
                catch (Java.Lang.InterruptedException e)
                {
                    Log.Debug("ReadInfoTask", e.Message);
                }
            }
        }
    

    ??上面兩個類準備好后,接下來就在Activity中使用了

    public class MainActivity : AppCompatActivity,IOnClickListener
        {
            private MyHandler _myHandler;
            private EditText _editText;
    
            public void OnClick(View v)
            {
                switch (v.Id)
                {
                    case Resource.Id.button1:
                        Java.Lang.Thread thread = new Java.Lang.Thread(new ReadDataTask(_myHandler));
                        thread.Start();//啟動一個子線程去執行耗時任務
                        break;            
    }
            }
    
            protected override void OnCreate(Bundle savedInstanceState)
            {
                base.OnCreate(savedInstanceState);
                SetContentView(Resource.Layout.Main);
    
                Button btn1 = this.FindViewById<Button>(Resource.Id.button1);
                btn1.SetOnClickListener(this);
    
                _editText = this.FindViewById<EditText>(Resource.Id.editText1);
    
                _myHandler = new MyHandler(RefreshUI); //在主線程實例化一個Handler對象
    
            }
    
            private void RefreshUI(Message msg)//在主線程更新UI
            {
                switch (msg.What)
                {
                    case 123:
                        string s = Convert.ToString(msg.Obj);
                        _editText.Text = s;
                        break;
                    case 456:
                        //……………………
                        break;
                }
            }
        }
    

    代碼和視頻在我上傳的CSDN資源中http://download.csdn.net/download/junshangshui/10022514

    版權聲明:本文為junshangshui原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/junshangshui/article/details/78242154

    智能推薦

    異步消息處理之Handler

    Handler-初識 為什么要使用Handler? 在程序運行時,有耗時操作進行,為了運行流暢,只能將該操作放入子線程里, 但操作涉及到UI更新等不可在子線程里操作的操作時,為了解決該問題引入Handler。 什么是Handler handler是一個類,通過該類里的一些方法能在子線程和主線程之間傳遞一些信息數據。 例如在進行耗時且要更新UI的操作時,子線程操作完畢可以使用handler發送消息,...

    安卓定時器類Timer與消息處理類Handler

    安卓串口編程中,需要循環判斷串口中是否有數據到來,如果有數據到來,則接收數據。可以通過定時器類Timer和消息處理類Handler來實現以上功能。 1 定時器類Timer Timer表示定時器類,該類可以為后臺進程安排要執行的任務。每個Timer對象相對應一個后臺線程,用于順序地執行任務。 1.1 創建定時器類對象 在onCreate()方法中通過new創建Timer類的對象。 1.2 ...

    異步消息處理總結(AsynTask和handler)

    Handler 在 Android 中的應用很廣泛,基本上每個 Android 開發人員都會使用到它。本篇文章將會介紹 Handler 和異步消息機制相關的使用方法。 由于 Android 系統不允許在主線程進行耗時任務,因此網絡請求等一般都會開新的線程執行,然而,Android 中的控件不是線程安全的,因此 Android 系統要求只能在主線程中訪問 UI 控件(當然如果你非要在子線程中訪問,也...

    Handler 異步消息處理機制

    1.在安卓開發中,我們常常通過一個線程來完成某些操作,然后同步顯示對應的視圖控件UI上,由于安卓中無法直接通過子線程來進行UI更新操作,因此Android提供了一套異步消息處理機制Handler。 在子線程中更新UI控件會報錯: 2. Handler實現方法 3. Handler實現原理 使用Handler方式進行異步消息處理主要由Message,Handler,MessageQueue,Loop...

    【安卓筆記】Handler:顯示時間

      1.簡單數字時鐘 學習通過Handler +線程刷新UI,時鐘或者計時器練習 下面這段簡短的代碼就可以實現(關鍵代碼就只有Handler的post和postDelayed方法),其中的機制(消息隊列等)還要繼續學習       這個是常規的比較完整的寫法了,比較容易理解     2.關于android自帶時鐘小工具 Android自帶...

    猜你喜歡

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡