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()开始循环。
  1. public static void main(String[] args) {    
  2.         SamplingProfilerIntegration.start();    
  3.     
  4.         // CloseGuard defaults to true and can be quite spammy.  We    
  5.         // disable it here, but selectively enable it later (via    
  6.         // StrictMode) on debug builds, but using DropBox, not logs.    
  7.         CloseGuard.setEnabled(false);    
  8.     
  9.         Environment.initForCurrentUser();    
  10.     
  11.         // Set the reporter for event logging in libcore    
  12.         EventLogger.setReporter(new EventLoggingReporter());    
  13.     
  14.         Process.setArgV0("<pre-initialized>");    
  15.     
  16.         Looper.prepareMainLooper();       //prepareMainLooper()
  17.     
  18.         // 创建ActivityThread实例    
  19.         ActivityThread thread = new ActivityThread();    
  20.         thread.attach(false);    
  21.     
  22.         if (sMainThreadHandler == null) {    
  23.             sMainThreadHandler = thread.getHandler();    
  24.         }    
  25.     
  26.         AsyncTask.init();    
  27.     
  28.         if (false) {    
  29.             Looper.myLooper().setMessageLogging(new    
  30.                     LogPrinter(Log.DEBUG, "ActivityThread"));    
  31.         }    
  32.     
  33.         Looper.loop();                  //开启循环
  34.     
  35.         throw new RuntimeException("Main thread loop unexpectedly exited");    
  36.     }    


这里通过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是什么, 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,Android中的ThreadLocal和java中的ThreadLocal有着一些区别。
当使用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操作。
 
   









猜你喜欢

转载自blog.csdn.net/zxc641483573/article/details/78973056