最近在做毕设,关于android的,其中觉得android的消息机制很有意思,这里就写下自己的想法
和Windows一样android也是消息驱动的。Android通过Handler和looper实现消息循环机制。
一、Handler的创建
每个Handler都会和一个线程和线程的message queue关联起来,此时你可以传递messages 和 runnable对象到message queue中。后面可以从message queue中拿出该对象并且执行。
上面提到了Handler, Message queue这两个对象,那他们之间是怎么交互的呢?
这中间就涉及到了looper的概念。我们首先来看下handler和looper,message queue??????? 这两个类的关系
Handler提供了很多的构造函数,
- ? ?public Handler() {
- ? ? ? ? mLooper = Looper.myLooper();
- ? ? ? ? if (mLooper == null) {
- ? ? ? ? ? ? throw new RuntimeException(
- ? ? ? ? ? ? ? ? "Can't create handler inside thread that has not called Looper.prepare()");
- ? ? ? ? }
- ? ? ? ? mQueue = mLooper.mQueue;
- ? ? ? ? mCallback = null;
- }
- ?
- ? ? public Handler(Looper looper) {
- ? ? ? ? mLooper = looper;
- ? ? ? ? mQueue = looper.mQueue;
- ? ? ? ? mCallback = null;
- ? ? }
从这两个构造函数中,我们发现当为提供looper的时候,android会通过Looper.myLooper()获得当前线程的looper。那通过myLooper()获得的当前线程的looper 可能会为 null 吗? 答案是肯定的,若在主线程下启动一个新的线程,那这个子线程是不会建立自己的Message Queue的。Android通过ActivityThread来启动一个activity,在activity的main()方法中有2句关键的代码
Looper. prepareMainLooper()? //具体查看源代码
Looper.loop()
因为创建子线程的时候并没有运行前面这两句话,所以子线程中调用Looper.myLooper()返回的是null。所以就造成了在子线程中创default handler是错误的。而要在子线程中有自己的looper就需要如下写,
- class LooperThread extends Thread {
- ? ? ? ? public Handler mHandler;
- ? ? ? ?
- ? ? ? ? public void run() {
- ? ? ? ? ? ?Looper.prepare();
- ? ? ? ? ? ?
- ? ? ? ? ? ? mHandler = new Handler() {
- ? ? ? ? ? ? ? ? public void handleMessage(Message msg) {
- ? ? ? ? ? ? ? ? ? ? // process incoming messages here
- ? ? ? ? ? ? ? ? }
- ? ? ? ? ? ? };
- ? ? ? ? ? ?
- ? ? ? ? ? Looper.loop();
- ? ? ? }
Looper源代码中有一个关键的方法prepare(),那他具体做什么事呢,代码如下
- ? public static final void prepare() {
- ? ? ? ? if (sThreadLocal.get() != null) {
- ? ? ? ? ? ? throw new RuntimeException("Only one Looper may be created per thread");
- ? ? ? ? }
- ? ? ? ? sThreadLocal.set(new Looper());
- }
通过ThreadLocal实现了一个线程只有一个Looper
二、Handler的消息发送
总感觉Handler就像一个交警一样,负责message的创建,获得,处理。Handler的sendMessage()方法最终其实就是调用了queue.enqueueMessage(msg, uptimeMillis);把消息放入message queue中。Looper通过Looper.loop()检测到有消息并将消息广播后,Handler又负责接收到此消息并调用handleMessage进行处理接下来的事情。Message的创建一般都是通过如下代码建立的,把Handler传进去
- ? ? ?public final Message obtainMessage()
- ? ? ? ? ? ? ? ?{
- ? ? ? ? ? ? ? ? ?return Message.obtain(this);
- ? ? ? ?}
三、Message
Message在里面是一个链表的结构。在这个方法中,会首先去MessagePool(消息回收站)去找Message,如果有被回收的Message,则会将这个Message取出来进行再利用。如果没有被回收的,这时系统才会真正new一个Message对象出来当然MessagePool不是无限大的,它最多只能保存十条回收的消息,多出来的就直接删除了
更新UI例子
- public class ReviewList extends ListActivity {
- ?
- ? ? private static final String CLASSTAG = ReviewList.class.getSimpleName();
- ? ? private static final int MENU_CHANGE_CRITERIA = Menu.FIRST + 1;
- ? ? private static final int MENU_GET_NEXT_PAGE = Menu.FIRST;
- ? ? private static final int NUM_RESULTS_PER_PAGE = 8;
- ? ?
- ? ? private TextView empty; ? ?
- ? ? private ProgressDialog progressDialog;
- ? ? private ReviewAdapter reviewAdapter;
- ? ? private List<Review> reviews;
- ? ?
- ? ? private final Handler handler = new Handler() {
- ? ? ? ? @Override
- ? ? ? ? public void handleMessage(final Message msg) {
- ? ? ? ? ? ? Log.v(Constants.LOGTAG, " " + ReviewList.CLASSTAG + " worker thread done, setup ReviewAdapter");
- ? ? ? ? ? ? progressDialog.dismiss();
- ? ? ? ? ? ? if ((reviews == null) || (reviews.size() == 0)) {
- ? ? ? ? ? ? ? ? empty.setText("No Data");
- ? ? ? ? ? ? } else {
- ? ? ? ? ? ? ? ? reviewAdapter = new ReviewAdapter(ReviewList.this, reviews);
- ? ? ? ? ? ? ? ? setListAdapter(reviewAdapter);
- ? ? ? ? ? ? }
- ? ? ? ? }
- ? ? }; ?
- ?
- ? ? @Override
- ? ? public void onCreate(Bundle savedInstanceState) {
- ? ? ? ? super.onCreate(savedInstanceState);
- ? ? ? ? Log.v(Constants.LOGTAG, " " + ReviewList.CLASSTAG + " onCreate");
- ?
- ? ? ? ? // NOTE* This Activity MUST contain a ListView named "@android:id/list"
- ? ? ? ? // (or "list" in code) in order to be customized
- ? ? ? ? // http://code.google.com/android/reference/android/app/ListActivity.html
- ? ? ? ? this.setContentView(R.layout.review_list);
- ?
- ? ? ? ? this.empty = (TextView) findViewById(R.id.empty);
- ?
- ? ? ? ? // set list properties
- ? ? ? ? final ListView listView = getListView();
- ? ? ? ? listView.setItemsCanFocus(false);
- ? ? ? ? listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- ? ? ? ? listView.setEmptyView(this.empty);
- ? ? } ?
- ?
- ? ? @Override
- ? ? protected void onResume() {
- ? ? ? ? super.onResume();
- ? ? ? ? Log.v(Constants.LOGTAG, " " + ReviewList.CLASSTAG + " onResume");
- ? ? ? ? // get the current review criteria from the Application (global state placed there)
- ? ? ? ? RestaurantFinderApplication application = (RestaurantFinderApplication) getApplication();
- ? ? ? ? String criteriaCuisine = application.getReviewCriteriaCuisine();
- ? ? ? ? String criteriaLocation = application.getReviewCriteriaLocation();
- ?
- ? ? ? ? // get start from, an int, from extras
- ? ? ? ? int startFrom = getIntent().getIntExtra(Constants.STARTFROM_EXTRA, 1);
- ?
- ? ? ? ? loadReviews(criteriaLocation, criteriaCuisine, startFrom);
- ? ? } ? ?
- ? ?
- ? ? @Override
- ? ? public boolean onCreateOptionsMenu(Menu menu) {
- ? ? ? ? super.onCreateOptionsMenu(menu);
- ? ? ? ? menu.add(0, ReviewList.MENU_GET_NEXT_PAGE, 0, R.string.menu_get_next_page).setIcon(
- ? ? ? ? ? ? android.R.drawable.ic_menu_more);
- ? ? ? ? menu.add(0, ReviewList.MENU_CHANGE_CRITERIA, 0, R.string.menu_change_criteria).setIcon(
- ? ? ? ? ? ? android.R.drawable.ic_menu_edit);
- ? ? ? ? return true;
- ? ? } ? ?
- ?
- ? ? @Override
- ? ? public boolean onMenuItemSelected(int featureId, MenuItem item) {
- ? ? ? ? Intent intent = null;
- ? ? ? ? switch (item.getItemId()) {
- ? ? ? ? ? ? case MENU_GET_NEXT_PAGE:
- ? ? ? ? ? ? ? ? // increment the startFrom value and call this Activity again
- ? ? ? ? ? ? ? ? intent = new Intent(Constants.INTENT_ACTION_VIEW_LIST);
- ? ? ? ? ? ? ? ? intent.putExtra(Constants.STARTFROM_EXTRA, getIntent().getIntExtra(Constants.STARTFROM_EXTRA, 1)
- ? ? ? ? ? ? ? ? ? ? + ReviewList.NUM_RESULTS_PER_PAGE);
- ? ? ? ? ? ? ? ? startActivity(intent);
- ? ? ? ? ? ? ? ? return true;
- ? ? ? ? ? ? case MENU_CHANGE_CRITERIA:
- ? ? ? ? ? ? ? ? intent = new Intent(this, ReviewCriteria.class);
- ? ? ? ? ? ? ? ? startActivity(intent);
- ? ? ? ? ? ? ? ? return true;
- ? ? ? ? }
- ? ? ? ? return super.onMenuItemSelected(featureId, item);
- ? ? }
- ? ?
- ? ? @Override
- ? ? protected void onListItemClick(ListView l, View v, int position, long id) {
- ? ? ? ? // set the current review to the Application (global state placed there)
- ? ? ? ? RestaurantFinderApplication application = (RestaurantFinderApplication) getApplication();
- ? ? ? ? application.setCurrentReview(this.reviews.get(position));
- ?
- ? ? ? ? // startFrom page is not stored in application, for example purposes it's a simple "extra"
- ? ? ? ? Intent intent = new Intent(Constants.INTENT_ACTION_VIEW_DETAIL);
- ? ? ? ? intent.putExtra(Constants.STARTFROM_EXTRA, getIntent().getIntExtra(Constants.STARTFROM_EXTRA, 1));
- ? ? ? ? startActivity(intent);
- ? ? } ? ?
- ? ?
- ? ? private void loadReviews(String location, String cuisine, int startFrom) {
- ?
- ? ? ? ? Log.v(Constants.LOGTAG, " " + ReviewList.CLASSTAG + " loadReviews");
- ?
- ? ? ? ? final ReviewFetcher rf = new ReviewFetcher(location, cuisine, "ALL", startFrom,
- ? ? ? ? ? ? ReviewList.NUM_RESULTS_PER_PAGE);
- ?
- ? ? ? ? this.progressDialog = ProgressDialog.show(this, " Working…", " Retrieving reviews", true, false);
- ?
- ? ? ? ? // get reviews in a separate thread for ProgressDialog/Handler
- ? ? ? ? // when complete send "empty" message to handler
- ? ? ? ? new Thread() {
- ? ? ? ? ? ? @Override
- ? ? ? ? ? ? public void run() {
- ? ? ? ? ? ? ? ? reviews = rf.getReviews();
- ? ? ? ? ? ? ? ? handler.sendEmptyMessage(0);
- ? ? ? ? ? ? }
- ? ? ? ? }.start();
- ? ? }
- }
在android里,新产生一个线程并不会自动建立其Message Loop。Android里并没有Global 的Message Queue,所以不同的APK里不能透过Message Queue来交换信息