ChainDestiny 2020-05-07
Handler
机制随处可见.Handle异步消息传递机制
。UI线程,程序启动时自动创建。
开发者自己开启的线程,执行耗时操作等。
UI线程与子线程通信的媒介,添加消息到消息队列(Message Queue)处理循环器分发过来的消息(Looper)。
Handler接受&处理的对象,存储需要操作的消息。
数据存储结构,采用先进先出方式,存储Handler发过来的消息。
消息队列与处理者的媒介,从消息队列中循环取出消息并发送给Handler处理。
使用Handler.sendMessage()、使用Handler.post()
/** * 方式1:新建Handler子类(内部类) */ // 步骤1:自定义Handler子类(继承Handler类) & 复写handleMessage()方法 class mHandler extends Handler { // 通过复写handlerMessage() 从而确定更新UI的操作 @Override public void handleMessage(Message msg) { ...// 执行UI操作 } } // 步骤2:在主线程中创建Handler实例 private Handler mhandler = new mHandler(); // 步骤3:创建所需的消息对象 Message msg = Message.obtain(); // 实例化消息对象 msg.what = 1; // 消息标识 msg.obj = "AA"; // 消息内容存放 // 步骤4:在工作线程中 通过Handler发送消息到消息队列中 mHandler.sendMessage(msg); /** * 方式2:匿名内部类 */ // 步骤1:在主线程中 通过匿名内部类 创建Handler类对象 private Handler mhandler = new Handler(){ // 通过复写handlerMessage() @Override public void handleMessage(Message msg) { ...// 需执行UI操作 } }; // 步骤2:创建消息对象 Message msg = Message.obtain(); // 实例化消息对象 msg.what = 1; // 消息标识 msg.obj = "AA"; // 消息内容存放 // 步骤3:在工作线程中 通过Handler发送消息到消息队列中 mHandler.sendMessage(msg);
new Thread() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // 通过psot()发送,传入1个Runnable对象 mHandler.post(new Runnable() { @Override public void run() { // 指定操作UI内容 } }); } }.start();
在源码分析前,先来了解Handler机制中的几个核心类
关于这几个类的具体作用前面已经介绍过了就不再过多阐述了。
下面开始源码分析,注意力集中了
上文中我们提到过Handler发送消息有两种方式,分别是
下面先从第一种开始分析:
//通过匿名内部类 创建Handler类对象 private Handler mhandler = new Handler(){ // 通过复写handlerMessage()从而确定更新UI的操作 @Override public void handleMessage(Message msg) { ...// 需执行的UI操作 } }; ---------->>开始源码分析 public Handler() { this(null, false); // ->>此处this指代的就是当前的Handler实例,调用有参构造 } public Handler(Callback callback, boolean async) { ...// 无关代码我就不贴了 // 1. 指定Looper对象 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can‘t create handler inside thread that has not called Looper.prepare()"); } // Looper.myLooper()作用:获取当前线程的Looper对象;若线程无Looper对象则抛出异常 // 可通过执行Loop.getMainLooper()方法获得主线程的Looper对象 // 2. 绑定消息队列对象(MessageQueue) mQueue = mLooper.mQueue; // 获取该Looper对象中保存的消息队列对象(MessageQueue) // 至此,完成了handler 与 Looper对象中MessageQueue的关联 }
public static void main(String[] args) { ... // 无关的代码 Looper.prepareMainLooper(); // 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue) ActivityThread thread = new ActivityThread(); // 2. 创建主线程 Looper.loop(); // 3. 自动开启 消息循环 }
public void dispatchMessage(Message msg) { // 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息 // 则执行handleCallback(msg),即回调Runnable对象里复写的run() if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } // 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息,即回调复写的handleMessage(msg) handleMessage(msg); } } public void handleMessage(Message msg) { ... // 创建Handler实例时复写 }
从以上源码来看,使用Handler.post()时,系统会自动回调Runnable对象里复写的run()方法,将其打包成msg对象, 实际上和sendMessage(Message msg)发送方式相同。
至此,关于Handler的异步消息传递机制的解析就完成了。
1.Handler的一般用法 : 新建Handler子类(内部类) 、匿名Handler内部类,而在我们编写代码的时候,其实编译器就会提示我们这种操作可能会发生内存泄漏,在android studio中就是这块代码会变黄。
2.提示的原因是
3.内存泄漏的原因
首先我们先要了解一些其他的知识点。
而在Handler处理消息的时候,Handler必须处理完所有消息才会与外部类解除引用关系,如果此时外部Activity需要提前被销毁了,而Handler因还未完成消息处理而继续持有外部Activity的引用。由于上述引用关系,垃圾回收器(GC)便无法回收MainActivity,从而造成内存泄漏。
将Handler的子类设置成 静态内部类,同时,还可加上 使用WeakReference弱引用持有Activity实例。
原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
// 设置为:静态内部类 private static class FHandler extends Handler{ // 定义 弱引用实例 private WeakReference<Activity> reference; // 在构造方法中传入需持有的Activity实例 public FHandler(Activity activity) { // 使用WeakReference弱引用持有Activity实例 reference = new WeakReference<Activity>(activity); } // 复写handlerMessage() @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: //更新UI break; case 2: //更新UI break; }
@Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期 }
推荐使用上述解决方法一,以保证保证Handler中消息队列中的所有消息都能被执行
本文主要讲述了Handler的基本原理和使用方法,以及造成内存泄漏的原因和解决方案。