Android开发实战总结

Android Handler详解

2017-10-21  本文已影响104人  AKyS佐毅

本期主要内容

1、Handler是什么?

2、为什么要使用Handler?

3、Handler 、 Looper 、Message 这三者都与Android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢?

Handler 、 Looper 、Message.png

Handler类包含如下方法用于发送、处理消息:

4:Handler如何去实现发送和处理消息

1: 使用Thread发送消息

Thread

2: 使用Runnable发送消息

Runnable.png

3: 使用Handler下载文件并更新进度条
1: 首先配置权限信息 读写权限问题

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" />

2: 在Android 6.0之后必须动态检测权限。所以在下载操作开始之前需要进行权限判断

Android6.0.png 发送消息,准备更新UI操作.png handleMessage.png

权限问题处理方法

权限问题处理方法.png

4: 使用 handler实现倒计时功能
首先强调一下需要注意的问题吧
Handler在进行异步操作并处理返回结果时经常被使用。通常我们的代码会这样实现。

Handler

但是,其实上面的代码可能导致内存泄露,当你使用Android lint工具的话,会得到这样的警告:

warning.png

以下几种情况都可能存在内存泄漏的情况

1.当一个Android应用启动的时候,会自动创建一个供应用主线程使用的Looper实例。Looper的主要工作就是一个一个处理消息队列中的消息对象。在Android中,所有Android框架的事件(比如Activity的生命周期方法调用和按钮点击等)都是放入到消息中,然后加入到Looper要处理的消息队列中,由Looper负责一条一条地进行处理。主线程中的Looper生命周期和当前应用一样长。

2.当一个Handler在主线程进行了初始化之后,我们发送一个target为这个Handler的消息到Looper处理的消息队列时,实际上已经发送的消息已经包含了一个Handler实例的引用,只有这样Looper在处理到这条消息时才可以调用Handler#handleMessage(Message)完成消息的正确处理。

3.在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用。

解决方式

🤢.png

面试要点

public Handler(Callback callback, boolean async) {
        
       //得到主线程(已经被ActivityThread初始化好)的looper
       mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread that has not called Looper.prepare()");
       }
       
       //得到主线程(已经被ActivityThread初始化好)的looper的MessageQueue,注释:①
       mQueue = mLooper.mQueue;
       mCallback = callback;
       mAsynchronous = async;
}

注意,看到了吗? 这里是核心:mLooper = Looper.myLooper();,

public static Looper myLooper() {
     return sThreadLocal.get();
}

而这个Looper对象,是在我们启动程序的时候,也就是ActivityThread 中的main方法帮我们初始化的,也就是我们主线程的Looper。

public static void prepareMainLooper() {
     ···
     prepare(false);
     ···
}

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

我们在子线程 使用 handler的实例进行 sendMessage(Message msg) 的时候:

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

最终走向enqueueMessage:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

最终把我们的Message,放进了主线程的MessageQueue里面进行循环!

上一篇 下一篇

猜你喜欢

热点阅读