Android Handler机制和ThreadLocal的应用
Android Handler机制和ThreadLocal的应用
Handler
handler是Google参考了windows的消息机制处理机制,在Android系统中实现的一套类似的消息处理机制。相信大家在平时使用的时候都有过,thread+handler的使用经历,handler作为将信息回调回主线程的工具,为我们更新ui线程的数据信息提供了可能。
Handler,Message,Looper,MessageQueue
我们都知道Android中维护了一个ui线程,也叫主线程,当我们在其他线程中对线程中的信息进行修改时候,程序会崩溃推出。那这个ui是怎么创建起来的,这个时候就要了解一下Message,Looper和MessageQueue。
Looper:一个循环执行的循环器,内部存在一个MessageQueue,通过不断的循环不断的取出
MessageQueue队列中的Message,同时执行Message中的操作。
Message:线程间通信的数据单元
MessageQueue:消息队列,handler的post,postdelayde等函数将Message进入队列中
使用handler机制的好处
当多个线程并发运行的时候,多个程序都对ui线程进行操作的时候,会带来严重的性能下降,使用消息队列的形式,可以很好的更新信息,使消息有顺序的运行。
可能都有过这种经历,当我们在线程中创建handler时候,会报错并提示不能创建没有Looper的Handler。我们看一下ActivityThread中的main函数,可以发现,其中先运行Looper.prepareMainLooper();进行准备,然后调用Looper.loop()开始循环。
- public static void main(String[] args) {
- SamplingProfilerIntegration.start();
- // CloseGuard defaults to true and can be quite spammy. We
- // disable it here, but selectively enable it later (via
- // StrictMode) on debug builds, but using DropBox, not logs.
- CloseGuard.setEnabled(false);
- Environment.initForCurrentUser();
- // Set the reporter for event logging in libcore
- EventLogger.setReporter(new EventLoggingReporter());
- Process.setArgV0("<pre-initialized>");
- Looper.prepareMainLooper(); //prepareMainLooper()
- // 创建ActivityThread实例
- ActivityThread thread = new ActivityThread();
- thread.attach(false);
- if (sMainThreadHandler == null) {
- sMainThreadHandler = thread.getHandler();
- }
- AsyncTask.init();
- if (false) {
- Looper.myLooper().setMessageLogging(new
- LogPrinter(Log.DEBUG, "ActivityThread"));
- }
- Looper.loop(); //开启循环
- throw new RuntimeException("Main thread loop unexpectedly exited");
- }
这里通过Looper.loop()的方法,不断的从MessageQueen中取出Message并执行,通过Handler从而达到一种回调回主线程的效果。
TheardLocal
在Handler handler=new Handler()的代码中,我们发现,他并没有指定利用什么线程来进行实例化,我们打开代码可以看到,是通过Loooper.myLooper得到了当前线程的Looper实例,那么问题来了,我们如何根据当前的线程得到当前的looper实例?
Android中通过ThreadLocal来进行实现。
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(); //这里得到当前线程的looper实例 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; }
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。这样,我们可以通过ThreadLocal将不同的线程和Looper对应起来。
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
通过set方法我们可以看到,通过泛型T传入值,这里将T存储在ThreadLocal中,这样就将T存储在了ThreadLocal中,同样也可以用来存储Looper。
那Looper中又是怎么连接起来的,他又向ThreadLocal中写入了什么信息,这里我们知道刚开始创建handler的时候需要进行Looper.prepare(),那我们来到这个 类来看下,他进行了什么操作
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)); }可以看到这里对sThreadLocal进行了判空操作,sThreadLocal其实就是一个ThreadLocal对象,如果里面已经存在了对象,直接抛出异常,同时将一个新的Looper对象存入其中,这也是为什么,如果在一个线程中反复的声明prepare,会崩溃掉。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }看到这里,基本上也就清楚了,总结一下,主线程会在线程开始的时候进行一个新的Looper的创建,同时循环从MessageQuene中取出信息,当不同的线程时候为了分清楚不同的Looper,通过ThreadLocal将Looper实例存入,这样的话每一个 线程就会有一个属于自己的Looper实例,同时也解决了线程的同步问题。
这里分析一下handler中的方法。
handler.sendMessage();最后辗转到sendMessageAtTime()方法中,这里看到 这里得到了messageQueen的实例,同时进行了入队的操作 。既然入队了,那就通过lopper的循环进行调用。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }我们知道通过handler.post和handler.postdelay操作同样可以进行入队的操作,但是这两个方法都需要传入一个Runnable方法,继续看他们的代码。
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
public final boolean postDelayed(Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis); }可以看到这里都是直接调用sendMessageDelayed方法进行的操作,同时其将Runnable对象进行封装到了Message中,所以可以进行send操作。