AndroidスレッドのLooperに関する知識
Androidの
Looper
クラスは、androidスレッドでメッセージ処理を行うためのメッセージループとメッセージキューをカプセル化するためのクラスです.Handler
は、メッセージキューにメッセージを挿入するためのツールクラスと見なすことができる.Android 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 prepare() in
the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler class.
This is a typical example of the implementation of a Looper thread, using the separation
of prepare() and loop() to create an initial Handler to communicate with the Looper.
Looper実現原理
1. Looper
2. Looper , MessageQueue( )。
3. 。
4. Looper , “ ” , , ,
。 , , handler handlemessage
Looper作成プロセス
mainHandler = new Handler()
はnew Handler(Looper.myLooper())
と等価である.Looper.myLooper()
:現在のプロセスのlooperオブジェクトを取得し、同様のLooper.getMainLooper()
を使用してメインスレッドのLooperオブジェクトを取得します.E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught
exception E/AndroidRuntime( 6173): Java.lang.RuntimeException: Can't create handler inside
thread that has not called Looper.prepare()
は、非プライマリ・スレッドではデフォルトでLooperオブジェクトが作成されていないため、Looper.prepare()
を呼び出してLooperを有効にする必要があります.Looper.prepare()
関連コード:/**
* Looper, loop() , quit()
* 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) {
if (sThreadLocal.get() != null) {// Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
// Looper
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()}
* Looper, 。 Looper
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//
mThread = Thread.currentThread();//
}
Looper.loop()
Looperを動作させ、メッセージキューからメッセージを取り出し、メッセージを処理する.注意:Looper.loop()
以降に書かれたコードは実行されません.この関数の内部はループであるべきです.mHandler.getLooper().quit
()が呼び出されると、loopは中止され、その後のコードが実行されます.Looper.loop()
ソースコード:/**
* 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 (;;) {
// , queue.next()
// Looper.quit()
Message msg = queue.next();
if (msg == null) {// MessageQueue.quit(); MessageQueue.dispose()
// No message indicates that the message queue is quitting.
//Return here if the message loop has already quit and been disposed.
return;
}
//msg.target Handler ,
msg.target.dispatchMessage(msg);
// 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);
}
//Message
msg.recycleUnchecked();
}
}
Looper.quit()
ソースコード
/**
* Quits the looper. ( )
*
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
*
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
*
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
*
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
/**
* Quits the looper safely. ( )
*
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
*
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
*
*/
public void quitSafely() {
mQueue.quit(true);
}
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
// UI
//1,Toast
//2,Dialog
//3,Snackbar UI , Looper.perpare(). Hander Looper
Looper.loop();
// looper.quit()
}
}