編輯:關於android開發
Android建議我們不要在UI線程中執行耗時操作,因為這很容易導致ANR異常(在Android源碼中我們可以看到,UI如果對用戶的操作超過5秒無響應,就會報ANR異常)。因此,一些耗時操作都會在子線程中完成。當我們在子線程中獲取了數據,要將其顯示到UI中,如果沒有Handler,這將很難完成。因此,Android之所以提供Handler,就是為了解決子線程訪問UI的問題。
為什麼Android不允許在子線程中訪問UI呢?顯然這樣做不安全,多線程訪問UI是不安全的(學過操作系統的盆友應該都了解線程互斥,這裡我就不詳細介紹了)。有人就會說了,可以通過設置信號量來解決啊。這中方法不是不可以,因為這種方法會使訪問UI的邏輯變得復雜;其次這會降低UI的訪問效率。而使用Handler就比較簡單高效。Handler是同個Message來通訊的。
使用Handler時,需要重寫handleMessage方法,在handleMessage中接受新線程發來的Message,並做相應的處理。在新線程中則是通過Message來傳遞消息,Message中往往也攜帶著需要傳遞的數據以及消息的類型。還要強調一點,如果當前線程有Looper就不需要執行Looper.prepare(),如果沒有,就需要在新線程內執行Looper.prepare(),否則會報錯。具體使用代碼如下:
1 public class MainActivity extends AppCompatActivity { 2 private Handler mHandler=new Handler(){ 3 @Override 4 public void handleMessage(Message msg) { 5 switch (msg.what) 6 { 7 case 1: 8 //執行需要修改的UI操作 9 break; 10 default: 11 break; 12 } 13 } 14 }; 15 16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.activity_main); 20 21 new Thread(new Runnable() { 22 @Override 23 public void run() {//在新線程中執行耗時操作 24 25 //如果當前線程有Looper就不需要執行Looper.prepare(); 26 Looper.prepare(); 27 try { 28 Thread.sleep(1000);//睡眠1秒 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 33 //操作完成之後通過發送Message,來通知Handler進行UI操作 34 35 Message msg=new Message(); 36 msg.what=1; 37 38 /*這部分是偽代碼,value 是想通過Message傳遞的值 39 Bundle data=new Bundle(); 40 data.putSerializable("key",value); 41 msg.setData(data); 42 43 */ 44 45 //設置好數據後,發送消息 46 mHandler.sendMessage(msg); 47 } 48 }).start(); 49 } 50 51 }
當然,handler也可以在子線程中創建,代碼如下:
1 private TextView tv_test; 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.handler_test_layout); 7 8 tv_test= (TextView) findViewById(R.id.tv_test); 9 10 11 } 12 13 //button點擊的函數 14 public void click(View v) 15 { 16 new Thread(new Runnable() { 17 @Override 18 public void run() { 19 Looper.prepare(); 20 Handler handler=new Handler(Looper.getMainLooper()){ 21 @Override 22 public void handleMessage(Message msg) { 23 switch (msg.what) 24 { 25 case 1: 26 tv_test.setText("receive msg"); 27 } 28 } 29 }; 30 Message msg=new Message(); 31 msg.what=1; 32 handler.sendMessage(msg); 33 } 34 }).start(); 35 }
上面的代碼是,當點擊按鈕後,就會創建一個新的線程,在新線程中創建handler,並發送消息、接受消息。這裡需要注意的是,在新線程中創建handler需要使用Handler handler=new Handler(Looper.getMainLooper())這樣的寫法,Looper.getMainLooper()將主線程中的Looper傳過去,這樣handler才能訪問UI。運行效果如下:
Handler創建時會采用Looper來建立消息循環。所以,當前線程必須要有Looper。當Handler創建完成後,其內部的Looper以及MessageQueue既可以和Handler一起協同工作了。Handler通過sendMessage將消息發送給內部的MessageQueue,而MessageQueue會調用queue.enqueueMessage(msg, uptimeMillis)方法,它的源碼如下:
1 boolean enqueueMessage(Message msg, long when) { 2 if (msg.target == null) { 3 throw new IllegalArgumentException("Message must have a target."); 4 } 5 if (msg.isInUse()) { 6 throw new IllegalStateException(msg + " This message is already in use."); 7 } 8 9 synchronized (this) { 10 if (mQuitting) { 11 IllegalStateException e = new IllegalStateException( 12 msg.target + " sending message to a Handler on a dead thread"); 13 Log.w(TAG, e.getMessage(), e); 14 msg.recycle(); 15 return false; 16 } 17 18 msg.markInUse(); 19 msg.when = when; 20 Message p = mMessages; 21 boolean needWake; 22 if (p == null || when == 0 || when < p.when) { 23 // New head, wake up the event queue if blocked. 24 msg.next = p; 25 mMessages = msg; 26 needWake = mBlocked; 27 } else { 28 // Inserted within the middle of the queue. Usually we don't have to wake 29 // up the event queue unless there is a barrier at the head of the queue 30 // and the message is the earliest asynchronous message in the queue. 31 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 32 Message prev; 33 for (;;) { 34 prev = p; 35 p = p.next; 36 if (p == null || when < p.when) { 37 break; 38 } 39 if (needWake && p.isAsynchronous()) { 40 needWake = false; 41 } 42 } 43 msg.next = p; // invariant: p == prev.next 44 prev.next = msg; 45 } 46 47 // We can assume mPtr != 0 because mQuitting is false. 48 if (needWake) { 49 nativeWake(mPtr); 50 } 51 } 52 return true; 53 }
通過源碼,我們發現,queue.enqueueMessage(msg, uptimeMillis)將消息放入了MessageQueue裡。Looper則會一直處理MessageQueue中的消息。
System.exit(0)和System.exit(1)區別,system.exit區別1.參考文獻 http://hi.baidu.com/accpzhangbo
Android SharedPreference的使用,sharedpreference在《Android 在內部存儲讀寫文件》一文中,談到了登錄用戶名和密碼的方法,通過
Android防微信首頁左右滑動切換,android防微大家看到微信首頁切換效果有沒有覺得很炫,滑動切換,點擊底部bar瞬間切換,滑動切換漸變效果,線上效果圖: &nbs
Android端通過HttpURLConnection上傳文件到服務器 Android端通過HttpURLConnection上傳文件到服務器 一:實現原理 最近在做An