亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android編程實現(xiàn)異步消息處理機(jī)制的幾種方法總結(jié)

 更新時間:2018年08月29日 11:11:10   作者:adayabetter  
這篇文章主要介紹了Android編程實現(xiàn)異步消息處理機(jī)制的幾種方法,結(jié)合實例形式詳細(xì)總結(jié)分析了Android異步消息處理機(jī)制的原理、相關(guān)實現(xiàn)技巧與操作注意事項,需要的朋友可以參考下

本文實例講述了Android編程實現(xiàn)異步消息處理機(jī)制的幾種方法。分享給大家供大家參考,具體如下:

1、概述

Android需要更新ui的話就必須在ui線程上進(jìn)行操作。否則就會拋異常。

假如有耗時操作,比如:在子線程中下載文件,通知ui線程下載進(jìn)度,ui線程去更新進(jìn)度等,這個時候我們就需要用到異步消息處理。

一、什么是Handler

Handler是Android提供用來異步更新UI的一套機(jī)制,也是一套消息處理機(jī)制,可以用它來發(fā)送消息,也可以用它來接收消息。

二、為什么使用Handler

Android在設(shè)計之時,就封裝了一套消息的創(chuàng)建、傳遞、處理機(jī)制,作為系統(tǒng)原生的異步消息處理機(jī)制的實現(xiàn)之一,我們需要遵循這樣的處理機(jī)制,該機(jī)制的另外一種實現(xiàn)是AsyncTask。

三、Handler用法

1、postdelayed()延時發(fā)送執(zhí)行子線程(Demo)
2、sendMessage()回調(diào)handleMessage()傳遞消息
3、sendToTarget()傳遞消息

四、為什么在Android中只能通過Handler機(jī)制在主線程中更新UI?

最根本的是解決多線程并發(fā)問題。
假如在同一個Activity中,有多個線程同時更新UI,且沒有加鎖,那會導(dǎo)致什么問題呢?
UI更新混亂。
假如加鎖呢?
會導(dǎo)致性能下降。
使用Handler機(jī)制,我們不用去考慮多線程的問題,所有更新UI的操作,都是在 主線程消息隊列中輪詢?nèi)ヌ幚淼摹?br /> Handler 、 Looper 、Message 這三者都與Android異步消息處理線程相關(guān)的概念。那么什么叫異步消息處理線程呢?
異步消息處理線程啟動后會進(jìn)入一個無限的循環(huán)體之中,每循環(huán)一次,從其內(nèi)部的消息隊列中取出一個消息,然后回調(diào)相應(yīng)的消息處理函數(shù),執(zhí)行完成一個消息后則繼續(xù)循環(huán)。若消息隊列為空,線程則會阻塞等待。
—此處有圖為證。

源碼解析

1、Looper

對于Looper主要是prepare()loop()兩個方法。

A. 首先看prepare()方法

public static final void prepare() {
    if (sThreadLocal.get() != null) {
      throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(true));
}

sThreadLocal是一個ThreadLocal對象,可以在一個線程中存儲變量。在第5行,將一個Looper的實例放入了ThreadLocal,并且2-4行判斷了sThreadLocal是否為null,否則拋出異常。這也就說明了Looper.prepare()方法不能被調(diào)用兩次,同時也保證了一個線程中只有一個Looper實例~

B. Looper的構(gòu)造方法:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
  }

在構(gòu)造方法中,創(chuàng)建了一個MessageQueue(消息隊列)。

