Android基础知识 Handler相关

https://juejin.im/post/5ba657e0f265da0a8b5722c2
一、Handler机制之ThreadLocal

ThreadLocal 是线程内部的数据存储类,通过它可以在指定线程中存储数据。
数据存储后,只有在指定线程中可以获取到存储的数据。

不同线程访问同一个ThreadLocal 对象,获取到的值是不一样的。
原因:

不同线程访问同一个ThreadLocal的get()方法,
ThreadLocal 内部会从各自线程中取出一个数组 ThreadLocalMap.table,
然后从数组中根据当前ThreadLocal 的索引,去查找出对应的value值。
不同线程中的数组是不同的,所以获取到的值也是不一样的。

每个线程中都有 ThreadLocalMap threadLocals,内部是一个哈希表(数组)
ThreadLocal对象的哈希值作为哈希表索引 ,数组对应索引的位置存储了值

Thread模型

ThreadLocal1
Thread1
Map threadLocals
key ThreadLocal1
value 线程1的值
Thread2
Map threadLocals
key ThreadLocal1
value 线程2的值
ThreadLocal1#get
Thread1#threadLocals#get<ThreadLocal1>
返回 线程1的值
ThreadLocal1#get
Thread2#threadLocals#get<ThreadLocal1>
返回 线程2的值
ThreadLocal1#get
Thread#currentThread
#threadLocals#get<ThreadLocal1>
public class ThreadLocal<T> {

	/**
	 * get()查询的源数据,来自于调用线程,是与线程绑定的。
	 */
	public T get() {
		Thread t = Thread.currentThread();//获取调用线程
		//获取调用线程的 ThreadLocalMap = t.threadLocals
		ThreadLocalMap map = getMap(t);
		if (map != null) {
			//从 ThreadLocalMap 中 获取 key 为 ThreadLocal 对象的value值。
			ThreadLocalMap.Entry e = map.getEntry(this);
			if (e != null) {
				@SuppressWarnings("unchecked")
				T result = (T)e.value;
				return result;
			}
		}
		return setInitialValue();
	}
	
	public void set(T value) {
		//先获取调用线程
		Thread t = Thread.currentThread();
		//获取调用线程的 ThreadLocalMap = t.threadLocals
		ThreadLocalMap map = getMap(t);
		if (map != null)
			//key为 当前ThreadLocal 对象, 把value塞入 ThreadLocalMap 中。
			map.set(this, value);
		else
			createMap(t, value);
	}
}

static class ThreadLocalMap {

    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

    /**
     * The initial capacity -- MUST be a power of two.
     */
    private static final int INITIAL_CAPACITY = 16;

    /**
     * ThreadLocal set() 方法保存的数据,存储在这个 Entry 数组中
	 * 元素的下标为,当前ThreadLocal 对象的 哈希值算出的值。
	 * 数组元素为set(value) 传入的值。
     */
    private Entry[] table;
	
	private void set(ThreadLocal<?> key, Object value) {
		//真正存储数据的是这个数组
        Entry[] tab = table;
        int len = tab.length;
		//根据 threadLocal对象(key)的哈希值算出数组下标
        int i = key.threadLocalHashCode & (len-1);

        for (Entry e = tab[i];
             e != null;
             e = tab[i = nextIndex(i, len)]) {
            ThreadLocal<?> k = e.get();

            if (k == key) {//把保存的value,传入到Entry对象的value字段
                e.value = value;
                return;
            }

            if (k == null) {
                replaceStaleEntry(key, value, i);
                return;
            }
        }

        tab[i] = new Entry(key, value);
        int sz = ++size;
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            rehash();
    }		
	
	/**
	 * 根据 threadLocal 对象的哈希值,算出数组下标,取出数组元素 Entry对象
	 * value 就存在  Entry 对象的 value字段中
	 */
	private Entry getEntry(ThreadLocal<?> key) {
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);
    }
}

二、Handler机制之Looper、Handler、MessageQueue

1、 Handler 的作用是

将自己的引用赋值给 Message#target,然后将 Message 对象发送到 MessageQueue 中去

