深入Android Handler與執行緒間通訊ITC的詳解

深入Android Handler與執行緒間通訊ITC的詳解

在《Android Handler之訊息迴圈的深入解析》中談到了Handler是用於操作執行緒內部的訊息佇列,所以Handler可以用來執行緒間通訊ITC,這種方式更加安全和高效,可以大大減少同步的煩惱,甚至都可以不用syncrhonized。
執行緒間通訊ITC
正常情況下函式呼叫棧都會生存在同一個執行緒內,想要把執行邏輯交換到其他執行緒可以新建一個Thread,然後start()。另外一種方法就是用ITC,也即用訊息佇列來實現,執行緒需要把執行邏輯交到其他執行緒時就向另外的執行緒的訊息佇列傳送一個訊息,傳送訊息後函式就此結束返回,呼叫棧也停止。當訊息佇列中有了訊息時,執行緒會被喚醒來執行處理訊息,從而把執行邏輯從一個執行緒轉到另外一個執行緒。這就實現了執行緒間的通訊ITC,與進行間通訊IPC有十分類似的思想。

通常的做法都是,在主執行緒建立一個Handler,然後在新建執行緒中使用此Handler與主執行緒通訊。因為主執行緒的訊息佇列已經建好,所以直接建立Handler即可,新建的執行緒就可以直接使用。
有些情況,需要在多執行緒之間進行通訊,這就要為每個執行緒都建立MessageQueue和Handler,只要執行緒能訪問其他執行緒的Handler就可以與之通訊。

