Android全面解析Handler

Posted 晴耕雨读,不羁少年!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android全面解析Handler相关的知识,希望对你有一定的参考价值。

前言:

由于HandlerBinderandroid开发的俩大利器之一,所以有必要来深入讲解一下Handler,关于Binder可以参考我上一篇博客《IPC机制 Binder》,废话不多说,今天我将图文并茂,一节一节解剖Handler,一节一节的总结Handler相关知识点。

Handler学习大纲
1:定义
2:作用
3:为什么要使用Handler消息传递机制
4:Handler必须掌握的相关概念
5:工作原理和源码解析
6:Handler的延伸
7:总结

一:Handler的定义

Handler的定义我觉得可以用一句话高度概括,那就是:Handler是Android提供的一套完整的消息传递机制。

二:Handler的作用

我们都知道,在Android种,一般情况下View是不能在工作线程更新的,除了surfaceview(特殊),那么如果我们非要在工作线程中更新UI那该怎么办了,那只能借助handler了,所以handler的作用就是:在多线程的应用场景中,将工作线程中需更新UI的操作信息传递到UI主线程,从而间接的实现工作线程对UI的更新处理,最终实现异步消息的处理。

更新UI的相关信息
传递工作线程更新UI的相关信息
在主线程更新UI
工作线程
Handler
主线程
更新结束

三:为什么要使用Handler消息传递机制

我们既然知道了使用handler可以间接实现工作线程对UI的更新,那你是否奇怪,为什么更新非要使用Handler,而不能是其它。那是因为,多个线程并发更新UI的同时要保证线程安全。详细描述见下表(图片来自:https://www.jianshu.com/p/f0b23ee5a922)

四:Handler必须掌握的相关概念

关于Handler的先关概念主要包括如下4大点。即 HandlerMessageMessage QueueLooper,希望大家先熟悉相关概念,下图大致高度介绍相关概念:(图片来自:https://www.jianshu.com/p/f0b23ee5a922)

五:Handler工作原理和源码分析

在第四步我们主要明白了几个相关概念,接下来,会深层次的揭破他们的源码,从源码里搞清他们之间的工作关系和工作原理。

5.1:Handler Looper Message 关系是什么?

5.1.1分析Handler

首先我们来分析分析一下 Handler 的用法,我们知道,要创建一个 Handler 对象
非常的简单明了,直接进行 new 一个对象即可,但是这里会隐
藏着什么注意点呢。现在可以试着写一下下面的一小段代码,然后自己运行看看:

public class MainActivity extends AppCompatActivity{
private Handler mHandler0;
private Handler mHandler1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler0 = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
mHandler1 = new Handler();
}
}).start();
}

这一小段程序代码主要创建了两个 Handler 对象,其中,一个在主线程中创建,
而另外一个则在子线程中创建,现在运行一下程序,则你会发现,在子线程创建
的Handler对象竟然会导致程序直接崩溃。奔溃异常信息如下:

2021-07-23 12:06:19.034 14393-14451/com.pcl.lpr.debug E/AndroidRuntime: FATAL EXCEPTION: Thread-10
    Process: com.pcl.lpr.debug, PID: 14393
    java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-10,5,main] that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:207)
        at android.os.Handler.<init>(Handler.java:119)
        at com.pcl.ocr.ui.TestActivity$1.run(TestActivity.java:23)
        at java.lang.Thread.run(Thread.java:919)

提示的错误竟然是:Can't create handler inside thread that has not called Looper.prepare()
于是我们按照 logcat 中所说,在子线程中加入 Looper.prepare(),即代码如下:

public class TestActivity extends AppCompatActivity {
    private Handler mHandler0;
    private Handler mHandler1;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        mHandler0 = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                mHandler1 = new Handler();
            }
        }).start();
        
    }
}

再次运行一下程序,发现程序不会再崩溃了,可是,单单只加这句Looper.prepare()是否就能解决问题了。我们探讨问题,就要知其然,才能了解得更多。我们还是先分析一下源码吧,看看为什么在子线程中没有加Looper.prepare()就会出现崩溃,而主线程中为什么不用加这句代码?我们看下Handler()构造函数:

public Handler() {
this(null, false);
}

构造函数非常简单,直接调用 this(null, false),于是接着看其调用的函数:

public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || kla
ss.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static o
r leaks might occur: " +
klass.getCanonicalName());
 }
}

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Lo
oper.prepare()");
}
mQueue = mLooper.mQueue;mCallback = callback;
mAsynchronous = async;
}

不难看出,源码中调用了 mLooper = Looper.myLooper()方法获取一个 Looper对象,若此时 Looper 对象为 null,则会直接抛出一个“Can't create handlerinside thread that has not called Looper.prepare()”异常,那什么时候造成 mLooper 是为空呢?那就接着分析 Looper.myLooper()

public static Looper myLooper() {
  return sThreadLocal.get();
}

这个方法在 sThreadLocal 变量中直接取出 Looper 对象,若 sThreadLocal 变量中存在 Looper 对象,则直接返回,若不存在,则直接返回 null,而 sThreadLocal变量是什么呢?

static final ThreadLocat<Looper> sThreadLocal = new ThreadLocal<Looper>();

它是本地线程变量,存放在 Looper 对象,由这也可看出,每个线程只有存有一个 Looper 对象,可是,是在哪里给 sThreadLocal 设置 Looper 的呢,通过前面的试验,我们不难猜到,应该是在Looper.prepare()方法中,现在来看看它的源码:

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));}

由此看到,我们的判断是正确的,在 Looper.prepare()方法中给 sThreadLocal变量设置 Looper 对象,这样也就理解了为什么要先调用 Looper.prepare()方法,才能创建 Handler 对象,才不会导致崩溃。但是,仔细想想,为什么主线程就不用调用呢?不要急,我们接着分析一下主线程,我们查看一下ActivityThread中的 main()方法,代码如下:

public static void main(String[] args) {
		SamplingProfilerIntegrationAndroid全面解析之Handler机制:常见问题汇总

Android消息机制Handler解析(源码+Demo)

Android Studio - 如何从片段中停止 handler.postDelayed?

Android之Handler源代码深入解析

Android源代码解析之--&gt;异步任务AsyncTask

深入解析Android中Handler消息机制