Android异步消息处理机制完全解析-Handler详解
参考资料
- 官方介绍文档
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解
- 慕课网课程-Android面试常客Handler详解
如果在非UI线程中更新UI会出现问题吗?
实践:
public class MainActivity extends AppCompatActivity { @BindView(R.id.id_tv) TextView idTv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); new Thread() { @Override public void run() { try { Thread.sleep(1000*5); idTv.setText("Javen205测试非UI线程更新UI会出现什么异常呢?"); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }}
我们运行项目就会以下异常
非UI线程中更新UI出现为了更直观的看到报错原因,我们找到源码ViewRootImpl的checkThread方法,看它做了些什么。
ViewRootImpl.checkThread()但是Android为什么要这样搞呢?
我们仔细看这句话,只有创建了View的线程才能对这个View进行操作。而我们一般View都是为了显式在UI上的。Android正是为了防止我们在非UI线程去操作这些UI上的控件,才加了限制的。因为UI体验对用户来说是最直观的,如果谁都有权限去操作一下,那UI要么很乱,要么控制很复杂。
竟然Android是不允许我们在非UI线程中去执行更新UI,那我们要怎么解决这个问题呢?那我们就要使用Android 提供的Hander机制去更新UI了
一、什么是Handler
Handler是Android提供的用来更新UI的一套机制,也是一套消息处理机制,我们可以通过它发送消息,也可以通过它处理消息。
二、为什么要使用Handler
Android在设计的时候,就封装了一套消息创建、传递、处理机制,如果不遵循这样的机制就没有办法更新UI信息,就会抛出异常。
三、Handler怎么用呢?
- 在非UI线程借助Handler.post(Runnable)更新UI
首先在Activity中实例化一个Hander
Handler handler = new Handler();
然后在子线程中调用Handler.post(Runnable)更新UI
详细的代码如下:
public class MainActivity extends AppCompatActivity { @BindView(R.id.id_tv) TextView idTv; Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); new Thread() { @Override public void run() { try { Thread.sleep(1000*5);// idTv.setText("Javen205测试非UI线程更新UI会出现什么异常呢?"); handler.post(new Runnable() { @Override public void run() { idTv.setText("Javen205测试非UI线程更新UI会出现什么异常呢?"); } }); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }}
- 在非UI线程借助Handler.postDelayed(Runnable, DelayTime)定时执行相关动作
栗子:实现三张图片自动切换
实现三张图片自动切换 private int images[]= {R.drawable.image4,R.drawable.image6,R.drawable.image7}; private int index=0;Handler handler = new Handler();Thread myThread = new Thread(){ @Override public void run() { index++; index = index%3; System.out.println(index); idImg.setImageResource(images[index]); handler.postDelayed(myThread,1000); } };handler.postDelayed(myThread,1000);
要是我们想停止图片的切换,那要如何操作呢?
- Handler移除一个消息
handler.removeCallbacks(myThread);
- 自定义Handler和Message
自定义Hander(customHander)
Handler customHander = new Handler() { @Override public void handleMessage(Message msg) { idTv.setText("msg.arg1>"+msg.arg1+ "\nmsg.arg2>" +msg.arg2 +"\nmsg.obj>"+((Dog)msg.obj).toString()); } };
实体类
package com.javen205.entity;import java.io.Serializable;public class Dog implements Serializable{ private String name; private int age; public Dog() { } public Dog(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + '}'; }}
使用只定义Hander(customHander)发送消息
Dog dog=new Dog("萨摩耶",1);Message message = new Message(); message.arg1 = 1; message.arg2 = 2; message.obj = dog; customHander.sendMessage(message);
复用Message
Dog dog=new Dog("萨摩耶",1);// Message message = new Message(); Message message= customHander.obtainMessage(); message.arg1 = 1; message.arg2 = 2; message.obj = dog; customHander.sendMessage(message);
原理:obtainMessage()方法做了哪些操作呢?
/** * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). * If you don't want that facility, just call Message.obtain() instead. */ public final Message obtainMessage() { return Message.obtain(this);//this 就是Hander本身 }
我们看看Message.obtain(Handler h) 又做了哪些操作呢?
/** * Same as {@link #obtain()}, but sets the value for the target member on the Message returned. * @param h Handler to assign to the returned Message object's target member. * @return A Message object from the global pool. */ public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; }
target就是Hander自己,指消息要发送给谁
再来看看obtain()方法
/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
这个方法就是跟我们平常new Message 对象一样 ,只不过是在之前添加了一判断,判断系统中是否存在空的Message,如果存在就直接返回否则就创建一个Message对象。
其实发送消息也可以这样玩,不使用直接使用Hander而是使用Message的sendToTarget()方法,代码如下。
Dog dog=new Dog("萨摩耶",1);// Message message = new Message(); Message message= customHander.obtainMessage(); message.arg1 = 1; message.arg2 = 2; message.obj = dog;// customHander.sendMessage(message); message.sendToTarget();
我们来看看Message.sendToTarget()方法源码
/** * Sends this Message to the Handler specified by {@link #getTarget}. * Throws a null pointer exception if this field has not been set. */ public void sendToTarget() { target.sendMessage(this); }
上面提到过target
就是Hander自己,其实就是调用Hander自己的一个sendMessage,跟我们普通发送Message没有什么区别只是里面封装了一个target,本质还是调用了Hander.sendMessage(...)
- Handler 消息拦截使其接收者接收不到消息
自定义Handler(interceptHander)拦截消息
Handler interceptHander = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { System.out.println("is intercept Handler>"+msg.what); // 设置true拦截消息 return true; } }){ @Override public void handleMessage(Message msg) { System.out.println("is intercept Handler"); } };
interceptHander发送一个消息Message
interceptHander.sendEmptyMessage(1);
四、Handler几种发送消息方式之间区别
可以参考:Handler发送sendMessage和postRunnable的区别
五、Handler的原理是什么?
handler原理图Handler封装了消息的发送:内部会跟Looper关联
Looper(消息封装的载体):内部包含一个消息队列(MessageQueue),所有Handler发送的消息都会走向这个消息队列;
Looper.Looper方法是一个死循环,不断的从MessageQueue取消息,如果有消息就处理消息,没有消息就阻塞。MessageQueue(消息队列):可以添加消息,并处理消息
总结:Handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己(handleMessage),MessageQueue就是一个存储消息的容器。
六、自定义一个与线程相关的Handler
1、线程中创建一个Looper
可以使用 Looper.prepare();
方法
2、实例化一个Handler
3、调用Looper.loop();
方法循环处理消息
public class HandlerActivity extends AppCompatActivity { private MyThread myThread; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { System.out.println("UI :"+Thread.currentThread()); } }; class MyThread extends Thread{ public Handler handler; @Override public void run() { Looper.prepare(); handler= new Handler(){ @Override public void handleMessage(Message msg) { System.out.println("currentThread:"+Thread.currentThread()); } }; Looper.loop(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); myThread = new MyThread(); myThread.start(); try { Thread.sleep(1000*5); } catch (InterruptedException e) { e.printStackTrace(); } myThread.handler.sendEmptyMessage(1); handler.sendEmptyMessage(1); }}
输出结果:
System.out: currentThread:Thread[Thread-151,5,main]System.out: UI :Thread[main,5,main]
七、非UI线程真的不能更新UI吗?
我们运行下面的代码测试:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); new Thread() { @Override public void run() { idTv.setText("Javen205测试非UI线程更新UI会出现什么异常呢?"); } }.start(); }
最开始的案例中我们去掉Thread.sleep(...)会正常运行吗?答案:可正常运行。是不是感觉有点奇怪呢?那为什么直接在Activity的onCreate中添加子线程可以直接更新UI呢?
详细解答: 为什么我们可以在非UI线程中更新UI
八、Handler异步消息处理(HandlerThread)
Android HandlerThread 完全解析
Android异步消息处理机制完全解析,带你从源码的角度彻底理解
一个简单的例子
public class HandlerThreadActivity extends AppCompatActivity { private Handler handler; private HandlerThread thread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_thread); thread = new HandlerThread("Handler Thread"); thread.start(); handler = new Handler(thread.getLooper()){ @Override public void handleMessage(Message msg) { // 处理耗时操作 System.out.println("current thread>"+Thread.currentThread()); } }; handler.sendEmptyMessage(1); }}
测试输出结果
I/System.out: current thread>Thread[Handler Thread,5,main]
九、如何在主线程给子线程发送消息
public class HandlerThreadActivity extends AppCompatActivity { @BindView(R.id.id_btn1) Button idBtn1; @BindView(R.id.id_btn2) Button idBtn2; private Handler threadhandler; private HandlerThread thread; Handler handler = new Handler() { @Override public void handleMessage(Message msg) { System.out.println("UI thread>" + Thread.currentThread()); // 给主线程发送消息 Message message = new Message(); message.what =1; threadhandler.sendMessageDelayed(message, 1000); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_thread); ButterKnife.bind(this); thread = new HandlerThread("Handler Thread"); thread.start(); threadhandler = new Handler(thread.getLooper()) { @Override public void handleMessage(Message msg) { // 处理耗时操作 System.out.println("current thread>" + Thread.currentThread()); // 给主线程发送消息 Message message = new Message(); message.what =1; handler.sendMessageDelayed(message, 1000); } };// threadhandler.sendEmptyMessage(1); } @OnClick({R.id.id_btn1, R.id.id_btn2}) public void onClick(View view) { switch (view.getId()) { case R.id.id_btn1: handler.sendEmptyMessage(1); break; case R.id.id_btn2: handler.removeMessages(1); threadhandler.removeMessages(1); break; } }}
十、Android中更新UI的几种方式
- handler sendMessage
- runOnUIThread
- handler post
- view post
项目源码地址:https://github.com/Javen205/Hander
推荐阅读
AndroidStudio多渠道打包
Android依赖管理与私服搭建
Android Studio 上传aar(Library)到JCenter
Android版-支付宝APP支付
Android版-微信APP支付
支付宝Wap支付你了解多少?
一张二维码集成微信、支付宝支付
安利时间:
JPay是对微信App支付、支付宝App支付的二次封装,对外提供一个相对简单的接口以及支付结果的回调
极速开发微信公众号是对微信公众平台接口的二次封装。包括开发者模式、事件回调监听、微信模板消息、微信客服消息、自定义菜单、微信支付、素材管理等
如遇到问题欢迎留言交流
更多相关文章
- Android(安卓)AsyncTask源码简单分析
- Android开发之SurfaceView
- Android(安卓)studio爬取网页
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerT
- Android(安卓)LocationManager 使用
- android 发送邮件
- AOSP和Chromium的Android(安卓)WebViewTest
- Android(安卓)更新UI的两种方法——handler和runOnUiThread()
- Android消息处理机制