handler底层原

来源:互联网 发布:linux基本命令大全 编辑:程序博客网 时间:2024/06/05 04:15

参考文章:http://blog.csdn.net/woshiwxw765/article/details/38146185?locationNum=10


关键相关的类,Handler,Message,MessageQueue,Looper

  • Handler:用来发送消息,处理消息
  • Message:消息实体对象,handler通过sendMsg将实体放到消息队列里面 ,感觉是封装消息数据的意思
  • MessageQueue:通过链表的方式存放消息的队列
  • Looper:消息轮询器,轮询消息队列的消息然后取出,交给handler处理

  • 为什么会出现Handler,因为在子线程中不能更新UI,否则会挂掉,那么只能借助Message对象封装数据,然后通过handler对象发送到主线程,再进行UI更新。即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了。

  • 在子线程中创建的Handler是会导致程序崩溃的,提示的错误信息为 Can't create handler inside thread that has not called Looper.prepare() 。说是不能在没有调用Looper.prepare() 的线程中创建Handler,但在主线程中却不需要先调用Looper.prepare()方法,

  • Handler的无参构造函数如下所示:会检查是否有Looper对象

    public Handler() {    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对象,如果对象为空就抛异常    mLooper = Looper.myLooper();    if (mLooper == null) {        throw new RuntimeException(            "Can't create handler inside thread that has not called Looper.prepare()");    }    mQueue = mLooper.mQueue;    mCallback = null;}
  • 那什么时候Looper对象才可能为空呢?这就要看看Looper.myLooper()中的代码了,如下所示:

        public static final Looper myLooper() {        return (Looper)sThreadLocal.get();    }
  • 这个方法非常简单,就是从sThreadLocal对象中取出Looper。如果sThreadLocal中有Looper存在就返回Looper,如果没有Looper存在自然就返回空了。因此你可以想象得到是在哪里给sThreadLocal设置Looper了吧,当然是Looper.prepare()方法!我们来看下它的源码:

    public static final void prepare() {    if (sThreadLocal.get() != null) {        throw new RuntimeException("Only one Looper may be created per thread");    }    sThreadLocal.set(new Looper());}
  • 可以看到,首先判断sThreadLocal中是否已经存在Looper了,如果还没有则创建一个新的Looper设置进去。这样也就完全解释了为什么我们要先调用Looper.prepare()方法,才能创建Handler对象。同时也可以看出每个线程中最多只会有一个Looper对象。

  • 主线程中的Handler也没有调用Looper.prepare()方法,为什么就没有崩溃呢?细心的朋友我相信都已经发现了这一点,这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。查看ActivityThread中的main()方法,代码如下所示:

    public static void main(String[] args) {    SamplingProfilerIntegration.start();    CloseGuard.setEnabled(false);    Environment.initForCurrentUser();    EventLogger.setReporter(new EventLoggingReporter());    Process.setArgV0("<pre-initialized>");    Looper.prepareMainLooper();//这里调用这个方法,其实是同过Looper的内部成员ThreadLocal类型的变量sThreadLocal,将当前线程的数据存储起来,存起来的就是创建一个Looper对象,ThreadLocal是根据不同线程来存储数据的,同一个线程存储的数据,后面去获取还是原来线程的数据,线程之间只能存取自己的数据。    ActivityThread thread = new ActivityThread();    thread.attach(false);//这里初始化了一个Hndler对象,并且向消息池中发送两个主要的消息,一个是用于初始化Application,一个是用于初始化Activity,当然在这期间还调用了远程服务,通过IPC(Inter process communication进程间通信)调用了ActivityManagerService(AMS)服务里的方法,获取相关的信息,并作为消息对象传递    if (sMainThreadHandler == null) {        sMainThreadHandler = thread.getHandler();//拿到上一步初始化的handler    }    AsyncTask.init();    if (false) {        Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));    }    Looper.loop();//这里调用loop()方法时,就是去获取之前存储在ThreadLocal变量里面的Looper对象,还有消息队列MessageQueue对象,这个queue对象也是在创建Looper对象时创建的,在构造方法里创建了。    throw new RuntimeException("Main thread loop unexpectedly exited");}
  • 可以看到,在第7行调用了Looper.prepareMainLooper()方法,而这个方法又会再去调用Looper.prepare()方法,代码如下所示:

        public static final void prepareMainLooper() {        prepare();        setMainLooper(myLooper());        if (Process.supportsProcesses()) {            myLooper().mQueue.mQuitAllowed = false;        }    }
  • 因此我们应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。

  • 这样基本就将Handler的创建过程完全搞明白了,总结一下就是在主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象。

下面还是用一张图来展示一下handler执行的流程,内容太多可能看不清楚,自行放大一下



0 0
原创粉丝点击