編輯:關於Android編程
羅朝輝 (http://blog.csdn.net/kesalin)CC 許可,轉載請注明出處
在前文《Android多線程分析之二:Thread的實現》中已經詳細分析了Android Thread 是如何創建,運行以及銷毀的,其重點是對相應 native 方法進行分析,今天我將聚焦於 Android Framework 層多線程相關的類:Handler, Looper, MessageQueue, Message 以及它們與Thread 之間的關系。可以用一個不太妥當的比喻來形容它們之間的關聯:如果把 Thread 比作生產車間,那麼 Looper 就是放在這車間裡的生產線,這條生產線源源不斷地從 MessageQueue 中獲取材料 Messsage,並分發處理 Message (由於Message 通常是完備的,所以 Looper 大多數情況下只是調度讓 Message 的 Handler 去處理 Message)。正是因為消息需要在 Looper 中處理,而 Looper 又需運行在 Thread 中,所以不能隨隨便便在非 UI 線程中進行 UI 操作。 UI 操作通常會通過投遞消息來實現,只有往正確的 Looper 投遞消息才能得到處理,對於 UI 來說,這個 Looper 一定是運行在 UI 線程中。
在編寫 app 的過程中,我們常常會這樣來使用 Handler:
Handler mHandler = new Handler(); mHandler.post(new Runnable(){ @Override public void run() { // do somework } });或者如這系列文章第一篇中的示例那樣:
private Handler mHandler= new Handler(){ @Override public void handleMessage(Message msg) { Log.i("UI thread", " >> handleMessage()"); switch(msg.what){ case MSG_LOAD_SUCCESS: Bitmap bitmap = (Bitmap) msg.obj; mImageView.setImageBitmap(bitmap); mProgressBar.setProgress(100); mProgressBar.setMessage("Image downloading success!"); mProgressBar.dismiss(); break; case MSG_LOAD_FAILURE: mProgressBar.setMessage("Image downloading failure!"); mProgressBar.dismiss(); break; } } }; Message msg = mHandler.obtainMessage(MSG_LOAD_FAILURE, null); mHandler.sendMessage(msg);
final MessageQueue mQueue; final Looper mLooper; final Callback mCallback; final boolean mAsynchronous; public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } public Handler(Callback callback, boolean async) { 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() { this(null, false); }
mLooper:線程的消息處理循環,注意:並非每一個線程都有消息處理循環,因此 Framework 中線程可以分為兩種:有 Looper 的和無 Looper 的。為了方便 app 開發,Framework 提供了一個有 Looper 的 Thread 實現:HandlerThread。在前一篇《Thread的實現》中也提到了兩種不同 Thread 的 run() 方法的區別。
/** * Handy class for starting a new thread that has a looper. The looper can then be * used to create handler classes. Note that start() must still be called. */ public class HandlerThread extends Thread { Looper mLooper; /** * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { } public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; } /** * This method returns the Looper associated with this thread. If this thread not been started * or for any reason is isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized. * @return The looper. */ public Looper getLooper() { if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; } }
/** 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) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }
Handler 的成員變量 mQueue 是其成員變量 mLooper 的成員變量,這裡只是為了簡化書寫,單獨拿出來作為 Handler 的成員變量;成員變量 mCallback 提供了另一種使用Handler 的簡便途徑:只需實現回調接口 Callback,而無需子類化Handler,下面會講到的:
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { public boolean handleMessage(Message msg); }
同是否有無 Looper 來區分 Thread 一樣,Handler 的構造函數也分為自帶 Looper 和外部 Looper 兩大類:如果提供了 Looper,在消息會在該 Looper 中處理,否則消息就會在當前線程的 Looper 中處理,注意這裡要確保當前線程一定有 Looper。所有的 UI thread 都是有 Looper 的,因為 view/widget 的實現中大量使用了消息,需要 UI thread 提供 Looper 來處理,可以參考view.java:
view.java public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); return true; } ViewRootImpl.java private void performTraversals() { .... // Execute enqueued actions on every traversal in case a detached view enqueued an action getRunQueue().executeActions(attachInfo.mHandler); ... } static RunQueue getRunQueue() { RunQueue rq = sRunQueues.get(); if (rq != null) { return rq; } rq = new RunQueue(); sRunQueues.set(rq); return rq; } /** * The run queue is used to enqueue pending work from Views when no Handler is * attached. The work is executed during the next call to performTraversals on * the thread. * @hide */ static final class RunQueue { ... void executeActions(Handler handler) { synchronized (mActions) { final ArrayListactions = mActions; final int count = actions.size(); for (int i = 0; i < count; i++) { final HandlerAction handlerAction = actions.get(i); handler.postDelayed(handlerAction.action, handlerAction.delay); } actions.clear(); } } }
public final class ActivityThread { ... final Looper mLooper = Looper.myLooper(); }
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
W/dalvikvm(796): threadid=11: thread exiting with uncaught exception (group=0x40a71930) E/AndroidRuntime(796): FATAL EXCEPTION: Thread-75 E/AndroidRuntime(796): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. E/AndroidRuntime(796): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4746) E/AndroidRuntime(796): at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:823) E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473) E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473) E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473) E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473) E/AndroidRuntime(796): at android.view.View.requestLayout(View.java:15473) E/AndroidRuntime(796): at android.widget.ImageView.setImageDrawable(ImageView.java:406) E/AndroidRuntime(796): at android.widget.ImageView.setImageBitmap(ImageView.java:421) E/AndroidRuntime(796): at com.example.thread01.MainActivity$2$1.run(MainActivity.java:80)
/** * 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(); }
在分析 Looper::Loop() 這個關鍵函數之前,先來理一理 Thread,Looper,Handler,MessageQueue 的關系:Thread 需要有 Looper 才能處理消息(也就是說 Looper 是運行在 Thread 中),這是通過在自定義 Thread 的 run() 函數中調用 Looper::prepare() 和 Looper::loop() 來實現,然後在 Looper::loop() 中不斷地從 MessageQueue 獲取由 Handler 投遞到其中的 Message,並調用 Message 的成員變量 Handler 的 dispatchMessage 來處理消息。
下面先來看看 Looper 的構造函數:
final MessageQueue mQueue; final Thread mThread; volatile boolean mRun; private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
/** * 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(); } }
SystemServer.java 中的 ServerThread,ServerThread 的重要性就不用說了,絕大部分 Android Service 都是這個線程中初始化的。這個線程是在 Android 啟動過程中的 init2() 方法啟動的:
public static final void init2() { Slog.i(TAG, "Entered the Android system server!"); Thread thr = new ServerThread(); thr.setName("android.server.ServerThread"); thr.start(); } class ServerThread extends Thread { @Override public void run() { ... Looper.prepareMainLooper(); ... Looper.loop(); Slog.d(TAG, "System ServerThread is exiting!"); } }
public static void main(String[] args) { .... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
有了前面的鋪墊,這下我們就可以來分析 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; ... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg); msg.recycle(); } }
在系統之中,通過對話框可以對用戶的某些操作進行提示,但是在Android平台之中也提供了另外一套更加友好的提示界面效果,而且這種界面在提示用戶的時候不會打斷用戶的正常操作
由於IPC機制牽扯的東西比較多,所以這裡將分為一個系列進行總結主要介紹內如如下:IPC簡介 Android中的多進程模式開啟多進程模式 多進程模式的運行機制 IPC基礎概
Android開發本質上就是手機和web服務器之間進行通信,從服務端需要獲取數據,但是當訪問的數據比較大,比較多,並且是重復數據時,會極大影響性能,甚至應用崩潰,手機卡死
SparseArray是Android框架獨有的類,在標准的JDK中不存在這個類。它要比 HashMap 節省內存,某些情況下比HashMap性能更好,按照官方問答的解