参考来源
参考来源
参考来源
参考来源
Handler机制(面试版)
Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信
handler机制是android系统运行的基础,它采用生产者,消费者模式进行设计。其中生产者和消费者都是handler,多个handler会生产消息message投递到线程共享的messagequeue有序单链表里面,再由线程共享looper进行消费,将message消息dispatch到其指定的handler进行处理。
无论是activity/service/fragment的生命周期都基于handler机制运作,ui视图刷新/动画系统播放也是通过handler进行同步刷新,手机屏幕的触摸事件也是基于handler机制进行响应分发。
结构
- Message:代表行为What或者动作Runnable,每个消息在加入消息队列时,都有目标Handler
- ThreadLocal:线程本地存储区TLS,每个线程都有自己私有的本地存储区域,不同线程间彼此不能访问对方的TLS区域,
- MessageQueue:以单向链表形成队列来存取消息
- Looper:从队列中循环取出消息并交给Handler
- Handler:消息的发送,处理,获取,移除等
Handler内存泄漏
由于Handler支持延迟消息,而handler可能持有activity的引用,若延迟期间activity关闭则会导致内存泄漏
静态内部类+弱引用+Handler.removeCallbacksAndMessages(null)移除所有消息
Message获取
Message message = myHandler.obtainMessage();//通过 Handler 实例获取
Message message1 = Message.obtain();//通过 Message 获取
Message message2 = new Message(); //直接创建新的 Message 实例
public static Message obtain(Handler h) {
//调用下面的方法获取 Message
Message m = obtain();
//将当前 Handler指定给 message 的 target ,用来区分是哪个 Handler 的消息
m.target = h;
return m;
}
//从消息池中拿取 Message,如果有则返回,否则创建新的 Message
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
//从池子中取出空消息
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Handler发送消息
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//Handler,都是最后调用sendMessageDelayed,通过enqueueMessage将消息入队
sendEmptyMessage(int)
-> sendEmptyMessageDelayed(int,int)
-> sendMessageAtTime(Message,long)
-> enqueueMessage(MessageQueue,Message,long)
-> queue.enqueueMessage(Message, long);
Handler发送消息
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println(msg.what);
}
};
Handler机制
Android采用的是Linux的管道通信,在管道两端,分别是两个打开文件描述符,用于读写
消息队列创建时,通过JNI初始化NativeMessageQueue对象,NativeMessageQueue会初始化Looper对象,Looper使主线程在消息队列中没有消息时,进入等待状态,有消息时唤醒主线程来处理消息
在Handler机制中,Looper.loop方法会不断循环处理Message,其中消息的获取是通过 Message msg = queue.next(); 方法获取下一条消息。该方法中会调用nativePollOnce()方法,这便是一个native方法,再通过JNI调用进入Native层,在Native层的代码中便采用了管道机制。
我们知道线程之间内存共享,通过Handler通信,消息池的内容并不需要从一个线程拷贝到另一个线程,因为两线程可使用的内存是同一个区域,都有权直接访问,当然也存在线程私有区域ThreadLocal(这里不涉及)。
Handler机制中管道作用就是当一个线程A准备好Message,并放入消息池,这时需要通知另一个线程B去处理这个消息。线程A向管道的写端写入数据1(对于老的Android版本是写入字符W),管道有数据便会唤醒线程B去处理消息。
管道主要工作是用于通知另一个线程的,这便是最核心的作用。
常用API
//消息
Message message = Message.obtain();
//发送消息
new Handler().sendMessage(message);
//延时1s发送消息
new Handler().sendMessageDelayed(message, 1000);
//发送带标记的消息(内部创建了message,并设置msg.what = 0x1)
new Handler().sendEmptyMessage(0x1);
//延时1s发送带标记的消息
new Handler().sendEmptyMessageDelayed(0x1, 1000);
//延时1秒发送消息(第二个参数为:相对系统开机时间的绝对时间,而SystemClock.uptimeMillis()是当前开机时间)
new Handler().sendMessageAtTime(message, SystemClock.uptimeMillis() + 1000);
//避免内存泄露的方法:
//移除标记为0x1的消息
new Handler().removeMessages(0x1);
//移除回调的消息
new Handler().removeCallbacks(Runnable);
//移除回调和所有message
new Handler().removeCallbacksAndMessages(null);
总结
- 一个线程只有一个Looper,但可对应多个Handler,当通过Looper.prepare方法创建Looper时会调用ThreadLocal的get()方法来检查ThreadLocalMap中是否已经set过Looper。
- ActivityThread中的main()已经对Looper进行了prepar()操作,已经可以通过getMainLooper获取
- quitSafely()会调用MessageQueue的quit()方法,清空所有的Message,并调用nativeWake()方法唤醒之前被阻塞的nativePollOnce(),使得方法next()方法中的for循环继续执行,接下来发现Message为null后就会结束循环,Looper结束。如此便可以释放内存和线程。
- 使用Message的obtain()方法创建,直接new出来容易造成内存抖动。内存抖动是由于频繁new对象,gc频繁回收导致,而且由于可能被别的地方持有导致无法及时回收所以会导致内存占用越来越高。使用obtain()对内存复用,可以避免内存抖动的发生。其内部维护了一个Message池,其是一个链表结构,当调用obtain()的时候会复用表头的Message,然后会指向下一个。如果表头没有可复用的message则会创建一个新的对象,这个对象池的最大长度是50。
- 如果此时消息队列为空,不会执行,会计算消息需要等待的时间,等待时间到了继续执行
- 可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程)
- 添加消息的方法enqueueMessage()中有synchronize修饰,取消息的方法next()中也有synchronize修饰。
- 由于上述的加锁操作,所以时间不能保证完全准确。
拓展:
- handler.removeMessage(0)能够移除所有runable
- activity被回收时需要移除handler所有消息
- 有时候会发现removeCallbacks会失效,不能从消息队列中移除。出现这情况是activity切入后台,再回到前台,此时的runnable由于被重定义,就会和原先的runnable并非同一个对象。所以这么做,加上static即可
- message的设计模式:享元模式(多复用)、命令模式(指令封装为对象,解耦调用者和执行者,但可能导致产生过多指令类)
- messagequeue/looper是线程唯一共享的,即位于ThreadLocal中,ThreadLocalMap中存放着内部成员ThreadLocalMap。通过Entry类型的数组保存变量。Entry是一个静态内部类,继承了WeakReference。它主要记录了两个信息,一个是ThreadLocal<?>类型,一个是Object类型的值。
- looper.loop是个死循环,messagequeue.next也是个死循环,如果messagequeue取不到消息的时候,不会让渡cpu,这样才有往下执行idlehandler的基础。
- messagequeue里面有一个闲时处理器,idlehandler的机制,当从messagequeue取不到消息的时候会执行idlerhandler如果从messagequeue中取不到消息,会继续往下执行判断是否有idlehandler,如果有就从里面获取最多5个idlehandle并执行,设置循环等待标识0,继续尝试获取消息;如果没有任何idlehandle就设置循环等待标识-1,自循环等待消息。
- nativepollonce会阻塞是真的,不过有超时条件,也就是上面提到的循环等待标识设置,0标识查询没有可用(ready)的message就立即返回,-1表示等待直到有可用消息才返回,其他表示超时等待时间,一般是下一个消息when减去当前now时间,超时即返回。
- 最后epoll是基于linux内核工作,多路复用体现在1个内核线程监控多个fd状态,epoll比较高效是因为被监控fd状态ready了才会被查询到,从而得到处理。
- 从设计模式角度看这个问题,完完全全就是多生产者单消费者模式;这个模式典型应用在哪些场景?android里面的toast的实现就很经典了。
- 如果这个handler再搭配上子线程,我们会发现internservice就是一个典型的应用场景,先处理完一个intern,然后再处理下一个,单线程,有序地进行。
- idlehandler也有用于启动加速的,不过系统里面更典型的应用是activity的destroy生命周期调度,在下一个activity启动之后再通过idlehandler进行上一个destroy的调度,搭配上10s钟超时强制执行destroy,这也是为什么activity回收必然是10s内的原因。
- binder通信里面也不缺乏handler的应用。
HandlerThread
HandlerThread比Thread多了一个Looper
基本使用
public class MainActivity extends AppCompatActivity {
private HandlerThread thread;
static Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建一个HandlerThread并启动它
thread = new HandlerThread("MyHandlerThread");
thread.start();
//使用HandlerThread的looper对象创建Handler
mHandler = new Handler(thread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//这个方法是运行在 handler-thread 线程中的,可以执行耗时操作,因此不能更新ui,要注意
if (msg.what == 0x1) {
try {
Thread.sleep(3000);
Log.e("测试: ", "执行了3s的耗时操作");
} catch (InterruptedException e) {
e.printStackTrace();
}
//这个方法是运行在 handler-thread 线程中的,可以执行耗时操作,因此不能更新ui,要注意
// ((Button) MainActivity.this.findViewById(R.id.button)).setText("hello");
}
return false;
}
});
//停止handlerthread接收事件
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
thread.quit();
}
});
//运行
mHandler.sendEmptyMessage(0x1);
}
}
使用注意
- handlerthread等同子线程,能做耗时操作,但不能更新UI
- 需要通过handlerthread.quit()或handlerthread.quitSafely()停止接收新的任务事件,但哪怕调用后不会停止handlerthread此次任务,会停止下一轮任务的接受
- quit()和quitSafely(),前者实际执行了MessageQueue中的removeAllMessagesLocked,将消息池中所有消息都清空,后者则是调用了removeAllFutureMessagesLocked,清空延迟消息,把所有的非延迟消息都发出去
Handler源码解析
handler
前面提到main函数中会创建主线程的handler,是通过调用Looper.prepareMainLooper(),其内部调用prepare()来实例化Looper,Looper实例化的同时创建了messageQueue,主线程的handler则与此Looper关联
public class Handler {
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback; //回调
final boolean mAsynchronous; //是否异步消息
IMessenger mMessenger;
public interface Callback {
//如果不需要进一步的处理,则返回True
public boolean handleMessage(Message msg);
}
//有参构造
public Handler(Looper looper) {
this(looper, null, false);
}
//有参构造
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Callback callback, boolean async) {
//匿名类、内部类或本地类都必须申明为static,否则会警告可能出现内存泄露
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());
}
}
//从Looper类中的(ThreadLocal)获取Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("");
}
mQueue = mLooper.mQueue; //Looper取出消息队列
mCallback = callback; //回调
mAsynchronous = async; //设置消息是否为异步处理方式
}
Message
常用的字段就是what、bundle
public final class Message implements Parcelable {
//用户定义的消息代码,以便接收者能够识别
public int what;
//arg1和arg2是使用成本较低的替代品-也可以用来存储int值
public int arg1;
public int arg2;
//存放任意类型的对象
public Object obj;
//消息触发时间
long when;
//消息携带内容
Bundle data;
//消息响应方
Handler target;
//消息管理器,会关联到一个handler
public Messanger replyTo;
//回调方法
Runnable callback;
//消息存储的链表。这样sPool就成为了一个Messages的缓存链表。
Message next;
//消息池
private static Message sPool;
//消息池的默认大小
private static final int MAX_POOL_SIZE = 50;
//从消息池中获取消息
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool; //从sPool的表头拿出Message
sPool = m.next; //将消息池的表头指向下一个Message
m.next = null; //将取出消息的链表断开
m.flags = 0; // 清除flag----flag标记判断此消息是否正被使用(下方isInUse方法)
sPoolSize--; //消息池可用大小进行减1
return m;
}
}
return new Message(); //消息池为空-直接创建Message
}
//通过标记判断消息是否正被使用
boolean isInUse() {
return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
//5.0后为true,之前为false.
private static boolean gCheckRecycle = true;
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it is still in use.");
}
return;
}
recycleUnchecked(); //消息没在使用,回收
}
//对于不再使用的消息,加入到消息池
void recycleUnchecked() {
//将消息标示位置为IN_USE,并清空消息所有的参数。
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this; //当消息池没有满时,将Message加入消息池
sPoolSize++; //消息池可用大小加1
}
}
}
Looper
Looper的创建
这里注意主线程和子线程的区别,主线程通过prepareMainLooper(),子线程则是prepare()
主要是prepare(false)和prepare(true)的区别,后面则都是将其放入threadlocal中
public final class Looper {
//内部消息队列MessageQueue
final MessageQueue mQueue;
//Looper所在的线程
final Thread mThread;
//Looper的变量存储
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//主looper
private static Looper sMainLooper;
//私有构造方法,不能通过New实例化。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建与其绑定的消息队列MessageQueue
mThread = Thread.currentThread(); //绑定当前线程
}
//子线程的调用----->最终通过prepare(boolean)实例化Looper
public static void prepare() {
prepare(true);
}
//主线程的调用----->最终通过prepare(boolean)实例化Looper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();//存储区中looper作为主looper
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//quitAllowed代表是否允许退出,主线程调用为不允许退出,子线程为可退出
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
//看出一个线程只能存在一个Looper-->则调用二次Looper.prepare抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//Looper的变量存储+实例化Looper
}
Looper的启动
取出looper,从其中拿到消息队列
再从消息队列中拿到消息,向其target,即对应的handler调用dispatchMessage,最后回收message
public static void loop() {
final Looper me = myLooper(); //从存储区拿出looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //获取Looper对象中的消息队列
......
//进入loop的主循环方法
for (;;) {
Message msg = queue.next(); //可能会阻塞
if (msg == null) { //没有消息,则退出循环
return;
}
......
//target是handler,此处用于分发Message
msg.target.dispatchMessage(msg);
......
msg.recycleUnchecked(); //将Message放入消息池
}
}
public void quit() {
mQueue.quit(false);
}
Message
Message消息队列创建
看得出消息队列主要的操作都是依赖native来实现
public final class MessageQueue {
//供native代码使用
@SuppressWarnings("unused")
private long mPtr;
//交给native层来处理的核心方法
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis); //阻塞操作
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
Message mMessages;
//消息队列是否可以退出
private final boolean mQuitAllowed;
//构造方法
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit(); //通过native方法初始化消息队列,其中mPtr是供native代码使用
}
Message.next
//不停提取下一条message
Message next() {
final long ptr = mPtr;
//判断是否退出消息循环
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1;
//代表下一个消息到来前,还需要等待的时长
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//native层阻塞cpu。如果被阻塞,唤醒事件队列
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//如果当前消息是异步消息,都将赋值给prevMsg,过滤掉,直到取到了非异步消息
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 {
mBlocked = false;//指定为非阻塞任务
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
//设置消息的使用状态,即flags |= FLAG_IN_USE
msg.markInUse();
return msg; //成功地获取MessageQueue中的下一条即将要执行的消息
}
} else {
//表示消息队列中无消息,会一直等待下去
nextPollTimeoutMillis = -1;
}
......
//IdleHandler为发现线程何时阻塞的回调接口
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; //去除handler引用
boolean keep = false;
//queueIdle返回true会被空闲的处理器处理,false就会被移走
try {
keep = idler.queueIdle(); //idle时执行的方法
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler); //被移走
}
}
}
//重置idle handler个数为0,保证不会再次重复运行
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
Message Enqueue
按when将message插入对应位置
boolean enqueueMessage(Message msg, long when) {
// 每一个普通Message必须有一个target-handler
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//已在使用状态
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//消息在退出状态->被回收到消息池
if (mQuitting) {
msg.recycle();
return false;
}
//标记使用状态,记录执行时间
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//p为null代表MessageQueue没有消息或者msg的触发时间是队列中最早的
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; //当阻塞时需要唤醒
} else {
//将消息按时间顺序插入到MessageQueue。
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;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
Message removeMessages()和removeCallbacksAndMessages()
removeMessages():移除所有符合条件的message
emoveCallbacksAndMessages():移除所有de message文章来源:https://www.toymoban.com/news/detail-784520.html
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
//从消息队列的头部开始,移除所有符合条件的消息
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
//移除剩余的符合要求的消息
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
void removeCallbacksAndMessages(Handler h, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
Handler内存泄漏
- Activity要被回收,但thread未结束,且持有handler,handler又持有Activity,导致回收失败
- thread结束,但对应的looper内仍有消息未处理,消息又持有handler,handler持有activity,导致回收失败
解决方案:文章来源地址https://www.toymoban.com/news/detail-784520.html
- 对Activity进行弱引用
- handler作为静态内部类
- activity销毁时,移除子线程handler中所有未处理的Message:RemoveAllCallbacksAndMessages(),切断MessageQueue到Message,以及Message到Runnable的引用关系
- 暂停子线程并quit对应的looper quit()或quitSafely() 前者清除所有消息,后者清除延迟消息,发出非延迟消息
到了这里,关于Android:Handler的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!