zoukankan      html  css  js  c++  java
  • Handler使用小结

    个人概念里面handler用来更新UI。一直有一个问题困恼我,为什么我在主线程里面创建一个Handler不需要传递传递Looper,而在一个子线程里面必须调用Looper.prepare, Looper.loop。今天看了看源码,终于知道里面的原委。个人觉得一切和ThreadLocal有关,关于ThreadLocal,请阅读如下博客:Android的消息机制之ThreadLocal的工作原理。 简而言之,ThreadLocal和当前线程绑定,如果handler在UI线程里面创建,ThreadLocal已经和主线程绑定,即使handler传入为空,也可以拿到主线程的looper,代码如下,

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    我们知道,在app启动时候,在ActivityThread里面的main函数被执行:
    Looper.prepareMainLooper();
    
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    
    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();


    综合上面代码,looper已经在app启动的时候创建,所以 Looper.myLooper()取得的是UI主线程对应的looper,没有错误抛出。如果handler实在子线城里面创建:
    new Thread(new Runnable() {
        @Override
        public void run() {
              Handler handler = new Handler() {
                  @Override
                  public void handleMessage(Message msg) {
                      super.handleMessage(msg);
                  }
              };
        }
    }).start();

    可以很容易得出结论,子线程没有和ThreadLocal绑定,所以ThreadLocal里面的looper为空,如下异常抛出:
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }

    既然有上面问题,如何在子线程里面创建handler呢?
    new Thread(new Runnable() {
        @Override
        public void run() {
              Looper.prepare();
              Handler handler = new Handler(Looper.myLooper()) {
                     public void handleMessage(android.os.Message msg) {
                           // XXX
                     }
              };
             Looper.loop();
        }
    }).start();
    继续看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));
    }

     ThreadLocal的set方法如下, 可以看到内部ThreadLocalMap已当前线程为key进行绑定。

    public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }

    这样当前线程和 sThreadLocal就绑定了,Looper.myLooper()得到的就是上面创建的 new Looper(quitAllowed)。
    当然聪明的google意识到这个问题,所以有个HandlerThread可以省去手动调用prepare和loop的烦恼,核心代码如下:
    public class HandlerThread extends Thread {
        
        @Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
        
    }






  • 相关阅读:
    javascript函数的定义和调用(包括方法)
    iterable(遍历)
    循环
    C#基础知识 简单说明泛型的优点
    C#基础知识 yield与foreach
    C#基础知识 结构与类的区别
    Asp.net MVC 生成zip并下载
    Asp.net MVC 填充word并下载
    Asp.net MVC 简单实现生成Excel并下载
    CTF中怎么看phpinfo
  • 原文地址:https://www.cnblogs.com/budoudou/p/6760583.html
Copyright © 2011-2022 走看看