这篇文章用于分析自己对Handler源码的理解,会不定期把和Handler相关的内容整理到这。
文章目录
- 一 使用场景
- 二 Handler的常用方式
- 三 ThreadLocal
- 四 Handler相关源码分析
- 5 总结
一 使用场景
主线程中不能执行耗时操作,否则会报not respond的超时异常。所以耗时操作要放在子线程中去执行。但是子线程不能进行更新UI的操作。所以如果要更新UI,就要切换到主线程去执行。Handler就是用于处理线程的切换。
二 Handler的常用方式
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
一般常用的Handler的使用就是这样,在主线程中new一个Hander,并重写onHandle()方法。在子线程中调用hander.sendMessage();此时就会切换到主线程成中执行handleMessage()方法。
要明白Handler的原理首先要明白ThreadLocal;
三 ThreadLocal
- 什么是ThreadLocal呢?首先看官方的描述:
/**
* Implements a thread-local storage, that is, a variable for which each thread
* has its own value. All threads share the same {@code ThreadLocal} object,
* but each sees a different value when accessing it, and changes made by one
* thread do not affect the other threads. The implementation supports
* {@code null} values.
*
* @see java.lang.Thread
* @author Bob Lee
*/
public class ThreadLocal {
首先用自己的话描述一下上述解释:
声明一个ThreadLocal对象,就是在不同的线程中
有各自的值,所有的线程可以共享同一个ThreadLocal对象。
每个线程在使用ThreadLocal时,都会有各自的值,可以在不同的线程中单独设置。
在线程中操作不会对其他线程有影响。
举个例子:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThreadLocal = new ThreadLocal<>();
mThread1.start();
mThread2.start();
mThread3.start();
Thread currentThread = Thread.currentThread();
try {
currentThread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("TAG","value1"+value1);
Log.i("TAG","value2"+value2);
Log.i("TAG","value3"+value3);
}
Thread mThread1 = new Thread(){
@Override
public void run() {
mThreadLocal.set("德玛西亚之力!");
value1 = mThreadLocal.get();
}
};
Thread mThread2 = new Thread(){
@Override
public void run() {
mThreadLocal.set("德玛西亚皇子!");
value2 = mThreadLocal.get();
}
};
Thread mThread3 = new Thread(){
@Override
public void run() {
mThreadLocal.set("德玛西亚之翼!");
value3 = mThreadLocal.get();
}
};
打印结果:
01-06 08:22:26.209 1629-1629/example.com.demo_practice I/TAG: value1德玛西亚之力!
01-06 08:22:26.209 1629-1629/example.com.demo_practice I/TAG: value2德玛西亚皇子!
01-06 08:22:26.209 1629-1629/example.com.demo_practice I/TAG: value3德玛西亚之翼!
在不同的线程中操作同一个ThreadLocal对象,在ThreadLocal对象中获取的值是不同的。所以ThreadLocal在不同的线程中相互没有影响。
四 Handler相关源码分析
Hander的通常的使用就是在主线程更新UI,这里先不举例说明具体的使用例子,下面是简单的使用
Thread mHandlerThread = new Thread() {
Handler handler;
@Override
public void run() {
Looper.prepare();
handler = new Handler() {
};
Looper.loop();
}
};
那么为什么每次new Handler()时候都要先执行Looper.prepare()呢?先看一下Handler的构造方法:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
这里首先判断mLooper是否为空,如果是空直接抛出一个异常Can’t create handler inside thread that has not called Looper.prepare(),意思就是在调用Looper.prepare()方法之前不能创建一个Hander;
再来看Looper.prepare()方法:
public static void prepare() {
prepare(true);
}
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));
}
在此方法中,首先判断
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
也就是说,一个线程中只能调用prepare()方法一次。
从下面的这行代码可以看出ThreadLocal中保存的是Looper对象。
sThreadLocal.set(new Looper(quitAllowed));
可以看出sThreadLocal是静态的常量,即所有的线程中操作的ThreadLocal对象都是同一个ThreadLocal对象。
接下来接着分析Looper的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看出这里面声明了一个MessageQueue对象和一个Thread对象。也就是说一个Looper对应一个MessageQueue和一个Thread;
Looper.prepare()方法就分析到这,接下来看Looper.Loop();
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
从这段代码中可以看出,当msg==null时死循环就会阻塞,当msg!=nul时,就会执行
msg.target.dispatchMessage(msg);
这段代码首先是标记msg,然后调用dispatchmessage()方法;
然后看下dispatchmessage方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这里的CallBack我不理解。回头明白了在补充。这个方法最后调用的handleMessage()方法,然后再来看这个方法:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
可以看出这个 方法是个空方法,也就是要自己重写的方法。
写到这里,Handler的轮询消息的主线就已经看出来了,下次有时间在把Hanlder取消息再消息分析一下。
5 总结
1 用途: Handler是用于线程间切换处理的框架
2 核心类: Handler持有Looper对象和MessageQueue对象, Looper用于循环遍历MessageQueue中的消息
3 Looper是线程唯一的: Looper对象保存在ThreadLocal中,不同线程中的只能取出唯一的Looper对象
4 任务处理: 当通过Handler#sendMessage()后,Looper在无限循环中从MessageQueue中取出Message,转交到Handler#handleMessage()中处理