C. 然后我們看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.");
    }
    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();

    for (;;) {
      Message msg = queue.next(); // might block
      if (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 logger
      Printer logging = me.mLogging;
      if (logging != null) {
        logging.println(">>>>> Dispatching to " + msg.target + " " +
            msg.callback + ": " + msg.what);
      }

      msg.target.dispatchMessage(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.recycle();
    }
}

第2行:

public static Looper myLooper() {
return sThreadLocal.get();
}

方法直接返回了sThreadLocal存儲的Looper實例,如果me為null則拋出異常,也就是說loop方法必須在prepare方法之后執(zhí)行。
第6行:拿到該looper實例中的mQueue(消息隊列)
13到45行:就進(jìn)入了我們所說的無限循環(huán)。
14行:取出一條消息,如果沒有消息則阻塞。
27行:使用調(diào)用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。Msg的target是什么呢?其實就是handler對象,下面會進(jìn)行分析。
44行:釋放消息占據(jù)的資源。

Looper主要作用:

1、 與當(dāng)前線程綁定,保證一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue。
2、 loop()方法,不斷從MessageQueue中去取消息,交給消息的target屬性的dispatchMessage去處理。

好了,我們的異步消息處理線程已經(jīng)有了消息隊列(MessageQueue),也有了在無限循環(huán)體中取出消息的哥們,現(xiàn)在缺的就是發(fā)送消息的對象了,于是乎:Handler登場了。

2、Handler

使用Handler之前,我們都是初始化一個實例,比如用于更新UI線程,我們會在聲明的時候直接初始化,或者在onCreate中初始化Handler實例。所以我們首先看Handler的構(gòu)造方法,看其如何與MessageQueue聯(lián)系上的,它在子線程中發(fā)送的消息(一般發(fā)送消息都在非UI線程)怎么發(fā)送到MessageQueue中的。

public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
      final Class<? extends Handler> klass = getClass();
      if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
          (klass.getModifiers() & Modifier.STATIC) == 0) {
        Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
          klass.getCanonicalName());
      }
    }

    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;
  }

14行:通過Looper.myLooper()獲取了當(dāng)前線程保存的Looper實例,然后在19行又獲取了這個Looper實例中保存的MessageQueue(消息隊列),這樣就保證了handler的實例與我們Looper實例中MessageQueue關(guān)聯(lián)上了。

A.sendMessage方法

輾轉(zhuǎn)反則最后調(diào)用了sendMessageAtTime方法。

B. enqueueMessage方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
      msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
  }

enqueueMessage中首先為msg.target賦值為this,【如果大家還記得Looper的loop方法會取出每個msg然后交給msg,target.dispatchMessage(msg)去處理消息】,也就是把當(dāng)前的handler作為msg的target屬性。最終會調(diào)用queue的enqueueMessage的方法,也就是說handler發(fā)出的消息,最終會保存到消息隊列中去。

C. dispathMessage方法

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
      handleCallback(msg);
    } else {
      if (mCallback != null) {
        if (mCallback.handleMessage(msg)) {
          return;
        }
      }
      handleMessage(msg);
    }
  }

可以看到,第10行,調(diào)用了handleMessage方法,下面我們?nèi)タ催@個方法:

/**
  * Subclasses must implement this to receive messages.
  */
 public void handleMessage(Message msg) {
 }

可以看到這是一個空方法,為什么呢,因為消息的最終回調(diào)是由我們控制的,我們在創(chuàng)建handler的時候都是復(fù)寫handleMessage方法,然后根據(jù)msg.what進(jìn)行消息處理。

3、Handler post

post方法:

public final boolean post(Runnable r)
{
   return sendMessageDelayed(getPostMessage(r), 0);
}

getPostMessage方法:

private static Message getPostMessage(Runnable r) {
   Message m = Message.obtain();
   m.callback = r;
   return m;
}

可以看到,在getPostMessage中,得到了一個Message對象,然后將我們創(chuàng)建的Runable對象作為callback屬性,賦值給了此message.
注:產(chǎn)生一個Message對象,可以new ,也可以使用Message.obtain()方法;兩者都可以,但是更建議使用obtain方法,因為Message內(nèi)部維護(hù)了一個Message池用于Message的復(fù)用,避免使用new 重新分配內(nèi)存。
sendMessageDelayed方法和handler.sendMessage方法最終調(diào)用的都是:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
      RuntimeException e = new RuntimeException(
          this + " sendMessageAtTime() called with no mQueue");
      Log.w("Looper", e.getMessage(), e);
      return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

可以看到,這里msg的callback和target都有值,那么會執(zhí)行哪個呢?
看dispatchMessage方法就能看出來。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
      handleCallback(msg);
    } else {
      if (mCallback != null) {
        if (mCallback.handleMessage(msg)) {
          return;
        }
      }
      handleMessage(msg);
    }
}

第2行,如果不為null,則執(zhí)行callback回調(diào),也就是我們的Runnable對象。
mCallback 的值是如何賦值的,可以查看Handler的構(gòu)造方法,默認(rèn)mCallback 的值為Null

