Android26--Android之Handler进阶

1. Handler之前篇

1.1 ANR(Application Not Response)

在介绍为什么使用Handler之前,先来介绍一下ANR(Application Not Response)

1. ANR 发生的时机: 当用户进行的点击操作的时候,如果这个时候,应用程序在执行非常耗时的操作,导致,
系统和应用程序无法在继续处理用户的操作,
那么这个时候 系统会检测 操作的时间,如果超过5s,那么显示应用程序无响应。
2. 如何避免出现ANR的问题:
    2.1. 控件事件的操作,如果需要耗时处理,那么放到子线程中完成;
3. 子线程中,获取的数据,如何显示到UI 控件上?
对于子线程操作/更新UI,必然出现异常,可以通过子线程,向创建UI控件的线程,发送消息,来进行处理;
4.创建UI控件的线程,被称作为 “UI线程”,执行其他操作的线程叫做子线程。

1.2 为什么要使用Handler

在ActivityUI中,需要很短的时间进行界面的绘制,Android是在每16ms进行一次界面的绘制。但是有时候界面会一直出现未响应,这是因为在UI线程中执行了耗时的操作,我们可以通过主线程开启一个线程,执行耗时操作,之后,子线程将得到的结果再
通过Handler机制,将消息发送给主线程,今儿在handMessage中执行UI的更新。Handler机制成为了线程之间进行通信的桥梁。

2. Handler的机制

  1. 在一个Activity启动时,首先执行的方法是ActivityThread中的main()方法,在main()方法中,会执行两个方法
    Looper.prepareMainLooper(); 和Looper.loop();

    Looper.prepareMainLooper();方法会调用Looper中的prepare()方法;建立looper,下面是源码:


    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    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));
    }


    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

这样我们就可以得知,在Activity一启动,就会自动创建一个looper和一个messageQueue
在这之后,调用Looper.loop();一直等待messageQueue中的消息的来临,
(这里大家可以自己探究一下MessageQueue中的阻塞原理…….)

  1. 之后,我们会在Activity中new Handler(),在new Handler的同时,我们可以看一下他的源码:
    Handler的构造方法:
 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();
        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;
    }

Handler内部会自动调用Looper.myLooper();获取当前线程的lopper,
并且mLooper.mQueue;还会拿到messageQueue;
之后,我们可以说是handler和本线程中的looper mesageQueue绑定了.

3.之后,我们就可以新建Message了, 之后 可以添加相应的属性,但是在sendMessage之后,将消息发送到哪了哪?
我们最终会调到这个方法:enqueueMessage(),怎样将message加入到队列中


    final boolean enqueueMessage(Message msg, long when) {  
    if (msg.when != 0) {  
        throw new AndroidRuntimeException(msg + " This message is already in use.");  
    }  
    if (msg.target == null && !mQuitAllowed) {  
        throw new RuntimeException("Main thread not allowed to quit");  
    }  
    synchronized (this) {  
        if (mQuiting) {  
            RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");  
            Log.w("MessageQueue", e.getMessage(), e);  
            return false;  
        } else if (msg.target == null) {  
            mQuiting = true;  
        }  
        msg.when = when;  
        Message p = mMessages;  
        if (p == null || when == 0 || when < p.when) {  
            msg.next = p;  
            mMessages = msg;  
            this.notify();  
        } else {  
            Message prev = null;  
            while (p != null && p.when <= when) {  
                prev = p;  
                p = p.next;  
            }  
            msg.next = prev.next;  
            prev.next = msg;  
            this.notify();  
        }  
    }  
    return true;  
}  

3.1 sendMessageAtTime()方法接收两个参数,其中msg参数就是我们发送的Message对象,
而uptimeMillis参数则表示发送消息的时间,它的值等于自系统开机到当前时间的毫秒数再加上延迟时间,
如果没有延迟时间,延迟时间默认就为0.

3.2 从enqueueMessage方法我们可以看出,所谓的入队其实就是将所有的消息按时间来进行排序,
这个时间当然就是我们刚才介绍的uptimeMillis参数。

3.3 这样message就加入到messageQueue当中了.

4.之后我们回到Looper.loop()当中, 他会一直执行 Message msg = queue.next(); ,调用messageQueue的
next()方法,等待消息,如果没有就阻塞.拿到消息之后,会调到dispatchMessage, 调用相应的方法去处理消息.

     public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
           handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

如果我们的Handler中没有一个callback, 会调用到我们重写的handleMessage方法处理我们的消息.

到这里Handler的消息处理机制,我们就了解的差不多了.

有关细致的地方:
http://www.nowcoder.com/discuss/1965

3. Handler的使用

3.1 主线程往子线程发送消息

主线程往子线程发送消息,四步走
/* 在子线程中建立loop,如果没有,会出现这样的异常
 *提示的错误信息为 Can't create handler inside thread that
 * has not called Looper.prepare() 。
 */

1.Looper.prepare();
2.新建一个Handler,并重写handleMessage()
3. // 把子线程中的handler对象,交给主线程 (发送消息的Handler和接受消息的Handler必须是同一个)
   mMainHandler = handler;
4.Looper.loop();

应用场景:
聊天软件,子线程联网,界面部分发送消息给子线程。
注意:
1.Looper.myLooper().quit(); 代表当前线程中的Looper就退出了;looper的loop() 方法,结束;
这是一张关系图:
这里写图片描述

3.2 子线程通过Handler像主线程发送消息。

子线程往主线程中发送消息,这是我们通常见到的现象,子线程进行耗时的操作,
将得到的结果返回给主线程,主线程再在handleMessage()中处理.
通常的做法:
1.主线程中开启一个线程:new Thread();
2.在子线程中创建消息,并sendMessage();
3.主线程在handleMessage()中处理从MessageQueue中拿到的message进行处理.

这是一张他们的关系图
这里写图片描述

4.Handler和AsyncTask的区别

  1. Handler与线程配合,应用程序可以同时开启多个线程,一般的情况下,
    系统可以支持很多线程的运行;对于异步任务而言,同一时刻,能够同时执行的异步任务数量很少;
  2. 异步任务是一种能够在后台执行,短时间的后台任务的API;线程,可以在后台执行,长时间耗时操作的API;
  3. 代码编写:异步任务将线程与Handler进行封装,简化代码的开发;线程方式就需要编写Handler和发送消息。

5.一个关于Handler消息传送机制的关系图:

这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值