子线程中使用:

  • Handler 与 Looper的 关联

    Looper looper = Looper.myLooper();
    Handler handler = new Handler(looper);				
    
  • Handler 工作原理

    public class Handler {
      	//构造函数传入的 mCallback
      	final Callback mCallback;
      	public Handler(Looper looper, Callback callback, boolean async) {
      		mLooper = looper;
      		mQueue = looper.mQueue;
      		mCallback = callback;
      		mAsynchronous = async;
      	}
      	
      	/**
      	 * 发送消息,会把Runnable 塞到 Message.callback 中
      	 * 再把 Message 塞到队列
      	 * 所以 handler.post() 方法,
      	 * 是把 Runnable 塞到handler 所在线程消息队列尾部。
      	 */
      	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;
      	}
      	
      	//处理消息
      	public void dispatchMessage(Message msg) {
      		/** 
      		 * msg.callback 就是 post(Runnable) 发送的 Runnable
      		 */
      		if (msg.callback != null) {//优先执行 post()传过来的消息
      			handleCallback(msg);
      		} else {
      			if (mCallback != null) {//其次执行构造函数传入的 mCallback.handleMessage
      				if (mCallback.handleMessage(msg)) {
      					return;
      				}
      			}
      			//最后才是执行 handler的 handleMessage()方法
      			handleMessage(msg);
      		}
      	}
    }
    

handler.post(Runnable)
把Runnable 赋值给 Message.callback,然后把 Message 塞到队列尾部
所以 handler.post() 方法,是把 Runnable 塞到handler 所在线程消息队列尾部

2、 Looper
  • 1)子线程中 Looper 的使用

      Loopr.prepare();
      Handler handler = new Handler();
      Looper.loop();
    
  • 2)Looper 与 Thread 的关联
    Looper 与 Thread 通过 ThreadLocal 关联
    Looper.prepare()方法中,创建一个Looper对象,通过ThreadLocal,存到当前线程的 threadLocals 哈希表中

  • 3)Looper#loop()的作用
    死循环,将 Message 对象从 MessageQueue 中取出来,交给 Handler#dispatchMessage(Message) 方法
    不是 Handler#handleMessage(Message) 方法

    public final class Looper {
      	final Thread mThread;
      	static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
      	
      	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");
      		}
      		//创建一个Looper对象,存到当前线程的 ThreadLocal中
      		sThreadLocal.set(new Looper(quitAllowed));
      	}
      	
      	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;
      		...
      		//无限循环,从消息队列中取消息
      		for (;;) {
      			//调用的 MessageQueue 的 next() 方法
      			Message msg = queue.next(); // might block
      			if (msg == null) {
      				// No message indicates that the message queue is quitting.
      				return;
      			}
      			...
      			try {
      				//msg.target 就是把msg塞入消息队列的那个 Handler 对象
      				msg.target.dispatchMessage(msg);
      				dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
      			} finally {
      				...
      			}
      			...
      		}
      	}	
      }
    
3、 MessageQueue 的作用负责插入和取出 Message

MessageQueue是有序的单链表排序规则是,根据 Message#when 字段。
插入:MessageQueue#enqueueMessage()
取出:MessageQueue#next()
队列消息为空时,会阻塞线程。有消息时,唤醒线程。

public final class MessageQueue {
	
	/**
	 * 消息队列的插入操作:单链表插入,以when为排序依据
	 */
	boolean enqueueMessage(Message msg, long when) {
		...
		Message prev;
           for (;;) {
               prev = p;
               p = p.next;
			//按when 的值排序,小的排前面,大的排后面
               if (p == null || when < p.when) {
                   break;
               }
               if (needWake && p.isAsynchronous()) {
                   needWake = false;
               }
           }
           msg.next = p; // invariant: p == prev.next
           prev.next = msg;
		...
		if (needWake) {
			nativeWake(mPtr);//有新消息时,唤醒线程
		}
	}

	/**
	 * 读取消息,一个死循环,没有消息时,没有return 任何东西,一直保持循环阻塞阻塞线程
	 */
	Message next() {
		...
		for (;;) {
			...
			//阻塞线程 nextPollTimeoutMillis 毫秒,-1表示永久阻塞
			nativePollOnce(ptr, nextPollTimeoutMillis);
			
			
			synchronized (this) {
				...
				if (msg != null) {
					...
					msg.markInUse();
					return msg;			
				} else {
					/** 没有消息时,设置一下阻塞时间 nextPollTimeoutMillis
					 * 阻塞时间 -1 表示永久阻塞,在下一次循环时会阻塞
					 */
					// No more messages.
					nextPollTimeoutMillis = -1;
				}
				...
			}
			...
		}
	}
}

Message#when 字段,是由 MessageQueue#enqueue(Message, Long) 方法设置的。
是系统启动开始、到调用方法时的 mills + 入参 delayMills 之和,是个相对系统启动时间点的值。

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
	if (delayMillis < 0) {
		delayMillis = 0;
	}
	return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

SystemClock#uptimeMillis():自系统启动开始到调用该方法时相差的毫秒数

这里不用System.currentTimeMills(),修改系统时间会影响这个值,所以不可靠
因为它表示从1970-01-01 00:00:00 到当前时间的毫秒值。

