前言:之前看了网上很多关于Android异步消息处理机制的文章,对于这块知识从一般性的应用上升到了内部机制的理解,受益匪浅。本着“看过没总结过等于没收获过”的原则,也对Android异步消息处理这块做个总结,内容如有错误之处还望指正。
Android异步消息处理概述
- No response to an input event (such as key press or screen touch events) within 5 seconds.
- A
BroadcastReceiver
hasn't finished executing within 10 seconds.如果输入事件(点击或触屏)在5秒之内没有响应,BroadcastReceiver在10秒之内没有执行完毕,应用程序就会报ANR错误,用户只能选择等待或强制关闭,这种用户体验是非常差的。主线程(main thread/UI thread),它有一个消息队列(message queue),如果屏幕上发生了点击/触屏事件就会把它转化为一个消息(message)放到消息队列里,由looper不断地从message queue里取出消息派送给相应的handler进行处理。而如果某个消息执行时间非常耗时,就会阻碍其他消息的处理,如果该条消息在5秒之内没有得到响应的话,就会报ANR。
避免ANR的方法是创建子线程,由子线程来完成耗时任务。例如点击某条新闻查看新闻详情这一事件,它的实现过程是用户点击了某条新闻,这个点击事件会通过网络请求去获取服务器端的手机接口数据(耗时操作),获取到的新闻详情的数据内容会显示到新闻详情页面中(更新用户界面)。由子线程完成耗时任务,再由子线程处理返回结果来更新界面,这种做法是错误的,因为应用只允许在主线程里对UI做修改,子线程没有权利对用户界面做任何修改。因此子线程处理完耗时操作,其获取到修改UI的内容需要在主线程里做处理。
之前已经提到创建子线程处理耗时任务,那么怎样将子线程获取到的数据放到主线程中处理呢?这里就需要Handler+Message来做主线程和子线程之间的沟通。整个的异步消息处理的步骤如下:
1. 在main thread里创建worker thread来异步执行耗时任务
2.在main thread里创建Handler,并让worker thread持有handler的引用
3.将worker thread执行结束后,创建Message,将获取到的数据结果存放到message中
4.由worker thread持有的的handler引用将message发送出去
5.main thread中的handler接受到message并处理消息,UI更新
创建子线程
1. 创建线程的两种方式
一、继承(extends)Thread类(适用于单继承)二、实现(implements)Runnable接口(当一个类已经继承某个类,就只能用Runnable来创建线程类)
通过run()方法来执行代码,通过调用start()方法启动线程。
实现Runnable接口创建线程:/** * 点击按钮,创建子线程 */ private void excuteLongTimeOperation() { Thread workerThread = new Thread(new MyNewThread()); workerThread.start(); } class MyNewThread implements Runnable{ @Override public void run() { //执行耗时操作 ThreadUtil.logThreadSignature(); } }
通过继承Thread类来创建线程:/** * 点击按钮,创建子线程 */ private void excuteLongTimeOperation() { Thread workerThread = new MyNewThread(); workerThread.start(); } public class MyNewThread extends Thread{ @Override public void run() { super.run(); } }
可以简化为匿名内部类,更加简洁:/** * 点击按钮,创建子线程 */ private void excuteLongTimeOperation() { new Thread(){ @Override public void run() { super.run(); //执行耗时操作 } }.start(); }
在以下情况下可以使用匿名内部类:1.只用到类的一个实例2.类在定以后马上用到3.类非常小