深入Android Handler与线程间通信ITC的详解

时间:2021-12-09 16:02:17 

在《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会更新状态。

深入Android Handler与线程间通信ITC的详解


/**
 * 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,Handler,线程间通信ITC
0
投稿

猜你喜欢

  • Java如何基于IO流实现同一文件读写操作

    2023-07-30 15:06:38
  • C# WPF 通过委托实现多窗口间的传值的方法

    2022-03-27 19:02:23
  • Android WebView或手机浏览器打开连接问题解决办法总结

    2022-09-15 18:46:22
  • IDEA部署JavaWeb项目到Tomcat服务器的方法

    2023-11-02 23:21:22
  • java实现简单计算器功能

    2021-06-17 11:21:23
  • java Springboot实现多文件上传功能

    2023-11-09 04:31:32
  • Android 百度地图定位实现仿钉钉签到打卡功能的完整代码

    2022-09-05 17:32:46
  • android自定义ImageView仿图片上传示例

    2023-08-11 20:34:08
  • 简单讲解java中throws与throw的区别

    2022-06-01 05:16:55
  • idea中同一SpringBoot项目多端口启动

    2023-05-09 06:39:27
  • 详解使用JRebel插件实现SpringBoot应用代码热加载

    2021-06-15 10:39:00
  • Java实现验证码的产生和验证

    2022-10-30 22:41:39
  • Android实现Banner界面广告图片循环轮播(包括实现手动滑动循环)

    2022-02-06 17:05:35
  • flutter实现appbar下选项卡切换

    2023-06-21 13:35:24
  • idea向System.getenv()添加系统环境变量的操作

    2022-11-13 19:35:51
  • java 实现定时的方法及实例代码

    2023-03-31 22:48:41
  • 详解Java豆瓣电影爬虫——小爬虫成长记(附源码)

    2023-10-29 17:45:17
  • 浅析C#中静态方法和非静态方法的区别

    2023-04-07 07:55:15
  • Android Gradle模块依赖替换使用技巧

    2021-07-03 20:22:53
  • Java 面试题基础知识集锦

    2022-05-25 00:01:58
  • asp之家 软件编程 m.aspxhome.com