Android Handler使用精析

1 引出问题

在启动一个Android应用的同时,便会开启一个主线程——Main Thread(也叫UI线程),主线程负责处理与UI相关的事件。java

可是在开发中常常会作一些耗时的任务,这些耗时的任务会阻塞主线程,若长时间地阻塞主线程则会致使应用发生ANR(应用程序无响应)。android

所以咱们须要将这些耗时任务放在子线程中去处理,而且在处理耗时任务的过程当中,咱们须要更新UI以便告知用户耗时任务的进度、状态等信息。算法

那么如何在子线程中更新主线程中的UI控件呢?
对此,咱们能够借助Handler来完成。Handler提供了三种方式来解决上述问题:shell

  1. 调用Handler的sendMessage方法;
  2. 调用Handler的post方法;
  3. 调用Handler的obtainMessage方法;

2 Handler的简单使用

2.1 调用sendMessage方法

为了更好地理解Handler的使用,咱们建立一个Demo来作一个简单的演示示例。点击屏幕上的按钮开始执行任务,同时文本框显示“开始执行任务”,用休眠5秒钟模拟执行耗时任务,当任务执行完(休眠结束后),文本框显示“任务执行完毕”。
布局文件代码以下:并发

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tooltip_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/start_execute_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开始执行" />
</LinearLayout>

MainActivity的代码编写以下:less

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MA-cxs";
    //工具提示文本框
    private TextView tooltipTv;
    //开始执行任务按钮
    private Button startExecute;
    //是否开始执行
    private boolean isExecute = false;

    public final int MSG_EXECUTE_START = 1000;
    public final int MSG_EXECUTE_COMPLETE = 1001;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
    }

    /**
     * 初始化控件
     */
    private void initViews() {
        tooltipTv = findViewById(R.id.tooltip_tv);
        startExecute = findViewById(R.id.start_execute_btn);
        startExecute.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.start_execute_btn) {
            if (!isExecute) {
                new MyThread().start();
            }
        }
    }

    /**
     * 建立一个执行耗时任务的子线程,并发送消息
     */
    class MyThread extends Thread {
        @Override
        public void run() {
            isExecute = true;
            Log.d(TAG, "子线程开始执行");

            //发送消息给Handler
            mExecuteTaskHandler.sendEmptyMessage(MSG_EXECUTE_START);

            //借助休眠模拟执行任务的过程
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //执行完任务后再次发送一个执行成功的消息
            Message message = new Message();
            //此处也可设置message.arg一、message.arg二、message.obj、message.setData(Bundle对象)方法
            message.what = MSG_EXECUTE_COMPLETE;
            message.setData(new Bundle());
            mExecuteTaskHandler.sendMessage(message);

            isExecute = false;
            Log.d(TAG, "子线程执行完毕");
        }
    }

    //接收消息并进行处理
    private Handler mExecuteTaskHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case MSG_EXECUTE_START:
                    Log.d(TAG, "收到开始执行任务的消息");
                    tooltipTv.setText("开始执行任务");
                    break;
                case MSG_EXECUTE_COMPLETE:
                    Log.d(TAG, "收到任务执行完毕的消息");
                    tooltipTv.setText("任务执行完毕");
                    break;
            }
        }
    };
}

Handler的使用步骤总结:
1.发送消息:在执行耗时任务时发送消息给Handler;
2.接收消息并进行处理:在主线程(UI线程)中建立一个Handler对象,并实现其handleMessage()方法,而且根据message参数(what、arg一、arg2或obj)的不一样进行相应的处理——更新UI。async

设置Message除了能够设置其what、arg1及arg2以外,还能够借助Message的setData方法,传入一个Bundle对象。
发送消息的方法除了有sendMessage外还有其余的方法:
修饰符和返回值类型 方法及其描述
public final boolean sendEmptyMessage(int what) 发送仅包含what值的消息
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) 发送仅包含what值而且在指定的绝对时间传递的消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) 仅包含what值的消息,而且该消息将在延迟指定的时间后发送
public final boolean sendMessage(Message msg) 将消息放在当前待处理的消息队列的末尾
public final boolean sendMessageAtFrontOfQueue(Message msg) 将消息放入消息队列的最前面,以在消息循环的下一次迭代中进行处理
public boolean sendMessageAtTime(Message msg, long uptimeMillis) 在指定的绝对运行时间发送消息
public final boolean sendMessageDelayed(Message msg, long delayMillis) 在延迟指定的时间后发送消息
对于延时、定时消息,有时候须要取消,则能够经过如下方法将指定消息移除:
修饰符和返回值类型 方法及其描述
public final void removeCallbacksAndMessages(Object token) 移除obj为token的任何待处理的回调及已发送的消息
public final void removeMessages(int what) 删除消息队列中为what参数为what值的待处理的消息
public final void removeMessages(int what, Object object) 删除消息队列中为what参数what值而且obj参数为object的待处理的消息

