Handler机制详解
Posted 一叶飘舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Handler机制详解相关的知识,希望对你有一定的参考价值。
相关类
1. ThreadLocal
ThreadLocal存取的变量只能被当前线程访问,其他线程则无法访问和修改。
使用场景
数据以线程为作用域,不同的线程有不同的数据副本。
各个线程往***同一个***ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的
原理
Thread中有变量ThreadLocalMap
threadLocal调用set/get方法,实际是调用的各个线程的threadLocalMap.set/getEntry,并以该local实例为索引
虽然是在不同线程调用的local
但因为存取的map变量是在Thread内部,并且local是同一个
所以,能实现存储的变量属于当前线程,对其它线程隔离
使用
<!--#:ThreadLocal-->/**
* 实际上调用的是:各个线程中的threadLocalMap.set/getEntry
*/publicvoidset(T value)
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
ThreadLocalMap getMap(Thread t)
return t.threadLocals;
//Thead中的ThreadLocalMap是在ThreadLocal中被创建的voidcreateMap(Thread t, T firstValue)
t.threadLocals = new ThreadLocalMap(this, firstValue);
--------------------
<!--#:Thread-->
ThreadLocal.ThreadLocalMap threadLocals = null;//在ThreadLocal中被初始化//以local实例为索引privatevoidset(ThreadLocal<?> key, Object value)
private Entry getEntry(ThreadLocal<?> key)
/**
虽然是在不同线程调用的local
但因为存取的map变量是在Thread内部,并且local是同一个
所以,能实现存储的变量属于当前线程,对其它线程隔离
*/
ThreadLocal<String> local = new ThreadLocal<>();
//#Thread$1
local.set("strValue");
String value = local.get();
//#Thread$2
local.set("strValue");
String value = local.get();
复制代码
2. HandlerThread
Android多线程:HandlerThread详细使用手册
Android多线程:一步步带你源码解析HandlerThread
原理: Handler机制 + Thread
是Thread
通过继承Thread类,快速地创建1个带有Looper对象的新工作线程
通过新Handler使用该线程的Looper,在该Handler中处理任务,使工作任务在子线程中执行
HandlerThread实现
# HandlerThread:
@Override
public void run()
Looper.prepare();
synchronized (this)
//这步才是最主要的,让处理耗时操作的workHandler使用mLooper,这样就能在当前的子线程中处理workHandler的耗时操作了。
mLooper = Looper.myLooper();
notifyAll();
//空方法,loop前的准备onLooperPrepared();
Looper.loop();
使用
protected void onCreate()
HandlerThread handlerThread = newHandlerThread("word_handler");
handlerThread.start();
//使用子线程的Looper,所以workHandler中的耗时操作在子线程中执行WorkHandler workHandler = newWorkHandler(handlerThread.getLooper());
mainHandler = newMainHandler();
Message msg = Message.obtain();
msg.what = 1;
workHandler.sendMessage(msg);//work发送消息
//和HandlerThread同线程,处理工作任务
public classWorkHandlerextendsHandler
@Override
public void handleMessage(Message msg)
Message messag = Message.obtain();
messag.obj = "from word Handler";
mainHandler.sendMessage(messag);//通知主线程
classMainHandlerextendsHandler
public void handleMessage(Message msg)
tvTest.setText((String)msg.obj);
封装HandlerThread
protected void initData()
mainHandler = newMainHandler();
myHandlerThread = newWorkHandlerThread("my_handler_thread");
myHandlerThread.start();
@OnClick(R.id.btn_start)
public void onViewClicked(View view)
Message msg = Message.obtain();
msg.what = 1;
myHandlerThread.getWorkHandler().sendMessage(msg);
/**
* 当前线程
*/classMainHandlerextendsHandler
@Override
public void handleMessage(Message msg)
tvTest.setText((String)msg.obj);
/**
* 工作:处理耗时任务
*/classWorkHandlerThreadextendsMyHandlerThread
public WorkHandlerThread(String name) super(name);
@Override
public void workMessage(Message msg)
Thread.sleep(2000);
Message messag = Message.obtain();
messag.obj = "from word My WordHandler";
mainHandler.sendMessage(messag);
/**
* 封装基类
*/abstractclassMyHandlerThreadextendsHandlerThread
privateWorkHandler mWorkHandler;
@Overrideprotected void onLooperPrepared()
mWorkHandler = newWorkHandler(getLooper());
public WorkHandler getWorkHandler()return mWorkHandler;
public abstract void workMessage(Message msg);
classWorkHandlerextendsHandler
public WorkHandler(Looper looper)
super(looper);
@Override
public void handleMessage(Message msg)
super.handleMessage(msg);
workMessage(msg);
IdleHandler
你知道android的MessageQueue.IdleHandler吗?
是 Handler 机制提供的一种,可以在 Looper 事件循环的过程中,当出现空闲的时候,允许我们执行任务的一种机制
Message next()
intpendingIdleHandlerCount= -1; // -1 only during first iterationintnextPollTimeoutMillis=0;
for (;;)
synchronized (this)
finallongnow= SystemClock.uptimeMillis();
MessageprevMsg=null;
Messagemsg= mMessages;
if (msg != null)
if (now < msg.when)
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
else
// Got a message.return msg;
else
// No more messages.
nextPollTimeoutMillis = -1;
// If first time idle, then get the number of idlers to run.// Idle handles only run if the queue is empty or if the first message// in the queue (possibly a barrier) is due to be handled in the future.//第一次时满足<0条件if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when))
pendingIdleHandlerCount = mIdleHandlers.size();
//执行下面的for循环一次后,置为0,后面再到此处不再往下执行if (pendingIdleHandlerCount <= 0)
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
// We only ever reach this code block during the first iteration.//只有第一次执行能到达for (inti=0; i < pendingIdleHandlerCount; i++)
finalIdleHandleridler= mPendingIdleHandlers[i];
booleankeep=false;
try
keep = idler.queueIdle();
catch (Throwable t)
if (!keep)
synchronized (this)
mIdleHandlers.remove(idler);
// Reset the idle handler count to 0 so we do not run them again.//置为0,后面不再满足上面条件进入for循环
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
调用时机:消息队列empty或延迟消息还未执行
如果queueIdle返回true,会一直有idleHandler,为什么不会导致空闲时next在for循环的过程中,一直重复执行
在最后pendingIdleHandlerCount=0,不再满足条件
使用场景
启动页面优化
onResume在wm.addView之前执行,可以将不必要的操作在页面绘制完成之后执行
减少onResume中执行时间,达到页面启动优化
消息机制
Message中obtain()与recycle()的来龙去脉-系列
一、概述
什么是消息机制
android的消息机制主要是指Handler的运行机制
即:消息的发送、入队、出队、分发过程。
为什么需要消息机制
Android规定访问UI只能在主线程中进行,在子线程中访问UI就会抛异常。但是Android又不建议在主线程中做耗时操作,会可能导致ANR。所以,我们需要,能在子线程中做完耗时操作,然后去到主线程更新UI的办法。
Hander的主要作用是将一个任务切换到指定的线程中去执行。因此,系统提供Handler主要是***为了解决在子线程中无法访问UI的问题***(而不是把耗时操作放到子线程中的问题)。
单线程模型准则
在单线程模型中始终要记住两条法则:
1、不要阻塞UI线程
2、确保只在UI线程中访问UI
二、原理
分发机制:
Handler通过sendMessage()发送Message到MessageQueue队列;
Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理;
经过dispatchMessage()后,交回给Handler的handleMessage()或者runnable来进行相应地处理。
2.1 消息的发送、入队:
handler中有众多的send方法,时间点的区别而已,到最后都会调用下面的方法,把message放入消息队列
MessageQueue是链表结构
//消息发送
<!--#Handler-->
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
//注意此处,this指的是handler自己
msg.target = this;
//把消息插入队列
return queue.enqueueMessage(msg, uptimeMillis);
//入队
<!--#MessageQueue-->
boolean enqueueMessage(Message msg, long when)
synchronized (this)
msg.markInUse();msg.when = when;
Message p = mMessages;
boolean needWake;
//如果队列为空或者时间最小,插入到头部
if (p == null || when == 0 || when < p.when)
msg.next = p;mMessages = msg;needWake = mBlocked;
else
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//循环队列,根据时间找到合适的位置插入
for (;;) prev = p;p = p.next;
if (p == null || when < p.when)
break;
if (needWake && p.isAsynchronous())
needWake = false;
msg.next = p; // invariant: p == prev.nextprev.next = msg;
if (needWake)
nativeWake(mPtr);
return true;
2.2 消息的出队、分发:
loop将队列中的msg一个一个取出,分发到各自的handler中处理。
handler根据是否有callback选择不同的分发方式。
//出队
<!--#Looper-->
publicstaticvoidloop()
finalLooperme= myLooper();
finalMessageQueuequeue= me.mQueue;
//真正决定阻塞的是queue.next()中的for循环for (;;)
//next方法会阻塞线程,有消息就取出,没消息就等待。退出返回null结束当前循环。Messagemsg= queue.next(); // might blockif (msg == null)
// No message indicates that the message queue is quitting.//Looper.quit退出循环return;
//加入消息队列时,target == handler,分发的时候就可以找到原来的handler,让其自己处理。//所以,一个线程多个handler发送消息,虽然都在一个队列里,但是还是会分发到原来的handler处理消息。
msg.target.dispatchMessage(msg);
//取出消息
<!--#MessageQueue-->
Message next()
//阻塞线程for (;;)
synchronized (this)
// Try to retrieve the next message. Return if found.finallongnow= SystemClock.uptimeMillis();
MessageprevMsg=null;
Messagemsg= mMessages;
if (msg != null && msg.target == null)
//异步消息?do
prevMsg = msg;
msg = msg.next;
while (msg != null && !msg.isAsynchronous());
if (msg != null)
//还不到取出时间if (now < msg.when)
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
else
// 取出msg
mBlocked = false;
if (prevMsg != null)
prevMsg.next = msg.next;
else
mMessages = msg.next;
msg.next = null;
msg.markInUse();
return msg;
else
// No more messages.
nextPollTimeoutMillis = -1;
//Looper.quit(),返回null。//此时,Looper中的for循环收到null也return,退出循环if (mQuitting)
dispose();
returnnull;
//由target返回到Handler中执行分发//加入队列的:有的是Runnable封装的message,有的是callback,有的是message,在这里分发
<!--#Handler-->
publicvoiddispatchMessage(Message msg)
//runnable封装的message。 handler.post/view.postif (msg.callback != null) //msg中的callback 优先级1
handleCallback(msg);
else
if (mCallback != null)
//Handler(Callback callback)的时候,mCallback !=nullif (mCallback.handleMessage(msg)) //handler中的callback,优先级2return;
//正常的message那种形式,也即是new Handler时复写的方法。
handleMessage(msg);// 优先级3
复制代码
三、其它过程
3.1 子线程中初始化Handler
<!--#Looper-->
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));
public static @Nullable Looper myLooper()
return sThreadLocal.get();
<!--#Handler-->
public Handler(Callback callback, boolean async)
//验证当前线程有没有Looper对象,所以new之前要prepare存入
mLooper = Looper.myLooper();
if (mLooper == null)
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
<!--#:Thread1-->
Looper.prepare()//当前线程存
Handler handler = new Handler();//当前线程取
Looper.loop();
<!--#:Thread2-->
Looper.prepare()//当前线程存
Handler handler = new Handler();//当前线程取
Looper.loop();
<!--#:Thread3-->
Looper.prepare()//当前线程存
Handler handler = new Handler();//当前线程取
Looper.loop();
主线程 Handler
为什么主线程 new Handler之前不用prepare,也不用loop?
UI线程是主线程,系统已经自动帮我们调用了Looper.prepare()和Looper.loop()方法
3.2 优先级:依次降低。
优先级1:
<!--封装成Message,放入队列-->
//view.post(runnable)和handler.post(runnable)
//最后runnable都会分装成Message。分发的时候,在第一处调用。
private static Message getPostMessage(Runnable r)
Message m = Message.obtain();
m.callback = r;
return m;
<!--分发调用-->
private static void handleCallback(Message message)
message.callback.run();
注意:虽然是runnable,但并不是新的线程。
此处的run()就是正常的方法,Runnable中不要做耗时操作。
优先级2:
publicHandler(Callback callback, boolean async)
...
mCallback = callback;
...
优先级3:
常用的发送Message方式,设置what,obj参数,发送的消息。
3.3 退出
loop.quit() --> mQueue.quit(false)
loop.quitSafely() --> mQueue.quit(true)
<!--#MessageQueue-->
voidquit(boolean safe)
//主线程时,quit报错if (!mQuitAllowed)
thrownewIllegalStateException("Main thread not allowed to quit.");
synchronized (this)
//mQuitting该标记使next方法中返回null,使loop结束循环。
mQuitting = true;
if (safe)
removeAllFutureMessagesLocked();
else
removeAllMessagesLocked();
Message next()
//MessageQueue的next返回null,就能结束loop循环了if (mQuitting)
dispose();
returnnull;
publicstaticvoidloop()
finalMessageQueuequeue= me.mQueue;
for (;;)
Messagemsg= queue.next();
/*
* 不是指消息队列里没有消息了(没有消息,队列也会一直循环)
* 是调用了Loop的quit()、quitSafely()方法,结束循环
*/if (msg == null)
//退出循环return;
...
安全退出
<!--#MessageQueue-->
void quit(boolean safe)
if (safe)
removeAllFutureMessagesLocked();
else
removeAllMessagesLocked();
//回收所有消息
private void removeAllMessagesLocked()
Message p = mMessages;
while (p != null)
Message n = p.next;
p.recycleUnchecked();p = n;
mMessages = null;
//回收延迟消息
private void removeAllFutureMessagesLocked()
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null)
if (p.when > now)
removeAllMessagesLocked();
else
Message n;
for (;;) n = p.next;
if (n == null)
return;
if (n.when > now)
break;
//找到 <= now的msg ==> p
p = n;
//把p以后的消息回收
p.next = null;
do
p = n;n = p.next;
p.recycleUnchecked();
while (n != null);
quit()方法的作用:
1_标记mQuitting = true,使next方法返回null,结束循环。
2_移除消息队列中的消息。
3.4 主线程quit报错:
源码中可以看到,子线程中preaper时候,默认设置为true。
主线程中:prepareMainLooper的时候,默认是false。
所以,子线程的Loop是可以quit的,而主线程的不可以。
//app入口 ActivityThreadpublicstaticvoidmain(String[] args)
...
Looper.prepareMainLooper();
ActivityThread thread = newActivityThread();
...
Looper.loop();
publicstaticvoidprepareMainLooper()
prepare(false);
...
//初始化MessageQueue时设置MessageQueue(boolean quitAllowed)
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
voidquit(boolean safe)
//这个地方:主线程时,quit报错if (!mQuitAllowed)
thrownewIllegalStateException("Main thread not allowed to quit.");
...
3.5 runOnUiThread
public final void runOnUiThread(Runnable action)
if (Thread.currentThread() != mUiThread)
//非UI线程,到主线程的handler执行
mHandler.post(action);
else
//UI线程直接run
action.run();
3.6 内存泄露
Handler内部类引用Activity,msg.target = this引用handler,造成内存泄漏
1.退出时,remove掉handler(onStop中移除,onDestory不一定会调用)
2.静态内部类,弱引用
/**
为避免handler造成的内存泄漏
1、使用静态的handler,对外部类不保持对象的引用
2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
*/
private static class MyHandler extends Handler
private final WeakReference<Activity> mActivityReference;
MyHandler(Activity activity)
this.mActivityReference = new WeakReference<Activity>(activity);
@Override
public void handleMessage(Message msg)
super.handleMessage(msg);
MainActivity activity = (MainActivity) mActivityReference.get(); //获取弱引用队列中的activity
//若引用,判断null
if(activity != null && !activity.isFinish())
byte[] data = (byte[]) msg.obj;
activity.threadIv.setImageBitmap(activity.getBitmap(data));
break;
3.7 loop()为什么不会阻塞主线程造成ANR?
Android中为什么主线程不会因为Looper.loop()里的死循环卡死
首先,loop()会导致线程阻塞
class LooperThread extends Thread
public Handler mHandler;
public void run()
Looper.prepare();
mHandler = new Handler();
Looper.loop();
Log.d("loop()是死循环,阻塞线程,后面的代码不执行");
其次,主线程确实是阻塞的
没有loop的话,程序启动,执行完代码就结束APP就退出了
#ActivityThread:
public static void main(String[] args)
...
Looper.prepareMainLooper(); #1
ActivityThread thread = new ActivityThread(); #2
thread.attach(false);
if (sMainThreadHandler == null)
#3sMainThreadHandler = thread.getHandler();
#4
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
不会因为loop引起ANR
因为在loop处理消息的过程中,进行耗时操作才会引起ANR。
Android的各个生命周期和事件,都是在loop循环中接收、处理消息的。引起ANR是因为处理消息时阻塞了,而不是因为loop循环阻塞的。
主线程的死循环一直运行是不是特别消耗CPU资源呢?
其实不然,这里就涉及到Linux pipe/epoll机制;
简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,详情见Android消息机制1-Handler(Java层),此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。
这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。
所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
3.8 Handler的异步理解
Handler没有处理耗时操作的能力
Handler的异步只是体现在,在其它线程操作完以后发送msg,在handler线程处理收到消息后的任务。
3.9 View.post(runnable)
#HandlerActionQueue:
public boolean post(Runnable action)
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null)
//如果当前View加入到了window中,直接调用UI线程的Handler发送消息
return attachInfo.mHandler.post(action);
//View未加入到window,放入HandlerActionQueue的mActions(数组)中
getRunQueue().post(action);
return true;
#View:
void dispatchAttachedToWindow(AttachInfo info, int visibility)
//View加入window后,直接执行mActions保存的
mRunQueue.executeActions(info.mHandler);
View加入window之前post:
ViewRootImpl.getRunQueue().post(action)
getRunQueue().post(action);
7.0前后都有改动,实现runnable方式不同,不细看
View加入window之后post:直接用主线程handler发送消息
3.10 子线程一定不能更新UI吗?
有些控件支持在子线程更新,比如:SurfaceViw
在Activity的onResume()生命周期函数之前是可以在子线程中更新UI的
ViewRootImpl还未创建
更新时不会checkThread
3.11 Android实现异步的方式
继承Thread类
HandlerThread
IntentService
AsyncTask
线程池
注意事项
主线程创建Handler不用prepare,是因为系统创建主线程时已调用
Handler没有处理耗时操作的能力,只是能在处理以后通知主线程。
以上是关于Handler机制详解的主要内容,如果未能解决你的问题,请参考以下文章