Android Handler 訊息機制(解惑篇)
Android中的訊息處理機制概述
大家對於Android中的訊息處理機制的用法一定都比較熟悉,至於工作原理估計不少人有研究。就像我們自己寫的類我們用起來比較熟悉一樣,如果我們熟悉了訊息處理機制的具體實現,那麼我們用起來肯定也會事半功倍。
博主之前只是稍有涉獵,對其中一些地方也還心存疑慮,比如既然Looper.loop()裡是一個死迴圈,那它會不會很消耗CPU呢?死迴圈阻塞了執行緒,那我們其他的事務是如何被處理的呢?Android的UI執行緒是在哪裡被初始化的呢?等等。索性今天就把他們放到一起,說道說道。
Android中執行緒的分類
帶有訊息佇列,用來執行迴圈性任務(例如主執行緒、android.os.HandlerThread)
有訊息時就處理
沒有訊息時就睡眠
沒有訊息佇列,用來執行一次性任務(例如java.lang.Thread)
- 任務一旦執行完成便退出
帶有訊息佇列執行緒概述
四要素
Message(訊息)
MessageQueue(訊息佇列)
Looper(訊息迴圈)
Handler(訊息傳送和處理)
四要素的互動過程
具體工作過程
訊息佇列的建立
訊息迴圈
訊息的傳送
最基本的兩個API
Handler.sendMessage
- 帶一個Message引數,用來描述訊息的內容
Handler.post
- 帶一個Runnable引數,會被轉換為一個Message引數
訊息的處理
基於訊息的非同步任務介面
android.os.HandlerThread
- 適合用來處於不需要更新UI的後臺任務
android.os.AyncTask
- 適合用來處於需要更新UI的後臺任務
帶有訊息佇列執行緒的具體實現
ThreadLocal
ThreadLocal並不是一個Thread,而是Thread的區域性變數。當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。
從執行緒的角度看,目標變數就象是執行緒的本地變數,這也是類名中“Local”所要表達的意思。
Looper
用於在指定執行緒中執行一個訊息迴圈,一旦有新任務則執行,執行完繼續等待下一個任務,即變成Looper執行緒。Looper類的註釋裡有這樣一個例子:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
//將當前執行緒初始化為Looper執行緒
Looper.prepare();
// ...其他處理,如例項化handler
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
// 開始迴圈處理訊息佇列
Looper.loop();
}
}
其實核心程式碼就兩行,我們先來看下Looper.prepare()方法的具體實現
public final class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
//Looper內的訊息佇列
final MessageQueue mQueue;
// 當前執行緒
final Thread mThread;
private Printer mLogging;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//試圖在有Looper的執行緒中再次建立Looper將丟擲異常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//~省略部分無關程式碼~
}
從中我們可以看到以下幾點:
- prepare()其核心就是將looper物件定義為ThreadLocal
- 一個Thread只能有一個Looper物件
- prepare()方法會呼叫Looper的構造方法,初始化一個訊息佇列,並且指定當前執行緒
- 在呼叫Looper.loop()方法之前,確保已經呼叫了prepare(boolean quitAllowed)方法,並且我們可以呼叫quite方法結束迴圈
說到初始化MessageQueue,我們來看下它是幹什麼的
/**
* Low-level class holding the list of messages to be dispatched by a
* {@link Looper}. Messages are not added directly to a MessageQueue,
* but rather through {@link Handler} objects associated with the Looper.
*
*You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
它是一個低等級的持有Messages集合的類,被Looper分發。Messages並不是直接加到MessageQueue的,而是通過Handler物件和Looper關聯到一起。我們可以通過Looper.myQueue()方法來檢索當前執行緒的MessageQueue。
接下來再看看Looper.loop()
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//得到當前執行緒Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//得到當前looper的MessageQueue
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);
}
//將真正的處理工作交給message的target,即handler
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.recycleUnchecked();
}
}
通過這段程式碼可知,呼叫loop方法後,Looper執行緒就開始真正工作了,它不斷從自己的MessageQueue中取出隊頭的訊息(或者說是任務)執行。
除了prepare()和loop()方法,Looper類還有一些比較有用的方法,比如
Looper.myLooper()得到當前執行緒looper物件
getThread()得到looper物件所屬執行緒
quit()方法結束looper迴圈
這裡需要注意的一點是,quit()方法其實呼叫的是MessageWueue的quite(boolean safe)方法。
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
我們看到其實主執行緒是不能呼叫這個方法退出訊息佇列的。至於mQuitAllowed引數是在Looper初始化的時候初始化的,主執行緒初始化呼叫的是Looper.prepareMainLooper()方法,這個方法把引數設定為false。
Message
在整個訊息處理機制中,message又叫task,封裝了任務攜帶的資訊和處理該任務的handler。我們看下這個類的註釋
/**
*
* Defines a message containing a description and arbitrary data object that can be
* sent to a {@link Handler}. This object contains two extra int fields and an
* extra object field that allow you to not do allocations in many cases.
*
*While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.
*/
這個類定義了一個包含描述和一個任意型別物件的物件,它可以被髮送給Handler。
從註釋裡我們還可以瞭解到以下幾點:
儘管Message有public的預設構造方法,但是你應該通過Message.obtain()來從訊息池中獲得空訊息物件,以節省資源。
如果你的message只需要攜帶簡單的int資訊,請優先使用Message.arg1和Message.arg2來傳遞資訊,這比用Bundle更省記憶體
用message.what來標識資訊,以便用不同方式處理message。
Handler
從MessageQueue的註釋中,我們知道新增訊息到訊息佇列是通過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.
*
*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.
*
*/
註釋比較簡單,這裡就不過多翻譯了,主要內容是:每一個Handler例項關聯了一個單一的thread和這個thread的messagequeue,當Handler的例項被建立的時候它就被繫結到了建立它的thread。它用來排程message和runnables在未來某個時間點的執行,還可以排列其他執行緒裡執行的操作。
public class Handler {
//~省略部分無關程式碼~
final MessageQueue mQueue;
final Looper mLooper;
public Handler() {
this(null, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(boolean async) {
this(null, async);
}
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;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//~省略部分無關程式碼~
}
先看構造方法,其實裡邊的重點是初始化了兩個變數,把關聯looper的MessageQueue作為自己的MessageQueue,因此它的訊息將傳送到關聯looper的MessageQueue上。
有了handler之後,我們就可以使用Handler提供的post和send系列方法向MessageQueue上傳送訊息了。其實post發出的Runnable物件最後都被封裝成message物件
接下來我們看一下handler是如何傳送訊息的
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
這裡我們只列出了一種呼叫關係,其他呼叫關係大同小異,我們來分析一下
- 呼叫getPostMessage(r),把runnable物件新增到一個Message物件中。
- sendMessageDelayed(getPostMessage(r), 0),基本沒做什麼操作,又繼續呼叫sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在這個方法裡拿到建立這個Handler物件的執行緒持有的MessageQueue。
- 呼叫enqueueMessage(queue, msg, uptimeMillis)方法,給msg物件的target變數賦值為當前的Handler物件,然後放入到MessageQueue。
那傳送訊息說完了,那我們的訊息是怎樣被處理的呢?
我們看到message.target為該handler物件,這確保了looper執行到該message時能找到處理它的handler,即loop()方法中的關鍵程式碼。
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
我們看到這裡最終又呼叫到了我們重寫的handleMessage(Message msg)方法來做處理子執行緒發來的訊息或者呼叫handleCallback(Message message)去執行我們子執行緒中定義並傳過來的操作。
思考
為什麼要有Handler機制
這個問題可以這麼考慮
- 我們如何在子執行緒更新UI?——使用Handler機制傳遞訊息到主執行緒(UI執行緒)
- 為什麼我們不在子執行緒更新UI呢?——因為Android是單執行緒模型
- 為什麼要做成單執行緒模型呢?——多執行緒併發訪問UI可能會導致UI控制元件處於不可預期的狀態。如果加鎖,雖然能解決,但是缺點也很明顯:1.鎖機制讓UI訪問邏輯變得複雜;2.加鎖導致效率低下。
Handler機制與命令模式
我在之前分享過Android原始碼中的命令模式,我們仔細分下一下不難看出Handler機制其實是一個非典型的命令模式。
接收者:Handler,執行訊息處理操作。
呼叫者:Looper,呼叫訊息的的處理方法。
命令角色:Message,訊息類。
客戶端:Thread,建立訊息並繫結Handler(接受者)。
Android主執行緒是如何管理子執行緒訊息的
我們知道Android上一個應用的入口,應該是ActivityThread。和普通的Java類一樣,入口是一個main方法。
public static void main(String[] args) {
//~省略部分無關程式碼~
//建立Looper和MessageQueue物件,用於處理主執行緒的訊息
Looper.prepareMainLooper();
//建立ActivityThread物件
ActivityThread thread = new ActivityThread();
//建立Binder通道 (建立新執行緒)
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//訊息迴圈執行
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我們可以看到其實我們在這裡初始化了我們主執行緒(UI)的Looper並且啟動它。然後就可以處理子執行緒和其他元件發來的訊息了。
為什麼主執行緒不會因為Looper.loop()裡的死迴圈卡死或者不能處理其他事務
這裡涉及到的東西比較多,概括的理解是這樣的
為什麼不會卡死
handler機制是使用pipe來實現的,主執行緒沒有訊息處理時會阻塞在管道的讀端。
binder執行緒會往主執行緒訊息佇列裡新增訊息,然後往管道寫端寫一個位元組,這樣就能喚醒主執行緒從管道讀端返回,也就是說queue.next()會呼叫返回。
主執行緒大多數時候都是處於休眠狀態,並不會消耗大量CPU資源。
既然是死迴圈又如何去處理其他事務呢?
答案是通過建立新執行緒的方式。
我們看到main方法裡呼叫了thread.attach(false),這裡便會建立一個Binder執行緒(具體是指ApplicationThread,Binder的服務端,用於接收系統服務AMS傳送來的事件),該Binder執行緒通過Handler將Message傳送給主執行緒。
ActivityThread對應的Handler是一個內部類H,裡邊包含了啟動Activity、處理Activity生命週期等方法。
參考資料
相關推薦
Android Handler 訊息機制(解惑篇)
Android中的訊息處理機制概述 大家對於Android中的訊息處理機制的用法一定都比較熟悉,至於工作原理估計不少人有研究。就像我們自己寫的類我們用起來比較熟悉一樣,如果我們熟悉了訊息處理機制的具體實現,那麼我們用起來肯定也會事半功倍。 博主之前只是稍有涉
Android的訊息機制(Handler、Looper、MessageQueue)
注:本文原始碼基於Android7.0先大概概括一下訊息機制:這裡有三個角色,Handler、looper、MessageQueue。Handler負責發訊息和處理訊息,Looper負責從MessageQueue中取出訊息給Handler處理,MessageQueue則負責儲
從Handler.post(Runnable r)再一次梳理Android的訊息機制(以及handler的記憶體洩露)
Handler 每個初學Android開發的都繞不開Handler這個“坎”,為什麼說是個坎呢,首先這是Android架構的精髓之一,其次大部分人都是知其然卻不知其所以然。今天看到Handler.post這個方法之後決定再去翻翻原始碼梳理一下Handler
Android Handler 訊息機制分析(一)
Handler,Message,MessageQueue,Looper,是android一種訊息處理機制,在android開發中經常會用到,當Handler建立後,會被繫結到它所在的執行緒上,處理訊息的成員及其功能如下: Handler:傳送一個
《Android開發藝術探索》讀書筆記——Handler訊息機制(ThreadLocal)
ThreadLocal ThreadLocal是一個執行緒內部的資料儲存類。它可以為各執行緒儲存資料,同時只能由當前執行緒獲取到儲存的資料,對於其他執行緒來說則獲取不到。它可以在不同執行緒中維護一套資料的副本,並且彼此互不干擾。 一言不合上程式碼: privat
Android查缺補漏(View篇)--事件分發機制
touch事件 滑動沖突 今天 version schema ttr 步驟 isp win 事件分發機制是Android中非常重要的一個知識點,同時也是難點,相信到目前為止很多Android開發者對事件分發機制並沒有一個非常系統的認識,當然也包括博主個人在內。可能在平時的開
Android查缺補漏(View篇)--布局文件中的“@+id”和“@id”有什麽區別?
新增 布局 parent 直接 使用 margin 移除 控件 Coding Android布局文件中的“@+id”和“@id”有什麽區別? +id表示為控件指定一個id(新增一個id),如: <cn.codingblock.view.customer_view.
Android查缺補漏(IPC篇)-- 進程間通訊基礎知識熱身
內部 eat ack 學習過程 and ... 綁定 his nec 本文作者:CodingBlock 文章鏈接:http://www.cnblogs.com/codingblock/p/8479282.html 在Android中進程間通信是比較難的一部分,同時又非常重要
深入理解Java異常處理機制 (籠統篇)
throw 種類型 綜合 IV 算術 其它 wid all 作用 開篇 1.異常處理(Exception Handling): 就是一種解決這一問題的機制,能夠較好地處理程序不能正常運行的情況。 2.異常(Exception): 是程序在運行時可能出現的
Android Handler訊息機制學習
1.概述 Handler允許你傳送和處理Message,以及和執行緒相關聯的Runnable物件。每一個Handler例項都與一個執行緒及該執行緒的MessageQueue相關聯。既當你建立一個Handler時,該Handler必須繫結一個執行緒以及該執行緒的訊息佇列,一旦它被建立,它能把message
Android Handler訊息機制原始碼解析
好記性不如爛筆頭,今天來分析一下Handler的原始碼實現 Handler機制是Android系統的基礎,是多執行緒之間切換的基礎。下面我們分析一下Handler的原始碼實現。 Handler訊息機制有4個類合作完成,分別是Handler,MessageQueue,Looper,Message Handl
手把手帶你打造一個 Android 熱修復框架(上篇)
本文來自網易雲社群作者:王晨彥前言熱修復和外掛化是目前 Android 領域很火熱的兩門技術,也是 Android 開發工程師必備的技能。目前比較流行的熱修復方案有微信的 Tinker,手淘的 Sophix,美團的 Robust,以及 QQ 空間熱修復方案。QQ 空間熱修復方
Android Handler訊息機制中的諸多疑問
前言 網上總是有很多闡述Android訊息機制的文章,基本上大同小異,都是講Handle,Message,Looper,MessageQueue這四個類會如何協同工作的。但是動腦筋的童鞋們可能總是會有如下的一些疑問,我翻閱了數多微博,很多年了,也沒有看到相關比較
Android記憶體洩露利器(hprof篇)
set processName=com.sec.android.app.dialertab;android.process.acore;com.sec.android.provider.logsprovider
訊息佇列(入門篇)
什麼是訊息佇列? 小時候,我的爸爸希望我多讀書,並常常尋找好書給我看,最開始他每次看見我寫完作業之後就給我拿來書,並親自監督我讀完之後他才忙自己的事情。久而久之,我養成了讀書的習慣。所以方式就改成了,爸爸想要我讀的書,都放在書架上,由於我已經養成了好習慣,一有
Android 開發神器系列(工具篇)之 Android WiFi ADB
做為一個多年奮戰在Android 應用開發一線的程式設計師來說,程式除錯的苦是不言而喻的,在過去的很長一段時間裡,我們如果要除錯Android 應用只能通過USB資料線,一頭連著手機,一頭聯著電腦,不敢讓手機離開電腦半步。 、
Android學習之旅(第一篇) SurfaceView的原理以及使用場景
為什麼要使用SurfaceView來實現動畫? 因為View的繪圖存在以下缺陷: View缺乏雙緩衝機制 當程式需要更新View上的影象時,程式必須重繪View上顯示的整張圖片 新執行緒無法直接更新View元件 SurfaceView的繪圖機制
ASP.NET MVC學習---(九)許可權過濾機制(完結篇)
相信對許可權過濾大傢伙都不陌生 使用者要訪問一個頁面時 先對其許可權進行判斷並進行相應的處理動作 在webform中 最直接也是最原始的辦法就是 在page_load事件中所有程式碼之前 先執行一個許可權判斷的方法 至於其專業的許可權機制這裡不做討論 想要了解的同學可以自行
Android Handler訊息機制原始碼解讀
這個東西在網上已經被很多人寫過了,自己也看過很多文章,大概因為自己比較愚笨一直對此不太理解,最近重新從原始碼的角度閱讀,並且配合著網上的一些相關部落格才算明白了一些 本文從原始碼的角度順著程式碼的執行去原始碼,限於作者的表達能力及技術水平,可能會有些問題,請耐性
值得關注的 25 個新 Android 庫和專案(上篇)
這是我最新收集的 25 個 Android 庫和專案列表,你也許會發現它們有用,有趣,並值得檢視一下。所有的都在最近 3 個月釋出的,排序不分先後。 PS :有興趣的加入Android工程師交流QQ群:752016839 主要針對Android開發人員提升自己,突破瓶頸,相