4、 Handler、Looper、ThreadLocal、线程、MessageQueue 关系

各个对象的从属关系

Thread { 
	//每个线程类都有的数组:thread.threadLocals,内部包含一个Entry[]
	ThreadLocalMap {
		//Entry数组的下标是ThreadLocal的哈希值算出
		/* Entry数组中存了个 Looper 对象,
		 * Looper在数组中下标是,Looper类中的静态变量sThreadLocal 的哈希值算出的
		 */
		ThreadLocal : Looper {
					//Looper中由一个单链表消息队列
							MessageQueue {
								//消息队列中装了由handler塞入的消息Message
								Message {
							     //Message的target字段持有消息发送者handler的引用
									target ->Handler
								}
							}
					  }
	}
}

Handler {
	//handler中持有当前线程的looper对象引用
	currentThread.Looper
}

Looper == Thread # threadLocals # table [ ThreadLocal # threadLocalHashCode ]
线程的 threadLocals 哈希表中,存了一个 Looper 对象,Looper.prepare()方法中存进去的
Looper { MessageQueue [ Message#target == Handler ] }
Looper 中包含一个 MessageQueue 单链表有序队列,用于存取 Message
Message#target 用于保存发送消息的 Handler 的引用
Handler # Looper

Handler 中,持有当前线程的哈希表中 Looper的引用
所以,在任何非创建Handler 的线程,
只要持有 Handler 的引用,就可以给Handler所属线程的Looper的消息队列中塞入消息。
轮询取出消息后,通过 msg.target.dispatchMessage(msg),把消息交由handler处理。

比如:

1)在线程 A 创建了 Handler 对象,Handler 中持有线程 A 的 Looper 对象。
线程 A 在一直不停的轮询 looperA.messageQueueA。
线程A [ handlerA , looperA ]

2)线程 B 中,拿到 handlerA 的引用。
线程 B 中,执行完耗时操作,比如网络请求后,创建一个 MessageB
线程 B 中,handlerA.sendMessage(MessageB),把消息塞到了专属于线程A 的looperA的消息队列中。

3)线程 A 轮询 looperA.messageQueueA,取出了 MessageB
MessageB.target.dispatchMessage(MessageB)

线程 A 的 dispatchMessage 方法中,根据 MessageB 传递的消息,执行操作,比如更新UI。

三、Handler机制之Message的发送、取出

1、 发送消息的方法:
sendMessage(Message msg)
sendMessageDelayed(Message msg, long updateMills)
post(Runnable r)
postDelayed(Runnable r, long updateMills)
sendMessageAtTime(Message msg, long when)

sendEmptyMessage(int what)
sendEmptyMessageDelayed(int what, long updateMills)
sendEmptyMessageAtTime(int what, long updateMills)	
2、 取出消息
Looper.loop()
 -> MessageQueue.next()
  -> Message.target.dispatchMessage()
   -> handler.handleMessage()

四、Handler 中 Message 的回收机制

1、从消息池中获取消息

Message中 单链表对象池MAX_POOL_SIZE = 50
static Message sPool 全局共用

public final class Message implements Parcelable {
	public static final Object sPoolSync = new Object();
	
	//Message 单链表对象池,静态的,为了全局共用
	private static Message sPool;
	
	private static int sPoolSize = 0;

	private static final int MAX_POOL_SIZE = 50;
	
	
	public static Message obtain() {
		synchronized (sPoolSync) {
			if (sPool != null) {
				Message m = sPool;
				sPool = m.next;
				m.next = null;
				m.flags = 0; //重新标识当前Message没有使用过
				sPoolSize--;
				return m;// 从对象池中取出头部的 Message
			}
		}
		return new Message();
	}
}
2、回收消息到消息池

Message # recycleUnchecked()

public final class Message implements Parcelable {
	public static final Object sPoolSync = new Object();

	//Message 单链表对象池,静态的,为了全局共用
	private static Message sPool;
	
	private static int sPoolSize = 0;

	private static final int MAX_POOL_SIZE = 50;	

	public void recycle() {
		if (isInUse()) {
			...
			return;
		}
		recycleUnchecked();
	}

	void recycleUnchecked() {

		//用于表示当前 Message消息,已经被使用过了
		flags = FLAG_IN_USE;

		//清空之前 Message 数据
		what = 0;
		arg1 = 0;
		arg2 = 0;
		obj = null;
		replyTo = null;
		sendingUid = -1;
		when = 0;
		target = null;
		callback = null;
		data = null;
		
		//判断当前消息池中的数量是不是小于最大数量,其中 MAX_POOL_SIZE=50
		synchronized (sPoolSync) {
			if (sPoolSize < MAX_POOL_SIZE) {
				next = sPool;
				sPool = this;
				sPoolSize++;//记录当前消息池中的数量
			}
		}
	}
}

