深入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会更新状态。
/**
* 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);
}
}
}


猜你喜欢
- 前言;Apache common-pool对象池介绍:对象生命周期、Config详解、代码说明对象生命周期Config详解maxActive
- 如执行:"2|33|4".split("|")出来的结果是:""2334奇怪吧,
- 装饰器模式概述装饰器模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),属于结构型模式。它是指在不改
- 背景在接口请求过程中,传递json对象,springboot转换为实体VO对象后,所有属性都为null。post请求:后台接收请求:当时就懵
- JavaWeb登录界面登录失败在同一页面进行提示方法使用JSP 通过提交表单方式 判断账号密码是否正确 不正确则调用req.setAttri
- 使用前准备Build.gradle文件配置dependencies配置compile 'com.squareup.retrofit2
- static关键字static关键词与对象无关。static关键字主要修饰四个部分的内容这里我们主要介绍static修饰属性和修饰方法。1.
- 大二的时候做的课程设计,图片管理器,当时遇到图片很多的文件夹,加载顺序非常慢。虽然尝试用多个Thread加载图片,却无法保证图片按顺序加载。
- 前言之前因为项目需求,其中使用到了图片的单击显示取消,图片平移缩放功能,昨天突然想再加上图片的旋转功能,在网上看了很多相关的例子,可是没看到
- 本文实例为大家分享了C#线程倒计时器源码,供大家参考,具体内容如下using System;using System.Collections
- 1.创建简单的XML文件为了便于测试,我们首先创建控制台应用程序,项目命名为CreateXml,Program.cs代码如下:using S
- 1. 缓存、两级缓存1.1 内容说明Spring cache:主要包含spring cache定义的接口方法说明和注解中的属性说明sprin
- 前文传送门:Netty启动流程服务端channel初始化注册多路复用回到上一小节的代码:final ChannelFuture initAn
- 通过 SpringBoot 实现了表单下的文件上传,前后端分离情况下的文件上传。本案例不连接数据库,只做基本的文件上传操作。在 Spring
- 1、synchronized 方法与锁对象线程锁的是对象。1)A线程先持有 object 对象的 Lock 锁, B线程可以以异步的方式调用
- 本文实例讲述了Android编程实现将应用强制装到手机内存的方法。分享给大家供大家参考,具体如下:在Froyo(android 2.2,AP
- 有时候,我们需要把对象A的所有值复制给对象B(B = A),但是这样用等号给赋值你会发现,当B中的某个对象值改变时,同时也会修改到A中相应对
- 【前言】AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统
- 本文实例为大家分享了Java身份证号码校验工具类的具体代码,供大家参考,具体内容如下import java.text.ParseExcept
- 样例代码在讲 Flutter 的盒子模型前,先看看HTML 中的盒子模型。如下图所示,一个页面元素包括了与父级容器的外边距(margin),