网站首页 > 技术文章 正文
1.Handler的使用
如图所示,需要做的操作有两步:
public class MainActivity extends AppCompatActivity {@Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); final Handler handler=new Handler(){ @Override
public void handleMessage(Message m){ //更新ui操作
}
}; new Thread(){//子线程开启
@Override
public void run(){ Message m=new Message();
handler.sendMessage(m);
}
};
}
}
在主线程中实例化一个Handler,并且重写HandleMessage方法用来处理子线程发送来的消息。
在子线程中,首先声明Message对象,并将Message使用主线程中实例化的Handler的sendMessage(Message)方法发送给主线程。
2.Handler的机制
在使用sendMessage和handleMessage之前是有很多已经隐藏的封装好的过程的,现在一一讲解。
2.1 被消息传递的线程A准备
首先要在被消息传递的线程中创建Looper,因为Handler创建的时候要绑定当前线程的Looper,使用Looper.prepare()建立。
Looper.prepare()
public static void prepare() {prepare(true);
}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
总的来说,prepare干了两件事:创建MessageQueue和绑定当前线程。
然后使用Looper.loop()让Looper开始循环访问MessageQueue。
Looper.loop();
public static void loop() { // 拿到与当前线程关联的looper对象final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
} final MessageQueue queue = me.mQueue;
....................................... for (;;) {
Message msg = queue.next(); // might block
...................................
msg.target.dispatchMessage(msg);
..........................................
msg.recycleUnchecked();// 回收Message对象
}
}
总的来说,loop干了三件事:
1.拿到当前线程的looper。
2.根据looper拿到MessageQueue。
3.使用for循环对MessageQueu进行无限循环询问。
在for循环中也主要干了三件事:
1.从消息队列中取消息,如果没有就阻塞。
2.调用
msg.target.dispatchMessage(msg)方法对Message进行处理。3.处理完成后调用msg.recycleUnchecked()回收资源。
创建Handler,作为线程间通信的工具。
public Handler(Looper looper, Callback callback, boolean async){mLooper = looper;
// 与这个Handler关联的Looper中的消息队列
mQueue = looper.mQueue;
...............
}
创建Handler时将当前线程的Looper和Looper中的MessageQueue取到。
2.2 传递消息的线程B准备
创建Message时,使用Message.obtain()的效果大于New Message(),因为享元模式,维护了一个大小为50的Message对象池,比重复去创建Message更加高效。
Handler.sendMessage(Message)方法最终会调用sendMessageAtTime方法。
public boolean sendMessageAtTime(Message Message, long uptimeMillis) { // 1. 获取Hndler中的消息队列MessageQueue queue = mQueue; if (queue == null) {
RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e); return false;
} // 2. 将要发送消息和时间入队
return enqueueMessage(queue, Message, uptimeMillis);
}
总的来说,做了两件事:
1.获取handler中的MessageQueue。
2.调用enqueueMessage方法。
enqueueMessage()方法代码如下:
private boolean enqueueMessage(MessageQueue queue, Message Message, long uptimeMillis) { // 1. 将Handler赋值给Message的targetMessage.target = this; if (mAsynchronous) {
Message.setAsynchronous(true);
} // 2. 将Message加入到消息队列中(还有Handler)
return queue.enqueueMessage(Message, uptimeMillis);
}
总的来说,做了两件事:
1.将handler赋值给Message.target。
2.并且将Message放入MessageQueue中。
2.3 线程间通信
线程B的sendMessage流程执行完毕,至于线程A的Looper和Handler都已经创建好了,并且开始处理MessageQueue中的Message,还记得之前for循环中
public void dispatchMessage(Message msg) {
Message.target.dispatchMessage()方法吗,其实这里的Message.target就是handler,而且dispatchMessage()方法的源码如下:..........
handleMessage(msg);
}
在其中调用了handleMessage()方法,并且handleMessage()方法是个空方法,没有内容。所以我们只用重写这个方法就可以对线程B返回的消息进行处理了。
3.一些问题
为什么Activity主线程没有执行Looper.prepare()和Looper.loop()?
因为在ActivityThread的main方法中已经执行了这两个方法。
一个线程可以拥有几个Looper?几个Handler?
一个线程只能拥有一个Looper,无数个Handler。
如何从主线程发送消息给子线程?
在子线程中先建立Looper,然后再建立Handler,依靠sendMessage和handleMessage即可。
文/breakingsword(简书作者)
原文链接:
http://www.jianshu.com/p/8862ab82d0f4
猜你喜欢
- 2025-09-18 GPU集群扩展:Ray Serve与Celery的技术选型与应用场景分析
- 2025-09-18 【不背八股】2.操作系统-进程、线程、协程的基本理解
- 2025-09-18 Spring Boot 3.x 日志配置与 Logback 集成指南
- 2025-09-18 解锁C++异步之力:高效并发编程指南
- 2025-09-18 Flutter框架分析(八)-Platform Channel
- 2025-09-18 原来你是这样打印日志的,怪不得天天背锅……
- 2025-09-18 .NET Aspire 9.4 发布了 CLI GA、交互式仪表板和高级部署功能
- 2025-09-18 27.8K!一把梭 LLM:LiteLLM 带你用一套接口召唤 100+ 大模型
- 2025-09-18 Rust异步编程神器:用Tokio轻松创建定时任务
- 2025-09-18 C++ 回调革命:C 风格适配 Lambda 捕获指南
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)