五、Handler机制之 循环消息队列的退出

Loop.loop() 不会自动退出。
MessageQueue 空了以后,会阻塞等待。

可以通过 Looper#quit() 或者 Looper#quitSafely() 退出

六、Handler机制之内存泄漏

Message#target == handler

引用链: Activity <- Handler <- Message

handler.postDelay()
退出页面时,没有把handler里对应消息清空,会导致 Activity引用被持有无法释放,导致内存泄露。

Handler 在使用时,如果直接采用内部类,有可能会导致内存泄漏。

Handler内存泄漏原因:内部类 Handler拥有外部类 Activity的引用,且不能保证消息的发送是否有较长延时。

解决Handler内存泄漏的主要方法有:
采用静态内部类+弱引用
当外部类生命周期结束时,清空消息


七、Android中提供的对象池

1、同步对象池
android.support.v4.util.Pools$SynchronizedPool
2、简单对象池
android.support.v4.util.Pools$SimplePool
3、使用
public class MyPooledClass {
	 
	  //声明对象池的大小
	private static final SynchronizedPool<MyPooledClass> sPool =
		  new SynchronizedPool<MyPooledClass>(10);
			  
	  //从对象池中获取数据,如果为null,则创建
	public static MyPooledClass obtain() {
		MyPooledClass instance = sPool.acquire();
		return (instance != null) ? instance : new MyPooledClass();
	}
	  
	  //回收对象到对象池中。当然你也可以清除对象的状态
	public void recycle() {
		// 清除对象的状态,如果你自己需要的话,
		sPool.release(this);
	}
}


八、Handler机制之IdleHandle的理解及使用

looper 中 message 暂时处理完了,这个时候会回调 IdleHandler # queueIdle() 接口
返回false,执行后会移除该 IdleHandler
返回true,下次 message 处理完的时候,继续回调

public final class MessageQueue {

	private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
	
	public static interface IdleHandler {
		/**
		 * 在looper里面的message暂时处理完了,这个时候会回调这个接口,
		 * 如果回调实现方法中返回false,那么就会移除它,
		 * 回调方法中返回true,就会在下次message处理完了的时候继续回调
		 */
		boolean queueIdle();
	}
	
	Message next() {
		... //处理 messageQueue 中消息
		for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
			// 返回false,从 mIdleHandlers 中移除
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
	}
}

IdleHandler 使用:

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
	@Override
	public boolean queueIdle() { 
	    //在Looper.messageQueue 中消息执行完之后,回调该方法
		return false;//返回false,执行完上面代码后,从 mIdleHandlers 中移除
	}
});

我们的 onResumemeasure, layout, draw都是一个个 message 的话,
这个IdleHandler就提供了一个它们都 执行完毕的回调了。

1、在UI绘制完成后执行操作

我们在主线程的 Looper.messageQueue 中添加一个 IdleHandler,
那么在主线程的消息都执行完之后,会执行 IdleHandler 代码。
从而实现,在UI绘制完成时执行一些工作。

2、结合 HandlerThread 使用
//单线程操作完毕后通知外部
public AbsFavoriteModel() {        
	if (sThread == null) {
		sThread = new HandlerThread("favorite-model");
		sThread.start();
	}
	mHandler = new Handler(sThread.getLooper());        
	try {
		Field field = Looper.class.getDeclaredField("mQueue");
		field.setAccessible(true);
		MessageQueue queue = (MessageQueue) field.get(sThread.getLooper());
		queue.addIdleHandler(new MessageQueue.IdleHandler() {                

		   @Override
		   public boolean queueIdle() {                    
		   if (mListeners != null){                        
				for (DataChangeListener<T> mListener : mListeners) {
					mListener.onDataChange(new ArrayList<>(mData));
				 }
			}                    
		   return true;//返回true,用完后下次还要用,不移除
	   } });
	} catch (Exception e) {
		e.printStackTrace();
	}
}

推荐阅读:

《Android开发艺术探索》第十章 10.2节 Android的消息机制
IdleHandle的奇思妙想----->你知道android的MessageQueue.IdleHandler吗?https://mp.weixin.qq.com/s/KpeBqIEYeOzt_frANoGuSg
Handler面试常问题目----->你真的懂Handler吗?Handler问答 https://www.jianshu.com/p/f70ee1765a61
如果大家不嫌弃,可以看看我的Handler机制总结----->Anroid Handler机制总目录 https://juejin.im/post/5ba65f57e51d4539701e47d6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值