2.2 调用post方法

则改写上述代码以下:ide

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    //其他代码不变

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.start_execute_btn) {
            if (!isExecute) {
                //new MyThread().start();
                new PostThread().start();
            }
        }
    }

    class PostThread extends Thread {
        @Override
        public void run() {
            isExecute = true;
            Log.d(TAG, "PostThread run(): ThreadId=" + Thread.currentThread().getId() +
                    ", ThreadName=" + Thread.currentThread().getName());

            //发送消息给Handler
            mExecuteTaskHandler.post(new Runnable() {
                @Override
                public void run() {
                    Log.d(TAG, "Runnable run(): ThreadId=" + Thread.currentThread().getId() +
                            ", ThreadName=" + Thread.currentThread().getName());
                    tooltipTv.setText("开始执行任务");
                }
            });

            //借助休眠模拟执行任务的过程
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //执行完任务后再次发送一个执行成功的消息
            Message message = new Message();
            //此处也可设置message.arg一、message.arg二、message.obj、message.setData(Bundle对象)方法
            message.what = MSG_EXECUTE_COMPLETE;
            message.setData(new Bundle());
            mExecuteTaskHandler.post(new Runnable() {
                @Override
                public void run() {
                    Log.d(TAG, "Runnable run(): ThreadId=" + Thread.currentThread().getId() +
                            ", ThreadName=" + Thread.currentThread().getName());
                    tooltipTv.setText("任务执行完毕");
                }
            });

            isExecute = false;
        }
    }
}

运行应用程序后点击按钮,打印日志以下:函数

PostThread run(): ThreadId=154, ThreadName=Thread-2
Runnable run(): ThreadId=2, ThreadName=main
Runnable run(): ThreadId=2, ThreadName=main

总结:工具

从上面代码并结合日志能够看出:Handler的post方法参数为一个Runnable对象,因为Handler是在主线程中建立的,所以,Runnable也是在主线程中运行,则Runnable与建立它的线程无关,与调用post方法的线程无关。而且Runnable的run方法是在主线程中更新UI的。

与sendMessage方法相似,post方法也有多个类似的方法:

修饰符和返回值类型 方法及其描述
public final boolean post(Runnable r) 将Runnable对象添加到消息队列中
public final boolean postAtFrontOfQueue(Runnable r) 将Runnable对象添加到消息队列的最前面
public final boolean postAtTime(Runnable r, long uptimeMillis) 将Runnable对象添加到消息队列中,而且在指定的绝对时间执行
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) 同上
public final boolean postDelayed(Runnable r, long delayMillis) 将Runnable对象添加到消息队列中,并在通过指定的时间后运行
public final boolean postDelayed(Runnable r, Object token, long delayMillis) 同上
同sendMessage方法,能够经过 removeCallbacks(Runnable r)removeCallbacks(Runnable r, Object token)removeCallbacksAndMessages(Object token)方法取消post定时、延时处理的Runnable。

2.3 调用obtainMessage方法

obtainMessage方法与sendMessage方法相似,也能够当作是一种。经过下面的代码就能看出这一点:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    //其他代码不变
    
    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.start_execute_btn) {
            if (!isExecute) {
//                new MyThread().start();
//                new PostThread().start();
                new ObtainThread().start();
            }
        }
    }

    class ObtainThread extends Thread {
        @Override
        public void run() {
            isExecute = true;
            Log.d(TAG, "PostThread run(): ThreadId=" + Thread.currentThread().getId() +
                    ", ThreadName=" + Thread.currentThread().getName());

            //发送消息给Handler
            mExecuteTaskHandler.obtainMessage(MSG_EXECUTE_START).sendToTarget();

            //借助休眠模拟执行任务的过程
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //执行完任务后再次发送一个执行成功的消息
            mExecuteTaskHandler.obtainMessage(MSG_EXECUTE_COMPLETE).sendToTarget();

            isExecute = false;
        }
    }
}

总结:
经过调用obtainMessage方法便可生成Message对象,此对象携带其target对象,经过调用sendToTarget方法便可将消息发送到Handler的消息队列中,而后再由Handler的handleMessage方法进行处理。

3 Handler的官方定义

说了这么多,那么到底什么是Handler呢?

Android API是这样给Handler定义的:Handler能够用来发送处理与线程的MessageQueue关联的Message和Runnable对象。每一个Handler实例都与一个线程和该线程的消息队列相关联。Handler在建立时便被绑定到正在建立它的线程或MessageQueue上,而后Handler会把Message和Runnable对象传递到MessageQueue中,并在它们从MessageQueue中出来时执行它们。

Handler主要有两个用途(按本身理解进行翻译的):

