参考网站:https://www.jianshu.com/p/9fe944ee02f7 参考书籍 :《第一行代码》
背景(摘自《第一行代码》):
和许多其他的GUI库一样,Android的UI也是线程不安全的。也就是说,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。
一、Handler机制的定义
Handler机制是一套Android消息传递机制,也可以说是异步消息处理机制、Android多线程编程。
二、Handler机制的作用
在多线程的应用场景中,将工作行程中需要更新UI的操作消息传递到UI主线程,从而实现工作线程对UI的更新处理,最终实现异步消息的处理。
三、Handler机制存在的意义(为什么要用Handler消息传递机制?)
简短描述:
多个线程并发更新UI的同时,保证线程安全。
具体描述:
背景:在Android开发中,为了UI操作是线程安全的,规定了只允许UI线程(主线程)更新Activity里的UI组件。
冲突:在实际开发中,存在多个线程并发操作UI组件的情况,导致UI操作的线程不安全。
诱导出需求为:(1)多个线程可并发操作UI组件(2)保证线程安全
解决方案:Handler消息传递机制 (工作线程需要更新UI时,通过Handler通知主线程,从而在主线程更新UI操作)
四、Handler机制的相关概念
Android中的异步消息处理主要由4个部分组成:
Message(消息)、Handler(处理者)、MessageQueue(消息队列)、Looper(循环器)
(以下介绍摘抄《第一行代码》)
1.Message
Message是在线程之间传递的信息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。除了Message的what字段之外,还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。
2.Handler
Handler顾名思义就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handlerde的sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。
3.MessageQueue
MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
4.Looper
Looper是每个线程中的MessageQueue的管家(循环器),调用Looper 的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将他取出,并传递到Handler的handleMessage()方法中,每个线程中也只会只有一个Looper 对象。
概念示意图:
![]()
五、Handler机制使用的方法(方式)
- Handler使用方式,因发送消息到消息队列的方式不同而不同。
- 共分为2种:使用Handler.sendMessage()、Handler.post()。
使用步骤:
方式1:使用Handler.sendMessage()
在该使用方式中,又分为2种:新建Handler子类(内部类)、匿名Handler子类。但本质上相同,继承了Handler类&创建子类
第一种,新建Handler子类的方法:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final int UPDATE_TEXT = 1; TextView mTextView; MyHandler mHandler = new MyHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.button); mTextView = findViewById(R.id.text_view); button.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button: new Thread(new Runnable() { @Override public void run() { Message message = new Message(); message.what = UPDATE_TEXT; mHandler.sendMessage(message); } }).start(); break; } } class MyHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXT: mTextView.setText("你好,世界"); break; } } } }
第二种,匿名Handler子类的方法:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final int UPDATE_TEXT = 1; TextView mTextView; Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXT: mTextView.setText("你好,世界"); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.button); mTextView = findViewById(R.id.text_view); button.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button: new Thread(new Runnable() { @Override public void run() { Message message = new Message(); message.what = UPDATE_TEXT; mHandler.sendMessage(message); } }).start(); break; } } class MyHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXT: mTextView.setText("你好,世界"); break; } } } }
方式2:使用Handler.post()
public class MainActivity extends AppCompatActivity implements View.OnClickListener { TextView mTextView; private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.button); mTextView = findViewById(R.id.text_view); mHandler = new Handler(); button.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button: new Thread(new Runnable() { @Override public void run() { mHandler.post(new Runnable() { @Override public void run() { mTextView.setText("你好,世界!"); } }); } }).start(); break; } } }
六、Handler机制工作原理
- 理解Handler机制的工作流程,能更好地帮助我们使用Handler和理解其内部流程
6.1 工作流程解析:
- 异步通信准备
- 消息发送
- 消息循环
- 消息处理
示意图(注意:这里的处理器其实就是循环器):
6.2 工作流程图
6.3 示意图
6.4 特别注意
线程(Thread)、循环器(Looper)、处理者(Handler)之间的对应关系如下:
- 1个线程(Thread)只能绑定1个循环器(Looper),但可以有多个处理者(Handler)
- 1个循环器(Looper)可绑定多个处理者(Handler)
- 1个处理者(Handler)只能绑定1个循环器(Looper)
示意图:
七、Handler机制的源码分析
- 作为程序猿,知其然而必须知其所以然,理解其源码能更好地了解Handler机制的原理。
7.1 Handler机制的核心类
7.1.1 类说明:
Handler机制中有3个重要的类:
1.Handler(处理者)
2.MessaeQueue(消息队列)
3.Looper(循环器)
7.1.2 类图:
7.1.3 具体介绍:
7.1.4 源码分析:
由于源码的篇幅太长,我这里就直接贴上原网站链接了。
总结图:
![]()
八、使用Handler机制的特别注意(内存泄漏:Memory Leak)
- 在Android开发中,内存泄漏(Memory Leak)十分常见
1.内存泄漏的定义:本该被回收的对象不能被回收而停留在堆内存中
2.内存泄漏出现的原因:当一个对象已经不再被使用时,本该被回收但却因为有另外一个正在使用的对象持有它的引用从而导 致它不能被回收。这就导致了内存泄漏(Memory Leak)。
同样篇幅较长,直接贴上原网站链接了。
解决方案有两个:
方案1.静态内部类+弱引用
原理
静态内部类 不默认持有外部类的引用,从而使得 “未被处理 / 正处理的消息 ->Handler
实例 -> 外部类” 的引用关系 的引用关系 不复存在。具体方案
将Handler
的子类设置成 静态内部类
- 同时,还可加上使用WeakReference弱引用持有Activity实例
- 原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
- 解决代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final int UPDATE_TEXT = 1; static TextView mTextView; Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.button); mTextView = findViewById(R.id.text_view); mHandler = new MyHandler(this); button.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button: new Thread(new Runnable() { @Override public void run() { Message msg = new Message(); msg.what = UPDATE_TEXT; mHandler.sendMessage(msg); } }).start(); break; } } //解决内存泄漏 private static class MyHandler extends Handler { private WeakReference<Activity> mReference; MyHandler(Activity activity) { mReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXT: mTextView.setText("你好,世界"); break; } } } }
方案2.当外部类结束生命周期时,清空Handler内消息队列
原理
不仅使得 “未被处理 / 正处理的消息 ->Handler
实例 -> 外部类” 的引用关系 不复存在,同时 使得Handler
的生命周期(即 消息存在的时期) 与 外部类的生命周期 同步具体方案
当 外部类(此处以Activity
为例) 结束生命周期时(此时系统会调用onDestroy()
),清除Handler
消息队列里的所有消息(调用removeCallbacksAndMessages(null)
)具体代码
@Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期 }
使用建议:
为了保证
Handler
中消息队列中的所有消息都能被执行,此处推荐使用解决方案1解决内存泄露问题,即 静态内部类 + 弱引用的方式