首先撇清一個概念:Android的單線程設計是指每個應用程序的UI線程(主線程)是單線程的,即和用戶交互的界面是單線程的。但是,很顯然,用戶界面如果採用多線程處理效率會更高,Android爲什麼將UI線程限制爲單線程呢?這是爲了避免併發編程的複雜性,也是提高Android應用的健壯性的有效途徑。
但是,主線程是單線程的,並不等於Android不支持多線程,比如兩個Android應用程序之間的通訊。
Android中的Handler提供了線程之間通訊的簡便手段,如下圖所示:
Android的主線程(即UI線程)內部實現了消息隊列機制,即一個主線程會自動創建一個消息隊列(顯然也只有一個消息隊列),同時創建一個讀取消息隊列的Looper對象。主線程的所有用戶交互都通過消息隊列和Looper對象來實現:當有新的消息進入消息隊列時,Looper會及時讀取消息並調用相應的處理邏輯(更新用戶界面)。
當其他線程希望更新用戶界面時,可以通過Handler提供的sendMessage方法在消息隊列中放入一個消息(Message對象,可以攜帶大量信息),這樣Looper即可以及時獲取該消息,並調用Handler相應的handleMessage方法處理該消息,這樣就實現了線程之間的通訊。由此可以看出,Handler在線程通訊中起到了一個樞紐作用:Handler即負責和消息隊列打交道,也負責處理相應的消息,其他線程通過Handler和主線程通訊,就可以不需要考慮和主線程的競爭和同步問題,極大的簡化了線程的使用。
下面的示例代碼演示了一個每隔1秒顯示一條消息在主界面:
public class MainActivity extends Activity { private TextView label; private static int UPDATE = 1; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 1) { label.setText(String.valueOf(msg.obj)); super.handleMessage(msg); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); label = (TextView) findViewById(R.id.label); new Thread() { @Override public void run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Message msg = new Message(); msg.what = UPDATE; msg.obj = "當前循環變量:" + i; handler.sendMessage(msg); } } }.start(); } }
在上面的例子中,所謂的其他線程其實主線程的子線程,下面一個例子則演示了完全不同的兩個組件通過Handler實現的交互:一個Service每隔1秒產生一個隨機數,然後在主線程顯示出來:
ServiceDemo.java文件:
public class ServiceDemo extends Service { @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null; } private Thread workThread; @Override public void onCreate() { super.onCreate(); Toast.makeText(this, "(1) onCreate()", Toast.LENGTH_LONG).show(); workThread = new Thread(null,backgroudWork,"WorkThread"); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Toast.makeText(this, "(2) onStart()", Toast.LENGTH_SHORT).show(); if (!workThread.isAlive()){ workThread.start(); } } @Override public void onDestroy() { super.onDestroy(); Toast.makeText(this, "(3) onDestroy()", Toast.LENGTH_SHORT).show(); workThread.interrupt(); } private Runnable backgroudWork = new Runnable(){ @Override public void run() { try { while(!Thread.interrupted()){ double randomDouble = Math.random(); MainActivity.updateGUI(randomDouble); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } }; }
MainActivity.java文件:
public class MainActivity extends Activity { private static TextView label; private static int UPDATE = 1; protected static Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 1) { label.setText(String.valueOf(msg.obj)); super.handleMessage(msg); } } }; public static void updateGUI(double doubleRadom){ Message msg = new Message(); msg.what = UPDATE; msg.obj = "當前隨機數:" + doubleRadom; handler.sendMessage(msg); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); label = (TextView) findViewById(R.id.label); final Intent serviceIntent = new Intent(this, ServiceDemo.class); startService(serviceIntent); } }
可以看出,爲了實現在兩個組件間的通訊,主要的變化是:
- 將Handler對象定義爲static類型的,便於在其他組件訪問handler
- 在主線程中實現一個靜態的updateGUI方法,以便通過調用handler.sendMessage添加一個消息到消息隊列中
- 在Service的線程中,調用主線程的updateGUI並傳入適當的參數
Handler和消息循環機制的設計很巧妙,在WEB應用和企業級應用中是否可以借鑑呢?