Android Handler使用精析

1 引出问题

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

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

因此我们需要将这些耗时任务放在子线程中去处理,并且在处理耗时任务的过程中,我们需要更新UI以便告知用户耗时任务的进度、状态等信息。

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

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

2 Handler的简单使用

2.1 调用sendMessage方法

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




    

    

MainActivity的代码编写如下:

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.arg1、message.arg2、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、arg1、arg2或obj)的不同进行相应的处理——更新UI。

设置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方法

则改写上述代码如下:

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.arg1、message.arg2、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方法里执行。

4.1 Handler、Looper与MessageQueue的构建

首先来看Handler构造方法:

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

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

public Handler(@Nullable Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class 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对象
    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 sThreadLocal = new ThreadLocal();

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) {
        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() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        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) {
                // No message indicates that the message queue is quitting.
                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();
        }
    }

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) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //8.调用handleMessage来处理消息
        handleMessage(msg);
    }
}

总结:

  1. 通过上一部分的源码分析,可以发现,当启动一个Android应用时,会自动创建一个给应用的主线程使用的Looper实例,像Android中的一些事件(如按钮点击等)、生命周期方法的调用等都会被放入到消息中,Looper的主要工作就是挨个处理消息队列中的消息对象。
  2. 在通过Handler发送消息并将消息放入消息队列的同时会将Message的target赋值为当前的Handler对象,这样Looper在处理该消息时,才可以调用Handler的handleMessage方法完成消息的处理。

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造成的内存泄漏
 * 1、使用静态的handler,对外部类不保持对象的引用
 * 2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
 */
private static class ExecuteTaskHandler extends Handler {
    private final WeakReference mActivityReference;

    ExecuteTaskHandler(Activity activity) {
        this.mActivityReference = new WeakReference(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 使用详解

你可能感兴趣的:(android,handler)