1. 程式人生 > >android 程序/執行緒管理(一)----訊息機制的框架

android 程序/執行緒管理(一)----訊息機制的框架

一:android 程序和執行緒

程序是程式執行的一個例項。android通過4大主件,弱化了程序的概念,尤其是在app層面,基本不需要關係程序間的通訊等問題。

但是程式的本質沒有變,尤其是多工系統,以事件為驅動的軟體系統基本模式都是如下:

程式的入口一般是main:

1.初始化:

比如建立視窗,申請資源等。

2.進入while(true)

在迴圈中處理各種事件,直到程序退出。

四大元件是程序的部分載體,配置程序在androidmanifest.xml裡面,android:process 屬性。

當然預設所有的都在同一個程序裡面,由application裡面配置,預設程序為apk的包名。

執行緒是程序的有機組成部分,是CPU排程的基礎。

一般情況下,都有主執行緒和其他執行緒之分,只有主執行緒才可以重新整理UI。

應用程式啟動後,將建立ActivityThread 主執行緒。

不同包名的元件可以一定的方式執行在同一個程序中。

一個Activity啟動後,至少會有3個執行緒。一個主執行緒和2個binder執行緒。

二:android 程序內的訊息驅動機制---Handler,MessageQueue,Runnable,Looper

1.Runnable & MessageQueue:

Runnable 和Message 是訊息的2種載體。

訊息的行為本質上就是 一段操作Runnable,或者是一段資料Message,包含這操作內容,由handlemessage來判斷處理。

他們的操作方式就是:

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

public final boolean postAtTime(Runnable r, long uptimeMillis)

public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)

上面就是Runnable的方法,可以看到Runnable會被分裝成Message的形式傳送。

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

所以本質上,都是以Message的封裝方式處理。

最終所有的訊息都會放入MessageQueue裡面。

MessageQueue並不是一個真正的佇列,而是連結串列。

Looper就是迴圈在某件事情,類似於while(true)乾的事情。

Handler就是真正做事情的。

Looper不斷的從MessageQueue從取出資料,然後交給handler來處理。

2.Handler:

framework/base/core/android/os/Handler.java

其實handler的作用,它的註釋已經解釋的非常清楚。

/**
 * A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link MessageQueue}.  Each Handler
 * instance is associated with a single thread and that thread's message
 * queue.  When you create a new Handler, it is bound to the thread /
 * message queue of the thread that is creating it -- from that point on,
 * it will deliver messages and runnables to that message queue and execute
 * them as they come out of the message queue.
*
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed as some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.
*
 * <p>When posting or sending to a Handler, you can either
 * allow the item to be processed as soon as the message queue is ready
 * to do so, or specify a delay before it gets processed or absolute time for
 * it to be processed.  The latter two allow you to implement timeouts,
 * ticks, and other timing-based behavior.
*/

這個一共三段內容,大意是:

1)handler使用runnable或者message的方式傳遞,儲存在一個thread的messagequeue裡面。

當你建立一個新的handler的時候,他會與這個建立它的執行緒繫結。

對於一個Thread 來說MessageQueue,和Looper只有一個。

2)使用handler一般有2種場景。

希望do runnable或者某種Message 在in the future.

或者把一個action(Runnable or Message)傳遞到其他執行緒進行操作。

常見的操作就是在工作執行緒中使用主執行緒handler來操作UI。

3)你可以讓handler直接操作message內容,或者等待一段時間,這個時間是可以配置的。

handle的2大功能

處理message:

public void dispatchMessage(Message msg) 分發訊息
public void handleMessage(Message msg)   處理訊息,該方法通常情況下,須由子類繼承。

Looper.loop()方法會呼叫dispatchMessage來處理訊息。

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

handler的子類通過過載該方法,可以修改handler的訊息派發方式。

handler的第二個作用是把message & Runnable分裝到MessageQueue裡面。

handler,messagequeue,looper目的是什麼,目的就是啟動訊息機制。

MessageQueue:

MessageQueue從哪裡得到,從Handler原始碼看到,是從Looper裡面來的。

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Looper:

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

