在写这篇文章以前,先祝你们中秋佳节快乐,在这三天小假里,祝你们玩的开心!好了接下来狗哥给你们分析一下Handler的机制原理,这也算是我闲闲的三天假的一个补偿吧。其实Handler机制原理已经被许多大佬写透的东西了,这里咱们为何还要说呢?由于对于像我这种的菜狗来讲,也只是会使用Handler而已,对于其机制,也只是大概知道,今天咱们来结合源码深刻剖析其原理。html
咱们都知道,Android 中经常使用的网络请求都是在子线程中进行的,而更新UI的操做都是在主线程中进行的,说到这,猿猿们确定会吐槽了,那这跟你写的Handler有啥关系啊。狗哥在这给大家解释一下: 从 Android 4.0 开始,Android 中就强制不容许在主线程中进行网络请求,也不容许在子线程中更新UI,因此Handler机制应运而生,做为一套线程间通讯的机制,只不过常常被咱们用于更新UI。安全
首先介绍其原理以前,先给你们分析一下handler的四大类
Handler 负责发送消息及其处理消息(发送者和处理者)
Message 系统传递的消息
MessageQueue 消息队列,负责存储消息
Looper 消息泵,不断从消息队列中取出消息而且发送给持有本条消息的Handler
介绍完handler的四大类,咱们简单理解一下他的机制原理 :
Handler发送一个 Message 到 MessageQueue ,这时 handler 是充当发送者对象而后经过消息泵 Looper ,不断从消息队列
中取出消息,而后再经过 handler 处理消息,这时的 handelr 是充当的一个处理者的形象。
复制代码
来咱们首先看一下 handler 类的初始化操做bash
public class Handler {
final MessageQueue mQueue; // 关联的MessageQueue
final Looper mLooper; // 关联的looper
final Callback mCallback;
public Handler() {
// 默认关联当前线程的looper
mLooper = Looper.myLooper();
// looper不能为空,即默认的构造方法只能在looper线程中使用
if (mLooper == null) {
throw new RuntimeException(
//没法在未调用loop .prepare()的线程内建立处理程序。
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 直接把关联looper的MessageQueue做为本身的MessageQueue,
// 所以它的消息将发送到关联looper的MessageQueue上
mQueue = mLooper.mQueue;
mCallback = null;
}
}
复制代码
咱们经过源码发现,初始化中handler 关联了 messageQueue 及其 looper 对象。网络
咱们一般会这样建立handler,在handler建立时会关联一个looper,默认的构造方法将关联当前线程的looper。这也就是咱们常常建立 handler 对象的两种方式。app
Handler handler = new Handler(Looper.getMainLooper());
Handler handler = new Handler();
复制代码
handler关联了 looper 对象和 messagequeue 对象,才能完成调度工做,而在整个调度工做中,looper 对象才是真正的核心,也就至关于整个团队的leader 了,下面咱们仔细、认真的分析一下 looper 的源码。ide
Looper.getMainLooper(),咱们发现handler关联当前线程 looper 的入口实际上是在 ActivityThread 的 main() 方法中进行的。而在 main() 方法中它还作了另一件重要的事情。函数
public static void main(String[] args) {
//主线程会建立一个Looper对象。
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//执行消息循环
Looper.loop();
}
复制代码
没错就是执行消息循环 Looper.loop(); 。 总结 main()中作的两件事 :oop
一、Looper.prepareMainLooper(); //主线程会建立一个Looper对象。
二、Looper.loop(); //执行消息循环
复制代码
接下来咱们逐个分析:源码分析
一、Looper.prepareMainLooper();post
public static void prepareMainLooper() {
//在主线程中,其默认初始化一个Looper对象,所以咱们在主线程的操做中是不须要本身去调prepare()。
prepare(false);
synchronized (Looper.class) {
//这里先进行判断,在主线程是否已经存在Looper了,
// 避免咱们手动去调用prepareMainLooper(),由于这个是给程序入口初始化的时候系统会自动调用的
if (sMainLooper != null) {
//看抛出的异常,初始化looper已经被准备好了
throw new IllegalStateException("The main Looper has already been prepared.");
}
//设置全局变量,主线程的looper
sMainLooper = myLooper();
}
}
复制代码
注意这个函数的注释,大概意思是:在主线程建立一个looper,是这个主线程的主looper,当这个app在初始化的时候就会自行建立,所以这个函数不是给大家调用的,是给系统自身在程序建立的时候调用的。
继续往下看,有个prepare(boolean)函数,咱们去看看这个究竟是用来干什么的。
private static void prepare(boolean quitAllowed) {
//先判断当前线程是否已经存在Looper了,若是存在,不容许设置新的Looper对象,一个线程只容许存在一个Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//在当前线程中,建立新的Looper对象,并绑定当前线程
sThreadLocal.set(new Looper(quitAllowed));
}
复制代码
咱们看到了sThreadLocal,咱们先看看这个sThreadLocal在Looper是干什么用的。
//sThreadLocal在Looper中做为全局变量,用于保存每一个线程中的数据,能够看作是容器
public static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
复制代码
Looper中,sThreadLocal做为一个全局变量,sThreadLocal实际上是保存Looper的一个容器,咱们继续往ThreadLocal的get、set进行分析。
public T get() {
//获取当前线程保存的对象--经过get函数来获取Looper对象
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T) e.value;
}
return setInitialValue();
}
public void set(T value) {
//把当前的looper保存到当前线程中
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
复制代码
关键的代码:
Thread t=Thread.currentThread();
复制代码
也就是说,咱们的Looper对象分别保存在相对应的线程中。咱们再回来看刚才前面 的prepare(boolean)函数:
private static void prepare(boolean quitAllowed) {
//先判断当前线程是否已经存在Looper了,若是存在,不容许设置新的Looper对象,一个线程只容许存在一个Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//在当前线程中,建立新的Looper对象,并绑定当前线程
sThreadLocal.set(new Looper(quitAllowed));
}
复制代码
Looper.prepare()函数总结:
Looper.prepare(boolean)的做用就是建立一个Looper对象,并与当前线程绑定在一块儿。
在代码中,首先判断当前线程是否已经存在looper,若是不存在则建立新的looper而且绑定到当前的线程上。
复制代码
而后咱们再会看以前 prepareMainLooper() 的代码:
public static void prepareMainLooper() {
//在主线程中,其默认初始化一个Looper对象,所以咱们在主线程的操做中是不须要本身去调prepare()。
prepare(false);
synchronized (Looper.class) {
//这里先进行判断,在主线程是否已经存在Looper了,
// 避免咱们手动去调用prepareMainLooper(),由于这个是给程序入口初始化的时候系统会自动调用的
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//设置全局变量,主线程的looper
sMainLooper = myLooper();
}
}
复制代码
那么咱们看到sMainLooper是什么,myLooper()又是什么呢?
//保存一个主线程的looper
private static Looper sMainLooper; // guarded by Looper.class
public static Looper myLooper() {
//使用当前线程的looper
return sThreadLocal.get();
}
复制代码
总结:
sMainLooper在Looper作为一个全局变量,保存主线程绑定的looper,myLooper()则是获取当前线程绑定的Looper。
在prepareMainLooper()中,在主线程中建立一个新的Looper,而且绑定主线程中,同时把这个主线程的looper赋值给
sMainLooer这个全局变量。
复制代码
刚才讲到 handler 再 AcitvityThread 中的 main() 方法中主要作了两件事,刚才咱们分析了第一件事,也就是 Looper.prepareMainLooper();
接下来咱们讲他作的第二件事 :Looper.loop();
补脑一会儿,咱们都把前期工做给作好了,怎样才能让让 Handler 从 MessageQueue 中获取到 Message 进行处理呢 ?没错就是经过 Looper.loop(); 让整个消息队列动起来。来咱们分析一下 loop() 方法中的源码:
/**
* 调用此函数用于启动消息队列循环起来,做用至关于一个团队的leader下达的命令,
* 只有leader说今天加班,你今天就必须加班😂
* Run the message queue in this thread. Be sure to call
*/
public static void loop() {
//先进行判断当前线程是否有绑定looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取这个looper的消息队列
final MessageQueue queue = me.mQueue;
//确保这个线程的标识是本地进程的标识,
//并跟踪标识符其实是什么。
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//循环经过消息队列来获取消息
for (; ; ) {
Message msg = queue.next(); // might block
if (msg == null) {
//没有消息退出消息队列。
return;
}
// 这必须在本地变量中,以防UI事件设置日志记录器
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//关键点,这里的msg.target也就是hanlder.看回代码hanlder.enqueueMessage()
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//最后回收这个message
msg.recycleUnchecked();
}
}
复制代码
这一段代码比较长,咱们逐个注释,发现它是先判断当前的线程是否存在looper,若是存在获取保存在Looper的消息队列messagequeue,而后无限循环这个消息队列来获取message,下面这句代码是比较重要的:
//关键点,这里的msg.target也就是hanlder.看回代码hanlder.enqueueMessage()
msg.target.dispatchMessage(msg);
复制代码
分析:
msg.target其实就是咱们的handler,不管是handler经过post或者sendEmptyMessage,最终都会调用到调到这个
enqueueMessage(),在这里会将handler赋值到msg.target中。
复制代码
好,那咱们再继续分析这个 enqueueMessage() 函数
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//在message中放一个标记
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//在这里把消息放到队列里面去
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
既然 Looper 中的 loop() 调用了 msg.target.dispatchMessage,咱们就看看 Handler的dispatchMessage 是如何进行处理这个msg的。
注意: dispatchMessage() 分发函数是 Handler 做为处理者的身份进行的,天然这个函数也就在 Handler 类中了。
来咱们上源码继续逐个分析:
public void dispatchMessage(Message msg) {
//这里先判断callback是否为空
// callback就是咱们使用handler.post(Runnable r)的入参runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//若是hanlder的入参callback不为空,优先处理
if (mCallback != null) {
//若是回调返回true.则拦截了handler.handleMessage的方法
if (mCallback.handleMessage(msg)) {
return;
}
}
//这就是为何咱们使用hanlder的时候,须要重写handleMessage的方法
handleMessage(msg);
}
}
复制代码
分析:
在dispatchMessage函数中,意思就是分发这个消息,在代码中先判断msg.callback是否为空,msg.callback是什么?其实
就是handler.post中的runnable对象,通俗的来讲就是handler若是有post操做的,就处理post的操做。
复制代码
咱们在看看 handlerCallback这个函数。
private static void handleCallback(Message message) {
message.callback.run();
}
复制代码
很简单,就一行代码,咱们看到了熟悉的run方法,这个不就是咱们使用post的时候传进去的Runnbale对象的run方法吗? 来,咱们模拟一段代码:
/**
* 模拟开始
*/
private void doSometh() {
//开启个线程,处理复杂的业务业务
new Thread(new Runnable() {
@Override
public void run() {
//模拟很复杂的业务,须要1000ms进行操做的业务
......
handler.post(new Runnable() {
@Override
public void run() {
//在这里能够更新ui
mTv.setText("在这个点我更新了:" + System.currentTimeMillis());
}
});
}
}).start();
}
复制代码
写到这里或许大佬们会有个疑问,Handler 的 post 方法建立的线程和UI线程有什么关系?来咱们仍是继续分析源码解释:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
复制代码
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
复制代码
能够看到,在getPostMessage中,获得了一个 Message 对象,而后将咱们建立的Runable对象做为 callback 属性,赋值给了当前 message。咱们继续回到 handler.dispatchMessage(Message) 中,若是不是经过 post 那么 callback 就为空,咱们看到了一个 mCallback变量,咱们看看这个Callback的定义:
public interface Callback {
public boolean handleMessage(Message msg);
}
public Handler(Callback callback) {
this(callback, false);
}
复制代码
咱们能够经过实现这个接口,并做为一个参数传进去 Handler 来达处处理这个消息的效果。 咱们再回到以前的 dispatchMessage() 函数中理解一下刚才的思路:
public void dispatchMessage(Message msg) {
//这里先判断callback是否为空
// callback就是咱们使用handler.post(Runnable r)的入参runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//若是hanlder的入参callback不为空,优先处理
if (mCallback != null) {
//若是回调返回true.则拦截了handler.handleMessage的方法
if (mCallback.handleMessage(msg)) {
return;
}
}
//这就是为何咱们使用hanlder的时候,须要重写handleMessage的方法
handleMessage(msg);
}
}
复制代码
分析:最后一行代码中,咱们看到了熟悉的handleMessage,这不就是咱们常常 handler.handlerMessage 的方法吗?
但注意以前咱们所看到的,若是咱们mCallback.handlerMessage(msg)返回为true的话,这样就不交给handler.handleMessage处理了。这里咱们再 看看 handleMessage() 函数 :
public void handleMessage(Message msg) {
}
复制代码
在这里咱们发现了这个方法是空的,为什们呢 ?这里补充一句。由于消息的最终回调是由咱们控制的,咱们在建立handler的时候都是复写handleMessage方法,而后根据msg.what进行消息处理。产生一个Message对象,能够new ,也可使用Message.obtain()方法;二者均可以,可是更建议使用obtain方法,由于Message内部维护了一个Message池用于Message的复用,避免使用new 从新分配内存。
咱们继续看回来咱们的 Looper.loop() :
public static void loop() {
.....
.....
//循环经过消息队列来获取消息
for (; ; ) {
......
//最后回收这个message
msg.recycleUnchecked();
}
}
复制代码
在无限循环每一个消息的时候,除了调用 handler.dispatchMessage,最后还会调用 msg.recycleUnchecked() 进行回收这个消息。
1.为何在主线程中建立Handler不须要咱们调用 Looper.prepare().由于在程序的入口中系统会调用Looper.prepareMain
Looper()来建立,而且让其主线程的Looper启动起来。若是咱们在子线程建立 handler,须要手动建立 looper 而且启动。
2.每个线程只能存在一个 Looper, Looper有一个全局变量 sThreadLocal 用来保存每个线程的looper,经过 get、set 进行
存取 looper。
3.Handler 能够经过经过 post 或者sendMessage进行发送消息,由于其最终会调用 sendMessageDelayed,咱们能够经过
runnable 方式或者重写handleMessage进行消息的处理,固然若是经过 handler.sendMessage(msg) 的方式的话,咱们
能够实现Callback接口达到消息的处理。
4.为何不能在子线程更新UI?其实更准确的来讲应该是UI只能在建立UI的线程中进行更新,也就是主线程,若是子线程建立UI,其能够在子线程进行更新。
复制代码
转自 :www.cnblogs.com/devinzhang/…
方法一:Thread
new Thread( new Runnable() {
public void run() {
myView.invalidate();
}
}).start();
复制代码
分析: 能够实现功能,刷新UI界面。可是这样是不合理的,由于它违背了单线程模型:Android UI操做并非线程安全的,而且这些操做必须在UI线程中执行。
方法二 :Thread + Handler
Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
myView.invalidate();
break;
}
super.handleMessage(msg);
}
};
复制代码
class MyThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
Message message = new Message();
message.what = 0;
myHandler.sendMessage(message);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
复制代码
Handler来根据接收的消息,处理UI更新。Thread线程发出Handler消息,通知更新UI。
方法三:TimerTask + Handler 实现 Timer 功能
public class TestTimer extends Activity {
Timer timer = new Timer();
Handler handler = new Handler(){
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
setTitle("狗哥最帅");
break;
}
super.handleMessage(msg);
}
};
TimerTask task = new TimerTask(){
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timer.schedule(task, 10000);
}
}
复制代码
方法四:Runnable + Handler.postDelayed(runnable,time)
private Handler handler = new Handler();
private Runnable myRunnable= new Runnable() {
public void run() {
if (run) {
handler.postDelayed(this, 1000);
count++;
}
tvCounter.setText("Count: " + count);
}
};
复制代码
这里面的内容有我去年初学的时候在 CDSD上写的一些,在这里我不得不吐槽一下,因为CSDN社区真的是各类广告太多了,看个文章还要付钱,受不了,因此转战掘金社区,个人csdn原文地址:blog.csdn.net/MrZhao_Perf… ,这篇也算是一个总结与补充吧。 OK了,Handler 机制原理经过源码的方式给你们说了个大概,若是你们有什么疑问或者能够留言给我。