個人網站:臭蛋 www.choudan.net
莫道君行早,更有早行人。
Barcode Scanner不只是上面兩篇說的這么簡單,還有其他處理,如閃光燈,放大處理,最優的預覽尺寸等等。這些不影響對代碼的理解,知道camera的使用后,開始看看Barcode Scanner是如何高效的進行識別處理的。在android文件夾下,就有一個thread類:DecodeThread,兩個handler類:CaptureActivityHandler和DecodeHandler。在沒有認真看兩個類時,一直以為這兩個handler是理所當然的方式,以為自己就知道了。后面發現不是這么簡單,尤其是DecodeThread的實現是android 中工作線程的經典實現。
在Barcode Scanner中,有着繁多的消息傳送,處理的消息的handler也有兩個。如何將消息傳送到指定的handler中去,從代碼上看挺清晰的,將message和指定的handler綁定。前面提到過,在android中有主線程和工作線程之分。Activity這類的main thread,還有自己創建的work thread,如果要更新UI,則需要再main thread中進行處理,可以通過handler來實現消息傳遞,將消息送到main thread的消息隊列去。
Barcode Scanner中有兩個Thread 和 兩個handler,在這四個類中通過加入這樣的語句判斷他們thread中得關系:
1 System.out.println(TAG + "The worker thread id = " + Thread.currentThread().getId()); //判斷線程ID
最后運行的結果:
1 01-12 02:41:12.594: I/System.out(655): CaptureActivity The main thread id = 1
2 01-12 02:41:14.605: I/System.out(655): CaptureActivityHandler The handler thread id = 1
3 01-12 02:41:12.946: I/System.out(655): DecodeThread The worker thread id = 13
4 01-12 02:41:13.094: I/System.out(655): DecodeHandler The handler thread id = 13
由此可見,這兩個handler都分別屬於他們的thread。但在創建這兩個handler時,有很大的差別。CaptureActivityHandler的創建只是簡簡單單的new了,沒有其他輔助。這就是main activity在創建時,系統默認為它創建一個looper,負責管理該線程的消息循環,取送消息等,不需要額外指定。但對於自己創建的Thread,系統默認是沒有為其創建looper的,需要自己為它創建消息循環。
先看下DecodeThread的代碼 : 去掉了跟理解線程不相關的代碼。
1 final class DecodeThread extends Thread {
2 public static String TAG = DecodeThread.class.getSimpleName();
3 private final CaptureActivity activity;
4 private Handler handler;
5 private final CountDownLatch handlerInitLatch;//到計數的鎖
6 DecodeThread(CaptureActivity activity,
7 Vector<BarcodeFormat> decodeFormats,
8 String characterSet,
9 ResultPointCallback resultPointCallback) {
10
11 this.activity = activity;
12 handlerInitLatch = new CountDownLatch(1);//從1開始到計數
13
14 }
15
16 Handler getHandler() {
17 try {
18 handlerInitLatch.await();//阻塞先等handler被初始化了才能返回結果。改計數鎖即等countdown-->0。
19 } catch (InterruptedException ie) {
20 // continue?
21 }
22 return handler;
23 }
24
25 @Override
26 public void run() {
27 Looper.prepare();
28 handler = new DecodeHandler(activity, hints);
29 handlerInitLatch.countDown();//啟動到計數,countdown-1 變成0;
30 System.out.println(TAG + "The worker thread id = " + Thread.currentThread().getId()); //判斷線程ID
31 Looper.loop();
32 }
前面定義了一個CountDownLatch類型變量,該變量為一個倒計數用的鎖。用法挺簡單,如代碼中,先new CountDownLatch(1),計數值為1, handlerInitLatch.countDown(), 開始倒數。handlerInitLatch.await() 若計數值沒有變為0,則一直阻塞。直到計數值為0后,才return handler,因此在調用getHandler時不會返回null的handler。
在創建DecodeThread線程的handler時,首先在線程中調用Looper.prepare()來創建消息隊列,再創建附於該線程的handler對象,最后調用Looper.loop()進入消息循環,這個這個loop()循環不會立馬返回,需要自己主動調用Looper.myLooper().quit()才會返回。這就是自己創建一個工作線程,為其分配一個消息隊列,消息循環的簡單迅速辦法。
前面說到camera的自動聚焦,只是隔斷時間定期的不停向CaptureActivityHandler發送自動聚焦請求,這就是一個消息傳送:
1 Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
2 autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
這樣創建了消息,隔斷時間發送。
關於Android的消息處理機制,這里有篇更好的文章,請點這里。還有這一篇介紹looper的。這兩篇說的更透徹,實用。這還發現一篇巨作。