Looper 建構函式就幹了2件事。

建立Messagequeue,所以 每個Looper都有唯一的一個MessageQueue與之對應。

得到執行thread。

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Looper有個特殊的變數,ThreadLocal, 這個物件只對自己所在的執行緒全域性,其他的執行緒無法看到它。

Looper提供了很多static的方法,所以肯定還有一些能都識別“身份“的方法。

這些方法在我們使用looper 的時候,最重要的是如下2個:

    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));
    }
/**
     * 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.");
        }
        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();
        }
    }

prepare才是looper建立以及和thread繫結的地方。

looper.loop()方法是整個looper機制啟動的地方。

從此thread就會接受訊息和處理訊息了。

這裡有個小問題:

            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

一開始的時候,MessageQueue handler沒有傳遞訊息進佇列,按理說取到的訊息是null,這樣looper就直接退出了。

這個問題等到分析原始碼的時候,在解決。

這樣handler,messaqequeue,looper, 和thread都關聯起來了。

下面還有一個mainlooper的問題。

public static void main(String[] args) {
    ...

    Looper.prepareMainLooper();

    if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

    Looper.loop();
}

以上是ActivityThread的部分入口函式main的原始碼:

可見prepareMainLooper()的方法,是給主執行緒使用的。

而looper那邊的

private static Looper sMainLooper;  // guarded by Looper.class

是為了給其他執行緒應用使用。

這樣其他執行緒可以給主執行緒發訊息。

如圖所示:主執行緒的looper將由sMainLooper作為應用,儲存在static空間中,其他工作執行緒可以訪問它

 至此,整個訊息機制的框架已經驅動起來。

本文參考:

1.《深入理解android核心設計思想》林學森

2.《Android核心剖析》

相關文章:

相關推薦

android 程序/執行管理----訊息機制框架

一:android 程序和執行緒 程序是程式執行的一個例項。android通過4大主件,弱化了程序的概念,尤其是在app層面,基本不需要關係程序間的通訊等問題。 但是程式的本質沒有變,尤其是多工系統,以事件為驅動的軟體系統基本模式都是如下: 程式的入口一般是main: 1.初始化: 比如建立視窗,申

android 程序/執行管理----關於執行的迷思

一:程序和執行緒的由來 程序是計算機科技發展的過程的產物。 最早計算機發明出來,是為了解決數學計算而發明的。每解決一個問題,就要打紙帶,也就是打點。 後來人們發現可以批量的設定命令,由計算機讀取這些命令,並挨個執行。 在使用的過程中,有一個問題,如果要做I/O操作,是非常耗時的,這個時候CPU是閒著的

android 程序/執行管理----Thread,Looper / HandlerThread / IntentService

Thread,Looper的組合是非常常見的組合方式。 Looper可以是和執行緒繫結的,或者是main looper的一個引用。 下面看看具體app層的使用。 首先定義thread: package com.joyfulmath.androidstudy.thread; import co

執行管理執行的建立和執行

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:歐振聰 執行緒的建立和執行 在這個指南中,我們將學習如何在Java程式中建立和執行執行緒。與每個Java語言中的元素一樣,執行緒

執行管理AsyncTask與Thread的差別

AsycTask,做Android的應該都是熟悉的不能再熟悉了,當我們在進行耗時操作的時候,就可以使用這個了 但是有的人就在想,Thread不是也可以開一個子執行緒進行耗時操作嗎?那這兩個有什麼樣的區別呢? 那麼一起來看下AsycTask原始碼啦 p

Android執行View.post()原始碼分析——在子執行中更新UI

       提起View.post(),相信不少童鞋一點都不陌生,它用得最多的有兩個功能,使用簡便而且實用:        1)在子執行緒中更新UI。從子執行緒中切換到主執行緒更新UI,不需要額外new一個Handler例項來實

[Java 多執行技術]執行程序以及並行和併發的概念

程序與執行緒 1、 程序(Process)是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎。在早期面向程序設計的計算機結構中,程序是程式的基本執行實體;在當代面向執行緒設計的計算機結構中,程序是執行緒的容

Android執行執行

前言,學習安卓很久了,一直也沒有學部落格的習慣,下決心從今天開始要養成寫部落格總結學習經驗的好習慣! 一.Android中執行緒與執行緒池的簡介 在Android中執行緒主要可以分為兩大類:一個用於處理介面相關與使用者互動的執行緒-主執行緒;一個用於處理耗時任務-子執行緒

android執行佇列執行阻塞

我們的程式因為在子執行緒裡面做耗時操作,記憶體洩漏導致了程式的崩潰,下面是崩潰的日誌,從日誌中,可以清晰的看出,超過程式的最大記憶體4M,導致程式記憶體洩漏崩潰 下載小圖片(6張1M以下)的效果圖(2M的圖程式會崩潰): 執行出來大致是這種效果 FATAL EXCE

C#多執行程式設計程序執行

一、 程序         簡單來說,程序是對資源的抽象,是資源的容器,在傳統作業系統中,程序是資源分配的基本單位,而且是執行的基本單位,程序支援併發執行,因為每個程序有獨立的資料,獨立的堆疊空間。一個程式想要併發執行,開多個程序即可。

java併發學習--執行

關於java中的執行緒池,我一開始覺得就是為了避免頻繁的建立和銷燬執行緒吧,先建立一定量的執行緒,然後再進行復用。但是要具體說一下如何做到的,自己又說不出一個一二三來了,這大概就是自己的學習習慣流於表面,不經常深入的結果吧。所以這裡決定系統的學習一下執行緒池的相關知識。   自己稍微總結了一下,

java多執行系列:Thread、Runnable、Callable實現多執行的區別

實現多執行緒 java實現多執行緒的方法有三種,分別是繼承thread類,實現runnable介面,實現callable介面(call方法有返回值) /** * 繼承Thread */ public class MyThread extends Thread{ int a = 0;

java多執行-初探

啥是多執行緒?跟程序又是啥關係?   比方說:我去洗手,洗完手去吃飯。 程序(Processor) 洗手跟吃飯是兩個程序。 執行緒(Thread) 在洗手的程序裡,我同時聽歌,還唱歌。那這裡洗手是一個程序,聽歌跟唱歌是兩個執行緒。 在吃飯的程序裡,我同時聽歌,還

執行基礎

最近讀了高洪巖的《Java多執行緒程式設計核心技術》一書,打算記錄下多執行緒的基礎知識點,也算對本書的一個讀後感了。目前打算分四五篇博文進行記錄。 第一篇主要是記錄執行緒的概念,建立,常用的基礎方法等。 1. 什麼是執行緒? 通常我們所說執行緒是程序的最小單位。那麼問題來了,什麼是程序呢?程序就是作業系統結構

執行管理操作執行的中斷機制

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:歐振聰 操作執行緒的中斷機制 在之前的指南里,你學習瞭如何中斷執行執行緒和如何對Thread物件的中斷控制。之前例子中的機制可以

執行管理使用本地執行變數

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:方騰飛 使用本地執行緒變數 併發應用的一個關鍵地方就是共享資料。這個對那些擴充套件Thread類或者實現Runnable介面的物

執行管理執行的中斷

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:歐振聰 執行緒的中斷 一個多個執行緒在執行的Java程式,只有當其全部的執行緒執行結束時(更具體的說,是所有非守護執行緒結束或者

基本執行同步引言

宣告:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González     譯者:許巧輝 校對:方騰飛 引言 在這個章節中,我們將覆蓋: 同步方法 介紹 在併發程式設計中發生的最常見的一種情況是超過一個

執行管理線上程裡處理不受控制的異常

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:方騰飛 線上程裡處理不受控制的異常 Java裡有2種異常: 檢查異常(Checked exceptions): 這些異常必須強

執行管理獲取和設定執行資訊

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:歐振聰 獲取和設定執行緒資訊 Thread類的物件中儲存了一些屬性資訊能夠幫助我們來辨別每一個執行緒,知道它的狀態,調整控制其優