淺析Handler、Looper機制。通過自定義Handler、Looper,讓你有更直觀的了觀!
來源:程序員人生 發布時間:2014-11-21 08:26:19 閱讀次數:2686次
轉載請注明出處:http://blog.csdn.net/liu470368500/article/details/40625333
Handler、Looper。http://www.vxbq.cn/cxyms/的時候被問到的機率非常高的1個問題。固然。我說的是像我們這樣的低級。。。1般講授Handler、Looper機制的都是通過源碼去講授。這里我來通過自定義Handler、Looper。讓各位看官能有個更直觀的了解。相信有了這篇博文的基礎。再看Handler、Looper的源碼。理解起來就更容易了。
為了與安卓原生的相接軌。這里自定義的Handler-Looper使用的邏輯基本與系統原生的相1致。
先貼MyHanlder代碼:
/**
* 自定義Handler
*
* @author lzh
*
*/
public class MyHandler {
// 用于進行線程間通訊的阻塞隊列
private BlockingQueue<MyMessage> mQueue;
// 處理消息的回調
private CallBack callBack;
public MyHandler(CallBack callBack) {
super();
MyLooper looper = MyLooper.myLooper();
if (looper == null) {
throw new RuntimeException(
"在新開的線程中。創建MyHandler對象需要先調用MyLooper.prepare()方法。");
}
mQueue = looper.mQueue;
this.callBack = callBack;
}
/**
* 消息接收的回調
*
* @author Administrator
*
*/
public interface CallBack {
/**
* 處理消息
*
* @param msg
*/
void handleMessage(MyMessage msg);
}
/**
* 發送消息
*
* @param msg
*/
public void sendMessage(MyMessage msg) {
msg.target = this;
try {
mQueue.put(msg);
} catch (InterruptedException e) {
}
}
/**
* 派發消息
*
* @param msg
*/
public void dispatchMessage(MyMessage msg) {
callBack.handleMessage(msg);
}
}
再看MyLooper的代碼:
public class MyLooper {
private static ThreadLocal<MyLooper> sThreadLocal = new ThreadLocal<MyLooper>();
private static MyLooper myLooper;
/** 1個線程對應1個阻塞隊列。 */
public BlockingQueue<MyMessage> mQueue = null;
private MyLooper() {
super();
mQueue = new LinkedBlockingQueue<MyMessage>();
}
/**
* 為本線程準備對應的MyLooper對象
*/
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException(
"Only one MyLooper may be created per thread");
}
sThreadLocal.set(new MyLooper());
}
/**
* 獲得當前線程相對應的Looper對象
*
* @return 當未調用prepare()方法時。ThreadLocal.get()方法返回的為null;
*/
public static MyLooper myLooper() {
return sThreadLocal.get();
}
/**
* 這里啟動消息循環
*/
public static void loop() {
while (true) {
try {
myLooper = myLooper();
BlockingQueue<MyMessage> mQueue = myLooper.mQueue;
// take()方法是個阻塞方法。線程運行到此會阻塞住。以準備接收發過來的消息
MyMessage msg = mQueue.take();
msg.target.dispatchMessage(msg);
} catch (InterruptedException e) {
// 當線程關閉的時候會出現此異常。此時退出循環
return;
}
}
}
}
本來安卓原生中使用的是MessageQueue。但是卻死活創建不出來。這里只有使用BlockQueue代替了。
當中的ThreadLocal可能有部份朋友有點陌生。這是線程局部變量。它的set方法和get()方法比較成心思。是和線程相干的。你在哪一個線程里面set變量進去。你在哪一個線程里面get()出來的就是哪一個。所以在MyLooper中得先調用prepare()方法。先將與此線程相干的MyLooper實例創建出來加入進去。這樣便能保存1個線程只有1個Looper。相應的也只有1個阻塞隊列。
接下來看MyMessage代碼:
public class MyMessage {
public int msg1;
public int msg2;
public int what;
public Object obj;
public MyHandler target;
public Runnable runnable;
}
原生的Message由因而final標記的。而Message里面存的Handler對象又比較重要。得要依托它來指定終究的消息應當發送給哪一個Handler來接收。所以。這個也自定義了。
下面開始來測試。由于安卓不允許在UI線程中有阻塞操作。所以這里我們使用SurfaceView在子線程中畫圖來測試是不是可進行線程間通訊。
/**
* 測試自定義的Handler與Looper的測試工程,由于內部有阻塞隊列。而安卓的機制是不允許此類的阻塞行動在主線程中出現。
* 所以此處用SurfaceView在子線程中進行測試
*
* @author Administrator
*
*/
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MySurfaceView(this));
}
class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,
CallBack {
private static final String TAG = "MySurfaceView";
private SurfaceHolder mHolder;
Thread mThread;
MyHandlerCreateRunnable mRunnable;
private Paint mPaint;
private MyHandler handler = null;
public MySurfaceView(Context context) {
super(context);
// 初始化holder:
mHolder = getHolder();
mHolder.addCallback(this);
mRunnable = new MyHandlerCreateRunnable();
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setAntiAlias(true);
mPaint.setTextAlign(Align.CENTER);
mPaint.setTextSize(45);
new Thread(mRunnable).start();
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "==surfaceCreated==");
// mQueue = new LinkedBlockingQueue<String>();
new Thread(new MyTimerRunnable()).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.d(TAG, "==surfaceChanged==");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "==surfaceDestroyed==");
}
@Override
public void handleMessage(MyMessage msg) {
synchronized (mHolder) {
Canvas mCanvas = null;
System.out.println("==lockCanvas==");
mCanvas = mHolder.lockCanvas();// 鎖定畫布。以后就能夠在此畫布上畫圖了。
String content = (String) msg.obj;
mCanvas.drawColor(Color.WHITE);
mCanvas.drawText(content, 100, 400, mPaint);
mHolder.unlockCanvasAndPost(mCanvas);//
}
}
/**
* 定時發送消息的線程
*
* @author Administrator
*
*/
class MyTimerRunnable implements Runnable {
int index = 0;
@Override
public void run() {
while (true) {
MyMessage msg = new MyMessage();
msg.obj = "這是第" + index + "個";
index++;
handler.sendMessage(msg);
if (index >= 50) {
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 創建MyHandler的線程
*
* @author Administrator
*
*/
class MyHandlerCreateRunnable implements Runnable {
@Override
public void run() {
MyLooper.prepare();
handler = new MyHandler(MySurfaceView.this);
MyLooper.loop();
}
}
}
}
好了。代碼都貼完了。現在結合到1塊來看。在MyHandlerCreateRunnable中。我們對調用了MyLooper.prepare()方法對當前線程進行了線程局部變量保存。再創建出MyHandler對象。最后讓消息循環啟動。這時候。在此線程中如果沒有消息到來。就會在MyLooper的loop()方法中。被阻塞隊列的take()方法所阻塞。直到有消息到來。
然后我們在MyTimerRunnable中。對消息進行創建。并使用在MyHandlerCreateRunnable線程中創建的handler對象。對消息進行發送。為了方便。下面貼出局部代碼繼續分析
/**
* 發送消息
*
* @param msg
*/
public void sendMessage(MyMessage msg) {
msg.target = this;
try {
mQueue.put(msg);
} catch (InterruptedException e) {
}
}
在此handler的sendMessage方法處。將MyHandler本身作為msg對象的1個成員變量賦值。再將些消息寄存入此消息隊列中。放入以后。MyLooper.loop()方法中的take()方法就會獲得到些msg對象并消除阻塞。繼續運行。
/**
* 這里啟動消息循環
*/
public static void loop() {
while (true) {
try {
myLooper = myLooper();
BlockingQueue<MyMessage> mQueue = myLooper.mQueue;
// take()方法是個阻塞方法。線程運行到此會阻塞住。以準備接收發過來的消息
MyMessage msg = mQueue.take();
msg.target.dispatchMessage(msg);
} catch (InterruptedException e) {
// 當線程關閉的時候會出現此異常。此時退出循環
return;
}
}
}
運行以后可以看到。通過調用msg.target.dispatchMessage(msg)方法將此message發送給之前我們用來發送消息的MyHandler對象。
/**
* 派發消息
*
* @param msg
*/
public void dispatchMessage(MyMessage msg) {
callBack.handleMessage(msg);
}
接著立馬就將此消息對象發送給了自己定義的回調方法中。也就是我們handler用來處理消息的回調方法。handleMessage。
所以。弄了半天。真正用來對線程間進行通訊的其實就是1個阻塞隊列。。。相信這個結論夠簡潔明了。。。這是幾近完全仿照安卓原生的handler-looper邏輯來寫的。所以。如果你理解了這篇博客。相信更進1步的看Handler-Looper源碼會通暢很多。。。
下面提供demo下載。
點擊下載demo
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