Android全面解析Handler
Posted 晴耕雨读,不羁少年!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android全面解析Handler相关的知识,希望对你有一定的参考价值。
前言:
由于Handler
和Binder
是android开发的俩大利器之一,所以有必要来深入讲解一下Handler
,关于Binder
可以参考我上一篇博客《IPC机制 Binder》,废话不多说,今天我将图文并茂,一节一节解剖Handler
,一节一节的总结Handler
相关知识点。
一:Handler的定义
Handler
的定义我觉得可以用一句话高度概括,那就是:Handler是Android提供的一套完整的消息传递机制。
二:Handler的作用
我们都知道,在Android
种,一般情况下View
是不能在工作线程更新的,除了surfaceview
(特殊),那么如果我们非要在工作线程中更新UI那该怎么办了,那只能借助handler
了,所以handler
的作用就是:在多线程的应用场景中,将工作线程中需更新UI的操作信息传递到UI主线程,从而间接的实现工作线程对UI的更新处理,最终实现异步消息的处理。
三:为什么要使用Handler消息传递机制
我们既然知道了使用handler
可以间接实现工作线程对UI的更新,那你是否奇怪,为什么更新非要使用Handler
,而不能是其它。那是因为,多个线程并发更新UI的同时要保证线程安全。详细描述见下表(图片来自:https://www.jianshu.com/p/f0b23ee5a922
)
四:Handler必须掌握的相关概念
关于Handler的先关概念主要包括如下4大点。即 Handler
、Message
、Message Queue
、Looper
,希望大家先熟悉相关概念,下图大致高度介绍相关概念:(图片来自: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 Studio - 如何从片段中停止 handler.postDelayed?