原文:There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed at some point in the future;
(2) to enqueue an action to be performed on a different thread than your own.
  1. 在未来的某个时间点执行计划好的消息和Runnable对象;
  2. 建立本身的线程(执行耗时任务)并经过Handler与应用的主线程进行通信。

4 Handler具体实现解析

首先来看一下Handler的通讯机制。具体以下图所示:

  1. 建立Handler,并采用当前线程的Looper建立消息循环系统;
  2. 调用Handler的sendMessage(Message)或post(Runnable)发送消息,调用enqueueMessage方法将消息插入到消息链表中;
  3. Looper循环检测消息队列中的消息,如有消息则取出该消息,并调用该消息持有的handler的dispatchMessage方法,回调到建立Handler线程中重写的handleMessage方法里执行。

Handler通讯机制

4.1 Handler、Looper与MessageQueue的构建

首先来看Handler构造方法:

public Handler() {
    this(null, false);
}

发现其调用的带参构造方法,以下所示:

public Handler(@Nullable 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());
        }
    }
    //1. 获得主线程的Looper对象及MessageQUeue对象(详见该部分的补充分析)
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
    }
    //2. 获得主线程的Looper中的MessageQueue,由于构建Looper的同时会建立一个MessageQueue,这里的mQueue和4.2.2的步骤3中的mQueue是对应的。
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

下面补充一下主线程Looper对象时什么时候初始化的:
这个Looper对象是什么时候建立的?其实在程序启动时,借助ActivityThread的main方法初始化了一个Looper对象,即主线程的Looper,以下所示的prepareMainLooper方法

public static void main(String[] args) {
    
    ···
    
    //建立主线程的Looper对象
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    //开启消息循环
    Looper.loop();

    ···
}

再来看Looper的prepareMainLooper方法及prepare方法,将建立的Looper对象存储到ThreadLocal当中:

ThreadLocal是线程私有的数据存储类,能够来保存线程的Looper对象。
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
}

而构建Looper时,首先会建立一个消息队列MessageQueue,并获取当前的线程。

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

经过Looper.myLooper();得到主线程的Looper对象,从ThreadLocal中获取存储的Looper对象。

@Nullable
public static Looper myLooper() {
    return sThreadLocal.get();
}
关于ThreadLocal如何保存和获取Looepr请参考博客: (转)Android Handler 使用详解

4.2 Handler发送消息

首先来看一下Handler的sendMessage方法,代码以下:

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}

而后进入sendMessageDelayed方法。

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

再进入sendMessageAtTime方法,以下所示,sendMessageAtTime方法将穿过来的Message与Handler的mQueue(MessageQueue)经过enqueueMessage方法进入队列。

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    //3.发送消息,这里和4.2.1中步骤2对应
    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方法中,首先为message的target赋值为当前的Handler对象,而后经过MessageQueueenqueueMessage入队。

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
    //4.为message的target赋值为当前的Handler对象并入队
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
对于post(Runnable)而言,首先经过post方法传递该Runnable对象,而后调用getPostMessage静态方法构造一个Message对象,在经过sendMessageDelayed方法传递消息,后面和sendMessage流程就同样了。
public final boolean post(@NonNull Runnable r) {
    return  sendMessageDelayed(getPostMessage(r), 0);
}

4.3 消息入队列

而后进入MesssageQueue类中的方法中,代码以下:这个方法就是消息的入队方法,经过比较各个消息的执行时间,来进行从小到大的排序,执行时间在前的就在队列的前面,这里用到了单链表的插入算法。

//5.消息入队列
boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        //若已经调用了quit,这里便会抛出异常
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //若消息队列为空或者这个消息的执行时间要比队列头部的消息的执行时间还要早,
        //那么就将该消息插入到消息队列的头部
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
         } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                //不然就从消息队列的头部日后排
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

4.4 Looper处理消息

接下来看Looper在loop方法中处理消息,若消息为空,则返回,不然取出消息,并经过msg.target.dispatchMessage方法回调到Handler中去。