要正確的建立Handler,因為Handler要與執行緒繫結,所以在初始化Handler的時候就要注意:
如果給Handler指定Looper物件new Handler(Looper),那麼此Handler便繫結到Looper物件所在的執行緒中,Handler的訊息處理回撥會在那個執行緒中執行。
如果建立執行緒時不指定Looper物件,那麼此Handler繫結到建立此Handler的執行緒內,訊息回撥處理會在那個執行緒中執行,所以像下面的例子,如果這樣寫:
複製程式碼 程式碼如下:
private class CookServer extends Thread {
       private Handler mHandler = new Handler() {
               public void handleMessage(Message msg) {
                     ….
                }
        };

那麼,此mHandler會與建立此CookerServer的執行緒繫結,handleMessage也會執行於其中。顯然,如果是主執行緒呼叫new CookServer(),那麼mHandler其實是執行在主執行緒中的。正確的寫法應該是:
複製程式碼 程式碼如下:
private class CookServer extends Thread {
       public void run() {
             Looper.prepare();
                 // or new Handler(Looper.myLooper())
                 private Handler mHandler = new Handler() {
                       public void handleMessage(Message msg) {
                     ….
                }
        };

HandlerThread
如果要在一個執行緒中使用訊息佇列和Handler,Android API中已經有封裝好了的一個類HandlerThread,這個類已經做好了Looper的初始化工作,你需要做的就是重寫其onLooperPrepared()方法,在其中建立Handler:
複製程式碼 程式碼如下:
private class DeliverServer extends HandlerThread {
      private Handler mHandler;
      public DeliverServer(String name) {
           super(name);
      }
      @Override
      public void onLooperPrepared() {
            mHandler = new Handler(getLooper()) {
                    public void handleMessage(Message msg) {
                        …..
                    }
             };
       }
}

例項
此例項模擬了一個網路訂餐系統,客戶點選“Submit order”來產生一個定單,主執行緒中負責收集定單,然後交由CookServer來製作,CookServer在製作完成後會交由DeliverServer來把食物運送到客戶,至此一個定單完成,同時CookServer和DeliverServer會更新狀態。


複製程式碼 程式碼如下:
/**
 * How to attach an Handler to a Thread:
 * If you specify Looper object to Handler, i.e. new Handler(Looper), then the handler is attached to the thread owning
 * the Looper object, in which handleMessage() is executed.
 * If you do not specify the Looper object, then the handler is attached to the thread calling new Handler(), in which
 * handleMessage() is executed.
 * In this example, for class CookServer or DeliverServer, if you write this way:
 *     private class CookServer extends Thread {
  private Handler mHandler;
  private Looper mLooper;

  public CookServer() {
   mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
     ….
    }
       start();
  }
 * then mHandler is attached to thread calling new CookServer(), which is the main thread, so mHandler.handleMessage() will
 * be executed in main thread.
 * To attach mHandler to its own thread, you must put it in run(), or after mLooper is created. For our example, providing
 * mLooper or not won’t matter, because new Handler() is called in run(), which is in a new thread.
 */
public class HandlerITCDemo extends ListActivity {
    private static final int COOKING_STARTED = 1;
    private static final int COOKING_DONE = 2;
    private static final int DELIVERING_STARTED = 3;
    private static final int ORDER_DONE = 4;

    private ListView mListView;
    private static final String[] mFoods = new String[] {
 “Cubake”,
 “Donut”,
 “Eclaire”,
 “Gingerbread”,
 “Honeycomb”,
 “Ice Cream Sanwitch”,
 “Jelly Bean”,
    };
    private ArrayList<String> mOrderList;
    private TextView mGeneralStatus;
    private Button mSubmitOrder;
    private static Random mRandomer = new Random(47);
    private int mOrderCount;
    private int mCookingCount;
    private int mDeliveringCount;
    private int mDoneCount;

    private Handler mMainHandler = new Handler() {
 @Override
 public void handleMessage(Message msg) {
     switch (msg.what) {
     case COOKING_STARTED:
  mCookingCount ;
  break;
     case COOKING_DONE:
  mCookingCount–;
  break;
     case DELIVERING_STARTED:
  mDeliveringCount ;
  break;
     case ORDER_DONE:
  mDeliveringCount–;
  mDoneCount ;
     default:
  break;
     }
     mGeneralStatus.setText(makeStatusLabel());
 }
    };

    private CookServer mCookServer;
    private DeliverServer mDeliverServer;

    @Override
    protected void onDestroy() {
 super.onDestroy();
 if (mCookServer != null) {
     mCookServer.exit();
     mCookServer = null;
 }
 if (mDeliverServer != null) {
     mDeliverServer.exit();
     mDeliverServer = null;
 }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 mListView = getListView();
 mOrderList = new ArrayList<String>();
 mGeneralStatus = new TextView(getApplication());
 mGeneralStatus.setText(makeStatusLabel());
 mSubmitOrder = new Button(getApplication());
 mSubmitOrder.setText(“Submit order”);
 mSubmitOrder.setOnClickListener(new View.OnClickListener() {
     public void onClick(View v) {
  String order = mFoods[mRandomer.nextInt(mFoods.length)];
  mOrderList.add(order);
  mOrderCount = mOrderList.size();
  mGeneralStatus.setText(makeStatusLabel());
  setAdapter();
  mCookServer.cook(order);
     }
 });
 mListView.addHeaderView(mGeneralStatus);
 mListView.addFooterView(mSubmitOrder);
 setAdapter();
 mCookServer = new CookServer();
 mDeliverServer = new DeliverServer(“deliver server”);
    }

    private String makeStatusLabel() {
 StringBuilder sb = new StringBuilder();
 sb.append(“Total: “);
 sb.append(mOrderCount);
 sb.append(”    Cooking: “);
 sb.append(mCookingCount);
 sb.append(”    Delivering: “);
 sb.append(mDeliveringCount);
 sb.append(”    Done: “);
 sb.append(mDoneCount);
 return sb.toString();
    }

    private void setAdapter() {
 final ListAdapter adapter = new ArrayAdapter<String>(getApplication(), android.R.layout.simple_list_item_1, mOrderList);
 setListAdapter(adapter);
    }

    private class CookServer extends Thread {
 private Handler mHandler;
 private Looper mLooper;

 public CookServer() {
     start();
 }

 @Override
 public void run() {
     Looper.prepare();
     mLooper = Looper.myLooper();
     mHandler = new Handler(mLooper, new Handler.Callback() {
  public boolean handleMessage(Message msg) {
      new Cooker((String) msg.obj);
      return true;
  }
     });
     Looper.loop();
 }

 public void cook(String order) {
     if (mLooper == null || mHandler == null) {
  return;
     }
     Message msg = Message.obtain();
     msg.obj = order;
     mHandler.sendMessage(msg);
 }

 public void exit() {
     if (mLooper != null) {
  mLooper.quit();
  mHandler = null;
  mLooper = null;
     }
 }
    }

    private class Cooker extends Thread {
 private String order;
 public Cooker(String order) {
     this.order = order;
     start();
 }

 @Override
 public void run() {
            mMainHandler.sendEmptyMessage(COOKING_STARTED);
            SystemClock.sleep(mRandomer.nextInt(50000));
            mDeliverServer.deliver(order);
            mMainHandler.sendEmptyMessage(COOKING_DONE);
 }
    }

    private class DeliverServer extends HandlerThread {
 private Handler mHandler;

 public DeliverServer(String name) {
     super(name);
     start();
 }

 @Override
 protected void onLooperPrepared() {
     super.onLooperPrepared();
     mHandler = new Handler(getLooper(), new Handler.Callback() {
  public boolean handleMessage(Message msg) {
      new Deliver((String) msg.obj);
      return true;
  }
     });
 }
 public void deliver(String order) {
     if (mHandler == null || getLooper() == null) {
  return;
     }
     Message msg = Message.obtain();
     msg.obj = order;
     mHandler.sendMessage(msg);
 }

 public void exit() {
     quit();
     mHandler = null;
 }
    }

    private class Deliver extends Thread {
 private String order;
 public Deliver(String order) {
     this.order = order;
     start();
 }

 @Override
 public void run() {
     mMainHandler.sendEmptyMessage(DELIVERING_STARTED);
     SystemClock.sleep(mRandomer.nextInt(50000));
     mMainHandler.sendEmptyMessage(ORDER_DONE);
 }
    }
}

您可能感興趣的文章:

Android程序通訊之Messenger和AIDL使用詳解Android IPC機制利用Messenger實現跨程序通訊深入理解Android中的Handler非同步通訊機制Android Handler主執行緒和一般執行緒通訊的應用分析Android 程序間通訊實現原理分析Android Socket通訊詳解Android程式設計之客戶端通過socket與伺服器通訊的方法Android微控制器與藍芽模組通訊例項程式碼Android之網路通訊案例分析Android網路程式設計之UDP通訊模型例項Android開發使用Messenger及Handler進行通訊的方法示例