Handler我們在項(xiàng)目中經(jīng)常用到,因?yàn)锳ndroid中避免在主線程中處理耗時操作,我們就會借助Handler來更新數(shù)據(jù),使用方式就在主線程中創(chuàng)建Handler對象,在例如請求網(wǎng)絡(luò)數(shù)據(jù)時的子線程中發(fā)送消息,在主線程中收到消息,更新ui。
Handler的消息處理由以下幾個部分組成
Looper, Message, MessageQueue,ThreadLocal
我們先看一下各個名詞的源碼描述部分:
Meeage

翻譯一下:定義一個消息,該消息包含描述和任意的數(shù)據(jù)對象,用來發(fā)送給Handler,Message包含2個int的字段和一個額外的對象字段。
可以通過obtain Message.obtain()或Handler.obtainMessage()來的到Message
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*/
public int what;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg2;
/**
* An arbitrary object to send to the recipient. When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
*
* <p>Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
public Object obj;
what用來命名的,區(qū)別其他的Handler
arg1和arg2用來存儲int數(shù)據(jù)的
what是用來攜帶對象數(shù)據(jù)的
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.
*
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
被Looper分配的消息列表,Messages不是直接添加到MessageQueue中的,而是通過Looper相關(guān)聯(lián)的Hander添加的
你可以通過Looper.myQueue來檢索當(dāng)前的MessageQueue
Looper
/**
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
*/
Looper用于為線程運(yùn)行消息循環(huán)的,線程默認(rèn)是沒有消息循環(huán)的,要創(chuàng)建一個,請調(diào)用Looper.prepare在執(zhí)行循環(huán)的線程中,然后在調(diào)用Looper.loop讓它處理消息,直到循環(huán)停止。
示例如下:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
這段文案描述了Handler和Looper的使用情況,但是我們在主線程中使用Handler并沒有使用Looper.prepare()和 Looper.loop()
類似這種寫法:
public class TestHandlerActivity extends AppCompatActivity {
private static final String TAG = "TestHandlerActivity";
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//獲得剛才發(fā)送的Message對象,然后在這里進(jìn)行UI操作
Log.e(TAG,"------------> msg.what = " + msg.what);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
initData();
}
private void initData() {
//開啟一個線程模擬處理耗時的操作
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
//通過Handler發(fā)送一個消息切換回主線程(mHandler所在的線程)
mHandler.sendEmptyMessage(0);
}
}).start();
}
很奇怪是不是
我們看一下ActivityThread的啟動函數(shù)Main函數(shù)
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
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");
}
原來是在main方法中調(diào)用Looper.prepareMainLooper()和Looper.loop()
我們來看一下prepareMainLooper這個方法
/**
* 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();
}
}
main looper是Android應(yīng)用啟動時就創(chuàng)建了,你用不著來調(diào)用這個方法。
在子線程中創(chuàng)建Handler,示例如下:
//開啟一個線程模擬處理耗時的操作
new Thread(new Runnable() {
@Override
public void run() {
mHandler.sendEmptyMessage(0);
//調(diào)用Looper.prepare()方法
Looper.prepare();
mHandlerThread = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("sub thread","---------> msg.what = " + msg.what);
}
};
mHandlerThread.sendEmptyMessage(1);
//調(diào)用Looper.loop()方法
Looper.loop();
}
}).start();
ThreadLocal
我們來看一下ThreadLocal的文字描述部分
/**
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread's id is assigned the first time it invokes {@code ThreadId.get()}
* and remains unchanged on subsequent calls.
* <pre>
* import java.util.concurrent.atomic.AtomicInteger;
*
* public class ThreadId {
* // Atomic integer containing the next thread ID to be assigned
* private static final AtomicInteger nextId = new AtomicInteger(0);
*
* // Thread local variable containing each thread's ID
* private static final ThreadLocal<Integer> threadId =
* new ThreadLocal<Integer>() {
* @Override protected Integer initialValue() {
* return nextId.getAndIncrement();
* }
* };
*
* // Returns the current thread's unique ID, assigning it if necessary
* public static int get() {
* return threadId.get();
* }
* }
* </pre>
* <p>Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the {@code ThreadLocal}
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).
*
* @author Josh Bloch and Doug Lea
* @since 1.2
*/
ThreadLocal 不是 Thread,是一個線程內(nèi)部的數(shù)據(jù)存儲類,通過它可以在指定的線程中存儲數(shù)據(jù),對數(shù)據(jù)存儲后,只有在線程中才可以獲取到存儲的數(shù)據(jù),對于其他線程來說是無法獲取到數(shù)據(jù)