到此,這個流程已經(jīng)解釋完畢,總結(jié)一下

  • 1、首先Looper.prepare()在本線程中保存一個Looper實例,然后該實例中保存一個MessageQueue對象;因為Looper.prepare()在一個線程中只能調(diào)用一次,所以MessageQueue在一個線程中只會存在一個。
  • 2、Looper.loop()會讓當(dāng)前線程進(jìn)入一個無限循環(huán),不斷從MessageQueue的實例中讀取消息,然后回調(diào)msg.target.dispatchMessage(msg)方法。
  • 3、Handler的構(gòu)造方法,會首先得到當(dāng)前線程中保存的Looper實例,進(jìn)而與Looper實例中的MessageQueue相關(guān)聯(lián)。
  • 4、Handler的sendMessage方法,會給msg的target賦值為handler自身,然后加入MessageQueue中。
  • 5、在構(gòu)造Handler實例時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調(diào)用的方法。

在Activity中,我們并沒有顯示的調(diào)用Looper.prepare()Looper.loop()方法,為啥Handler可以成功創(chuàng)建呢,這是因為在Activity的啟動代碼中,已經(jīng)在當(dāng)前UI線程調(diào)用了Looper.prepare()Looper.loop()方法。

4、擴(kuò)展

其實Handler不僅可以更新UI,你完全可以在一個子線程中去創(chuàng)建一個Handler,然后使用這個handler實例在任何其他線程中發(fā)送消息,最終處理消息的代碼都會在你創(chuàng)建Handler實例的線程中運(yùn)行。

代碼:

new Thread()
    {
      private Handler handler;
      public void run()
      {

        Looper.prepare();

        handler = new Handler()
        {
          public void handleMessage(android.os.Message msg)
          {
            Log.e("TAG",Thread.currentThread().getName());
          };
        };
         Looper.loop();
        }

四種更新UI的方法

1、Handler.post();
2、Handler.sendMessage();
3、runOnUIThread()
4、View.post()

查看runOnUIThread()的源代碼(Activity中)

Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.
Parameters:
action the action to run on the UI thread
public final void  runOnUiThread(Runnable action) {
       if (Thread.currentThread() != mUiThread) {
           mHandler.post(action);
       } else {
            action.run();
       }
}

補(bǔ)充:

1.異步消息處理機(jī)制的另一種實現(xiàn):AsyncTask:

主要方法:

  • onPreExecute(): 這個方法是在執(zhí)行異步任務(wù)之前的時候執(zhí)行,并且是在UI
    Thread當(dāng)中執(zhí)行的,通常我們在這個方法里做一些UI控件的初始化的操作,例如彈出ProgressDialog
  • doInBackground(Params… params):

    onPreExecute()方法執(zhí)行完后,會馬上執(zhí)行這個方法,這個方法就是來處理異步任務(wù)的方法,Android操作系統(tǒng)會在后臺的線程池當(dāng)中開啟一個worker
    thread來執(zhí)行這個方法(即在worker thread當(dāng)中執(zhí)行),執(zhí)行完后將執(zhí)行結(jié)果發(fā)送給最后一個 onPostExecute
    方法,在這個方法里,我們可以從網(wǎng)絡(luò)當(dāng)中獲取數(shù)據(jù)等一些耗時的操作

  • onProgressUpdate(Progess… values): 這個方法也是在UI

    Thread當(dāng)中執(zhí)行的,在異步任務(wù)執(zhí)行的時候,有時需要將執(zhí)行的進(jìn)度返回給UI界面,例如下載一張網(wǎng)絡(luò)圖片,我們需要時刻顯示其下載的進(jìn)度,就可以使用這個方法來更新進(jìn)度。這個方法在調(diào)用之前,我們需要在
    doInBackground 方法中調(diào)用一個 publishProgress(Progress) 的方法來將進(jìn)度時時刻刻傳遞給
    onProgressUpdate 方法來更新

  • onPostExecute(Result… result): 當(dāng)異步任務(wù)執(zhí)行完之后,就會將結(jié)果返回給這個方法,這個方法也是在UI

    Thread當(dāng)中調(diào)用的,我們可以將返回的結(jié)果顯示在UI控件上

更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android線程與消息機(jī)制用法總結(jié)》、《Android開發(fā)入門與進(jìn)階教程》、《Android調(diào)試技巧與常見問題解決方法匯總》、《Android基本組件用法總結(jié)》、《Android視圖View技巧總結(jié)》、《Android布局layout技巧總結(jié)》及《Android控件用法總結(jié)

希望本文所述對大家Android程序設(shè)計有所幫助。

相關(guān)文章

最新評論