android 進程/線程管理(一)----訊息機制的架構

來源:互聯網
上載者:User

標籤:

一: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空間中,其他背景工作執行緒可以訪問它

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

android 進程/線程管理(一)----訊息機制的架構

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.