public static void loop() {
    
        //获取当前线程的Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获取当前线程的MessageQueue
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        for (;;) {
            // 6.从Looper中的MessageQueue中取出Message
            Message msg = queue.next(); // might block
            if (msg == null) {
                // 若消息不为空,则MessageQueue不会被quit
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                //7.若消息不为空,则经过调用mag.target获取Handler对象并调用其dispatchMessage回调到Handler中去
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            //消息回收
            msg.recycleUnchecked();
        }
    }

再来看MessageQueue的next()方法作了什么:

Message next() {
    ...// 仅贴出关键代码
    for (;;) {
        synchronized (this) {
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMessages;
        if (msg != null) {
            if (now < msg.when) {
                // 若是还没处处理消息的时候,就继续循环
                nextPollTimeoutMillis = (int) Math.min(msg.when - now,                                                                         Integer.MAX_VALUE);
            } else {
                // 把这个消息取出来返回给 looper 进行处理
                mBlocked = false;
                if (prevMsg != null) {
                    prevMsg.next = msg.next;
                } else {
                    mMessages = msg.next;
                }
                msg.next = null;
                if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                msg.markInUse();
                return msg;
            }
        } else {
            nextPollTimeoutMillis = -1;
        }

        // 若是使用了 quit,这里就会返回 null,而后当前线程的 looper 循环也就结束了
        if (mQuitting) {
            dispose();
            return null;
        }
    }
}

4.5 Handler处理消息

最后再回到Handler类当中,看一下dispatchMessage方法:若该消息有callback,即经过post(Runnable)方式发送的消息,由于在发送Runnable对象时,把Runnable对象赋值给了message的callback,则交由handleCallback方法处理;不然交由handleMessage方法处理,在使用Handler时重写handleMessage方法便可。

/**
 * Handle system messages here.
 */
    public void dispatchMessage(@NonNull Message msg) {
    //若使用post方法发送的消息,则会在这里执行
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
                // 若使用 Handler(new Handler.Callback()) 建立的handler对象,
                // 则会在它的回调函数handleMessage内处理消息
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //8.调用handleMessage来处理消息
        //使用Handler()建立的Handler对象则会在此到处理消息
        handleMessage(msg);
    }
}

总结:

  1. 经过上一部分的源码分析,能够发现,当启动一个Android应用时,会自动建立一个给应用的主线程使用的Looper实例,像Android中的一些事件(如按钮点击等)、生命周期方法的调用等都会被放入到消息中,Looper的主要工做就是挨个处理消息队列中的消息对象。
  2. 在经过Handler发送消息并将消息放入消息队列的同时会将Message的target赋值为当前的Handler对象,这样Looper在处理该消息时,才能够调用Handler的handleMessage方法完成消息的处理。
  3. 若在主线程不须要此步骤,由于系统自动建立并开启了 Looper;而在子线程须要手动调用 Looper.prepare()去建立Looper,并调用Looper.loop()开启消息循环。
  4. 若想要在主线程接收消息,这个Handler对象就须要在主线程建立;若想在子线程接收消息,就须要在子线程建立Handler对象,记得在建立Handler对象以前建立Looper,不然就会抛出异常。
  5. 可使用Message.obtain()来获取Message对象,来减小系统建立对象的开销。

5 Handler存在的问题及改进方法

5.1 存在的问题

存在的问题1
在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,例如当Activity finish时,Handler可能并未执行完,这样可能会形成Activity的内存泄漏。

存在的问题2
当Activity finish时,在onDestroy方法中释放了一些资源,若此时Handler在执行handleMessage方法时,会因为相关资源被释放而引发空指针异常。

5.2 改进方法

那么如何避免上述问题呢?

  1. 针对内存泄漏,咱们可使用静态内部类,这是由于静态内部类不会持有外部类的引用,所以不会致使外部类实例的内存泄漏。而当咱们须要在静态内部类中调用外部的Activity时,咱们可使用弱引用来处理,以下代码所示:
ExecuteTaskHandler mExecuteTaskHandler = new ExecuteTaskHandler(MainActivity.this);

/**
 * 为避免handler形成的内存泄漏
 * 一、使用静态的handler,对外部类不保持对象的引用
 * 二、但Handler须要与Activity通讯,因此须要增长一个对Activity的弱引用
 */
private static class ExecuteTaskHandler extends Handler {
    private final WeakReference<Activity> mActivityReference;

    ExecuteTaskHandler(Activity activity) {
        this.mActivityReference = new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        MainActivity mainActivity = (MainActivity) mActivityReference.get();

        switch (msg.what) {
            case MSG_EXECUTE_START:
                Log.d(TAG, "收到开始执行任务的消息");
                mainActivity.tooltipTv.setText("开始执行任务");
                break;
            case MSG_EXECUTE_COMPLETE:
                Log.d(TAG, "收到任务执行完毕的消息");
                mainActivity.tooltipTv.setText("任务执行完毕");
                break;
        }
    }
}
  1. 针对可能形成的空指针异常,咱们能够经过添加try catch来解决
  • 如果使用handleMessage,则在该方法中添加try catch;
  • 如果调用的post方法则在Runnable方法中添加try catch;

固然,最好不加try catch,而是在onDestroy中把消息队列的消息remove掉。

@Override
protected void onDestroy() {
    super.onDestroy();
    //避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列
    handler.removeCallbacks(postRunnable);   //清除runnable对应的message
    //handler.removeMessage(what)  清除what对应的message
}

参考资料:

  1. Android Handler的基本使用
  2. Handler | Android Developers
  3. Android Handler详解
  4. (转)Android Handler 使用详解
相关文章
相关标签/搜索