Android异步消息处理机制之Handler、Looper、Message

简介: 因为Android UI线程是线程不安全的,在子线程中更新UI会直接程序崩溃,另外当UI线程需要执行一个比较耗时的操作的话(IO操作,网络通信等),若是执行时间超过5s,程序会直接ANR,为了解决上述问题,可以使用异步消息处理机制[Handler]
  • 为什么用异步消息处理机制?

因为Android UI线程是线程不安全的,在子线程中更新UI会直接程序崩溃,另外当UI线程需要执行一个比较耗时的操作的话(IO操作,网络通信等),若是执行时间超过5s,程序会直接ANR,为了解决上述问题,可以使用异步消息处理机制Handler,Handler有两大用处:
(1) . 在未来的某个时间点执行调度Message或者Runnable对象
(2) . 将执行的操作以队列形式放到其他线程中,并可以实现线程间通信

  • Handler、Looper、Message

通常在主线程中创建Handler,在子线程中执行耗时操作,并在子线程中将执行结果通过handler传递到主线程中刷新UI,首先在Activity中创建handler:

 Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    tv_text.setText("更新UI");
                    break;
                default:
                    break;
            }
        }
    };

新起一个子线程:

 new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //模拟耗时操作
                    Thread.sleep(2000);
                    //模拟子线程处理完数据,通过handler将结果传到主线程
                    handler.sendEmptyMessage(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

整个流程:首先在子线程中处理耗时操作,当子线程处理完后,通过handler将处理结果传到主线程用来刷新UI,通过handler就完成了线程间的通信,那么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;
    }

可以看到首先通过Looper.myLooper()来创建了一个Looper对象,如果Looper为空,则抛出异常Can't create handler inside thread that has not called Looper.prepare(),来看Looper.myLooper():

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

从sThreadLocal对象中取出Looper,如果sThreadLocal中有Looper存在就返回Looper,如果没有则返回null了,sThreadLocal对象什么时候set的呢?答案是Looper.prepare():

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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.prepare()只能被调用一次,即一个handler只能对应一个Looper,如果被多次调用,则会抛出异常:Only one Looper may be created per thread,如果在子线程使用handler,必须首先调用Looper.prepare()来创建Looper;但是我们在主线程中并没有调用Looper.prepare(),也没有崩溃呀!这是因为系统已经为我们创建好了:

public final class ActivityThread {
    public static void main(String[] args) {
        ......//省略其他代码
        Looper.prepareMainLooper();
        ......//省略其他代码
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

来看Looper.prepareMainLooper()方法:

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

因此我们的主线程一直有Looper对象,再来看Looper.loop():

  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;
        ......//省略其他代码
        for (;;) {
            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);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

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

ActivityThread的main()方法执行 Looper.loop()后,主线程就开始无限循环处理消息,没有消息时阻塞等待,既然无限循环为什么主线程没有因为死循环而卡死呢?原因可以参考下这篇文章:Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
handler创建完成,就可以发送Message了,来看Message都有哪些成员变量:

//Message的识别码,用来区别不同的Message
public int what;
//arg1 arg2都是用来传递整形数据
public int arg1;
public int arg2;
//可以传递任意数据
public Object obj;

创建一个新消息,如:

Message message = Message.obtain();//建议使用Message.obtain()而不是new Message(),Message.obtain()可以从消息池中取消息
 message.what = 1; //识别码为1
 message.arg1 = 100; //携带int类型数 100和101
 message.arg2 = 101;
 message.obj = MyObject;//携带MyObject对象
 Bundle bundle = new Bundle(); //封装bundle
 bundle.putString("key", "value");
 message.setData(bundle);
 handler.sendMessage(message); //通过handler将Message送到消息队列中

来看handler都有哪些处理Message的常用方法:

方法 备注
sendEmptyMessage( int what) 只包含what的Message
sendEmptyMessageDelayed( int what, long delayMillis) 只包含what的Message,延迟delayMillis之后发送
sendEmptyMessageAtTime( int what, long uptimeMillis) 发送空消息最终会调用的方法,uptimeMillis=SystemClock.uptimeMillis() + delayMillis,其中SystemClock.uptimeMillis()表示从开机到现在的毫秒数
sendMessage(Message msg) 发送消息
sendMessageDelayed(Message msg, long delayMillis) 延迟发送消息,delayMillis为延迟时间
sendMessageAtTime(Message msg, long uptimeMillis) 发送消息最终会调用的方法,uptimeMillis=SystemClock.uptimeMillis() + delayMillis,其中SystemClock.uptimeMillis()表示从开机到现在的毫秒数
dispatchMessage(Message msg) 分发消息
handleMessage(Message msg) 处理消息
removeMessages( int what) 移除在MessageQueue里面识别码为what的消息
post(Runnable r) 将Runnable转换成一条消息,见下面
postAtTime(Runnable r, long uptimeMillis) 将Runnable转换成一条消息定时发送,见下面
postDelayed(Runnable r, long delayMillis) 将Runnable转换成一条消息延时发送,见下面
removeCallbacks(Runnable r) 移除runnable

上面表格中除了直接发送Message外,还可以用post一个runnable,看post()方法:

 public final boolean post(Runnable r)
    {
     //通过getPostMessage()方法将Runnable 转换成消息
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
 private static Message getPostMessage(Runnable r) {
        //新建一个Message
        Message m = Message.obtain();
        //Runnable 作为Message的一个变量callback 
        m.callback = r;
        return m;
    }
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

可见post(runnable)最终也是调用sendMessageAtTime()来发送消息MessageQueue的。通过上面的代码我们知道runnable 作为Message的一个变量callback 封装到Message中了,那么什么时候回调这个runnable呢?答案是当我们new Handler()时执行Looper.loop()的时候,会执行一句msg.target.dispatchMessage(msg),其中msg.target即是发送Message的handler,最终处理Message还是这个handler,来看dispatchMessage的源码:

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

如果callback 不为null,则执行handleCallback(msg),直接回调runnable里的run()方法;如果callback 为null,则执行Handler的handleMessage来处理之前传递到MessageQueue的消息,整个过程:

handler.jpg

总结:
**1、在子线程中更新UI有多种: Handler发送Message、Handler的post(runnable)方法、View.post()方法、Activity的runOnUiThread()方法
2.Handler的初始化会获取到当前线程的Looper对象,并通过Looper获得对应的MessageQueue对象
3.如果在子线程中使用Handler,必须首先调用Looper.prepare(),如:**

new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                //初始化Handler之前必须先调用Looper.prepare()
                Handler handler = new Handler();
                Looper.loop();
            }
        }).start();

4.Handler在当前线程初始化时创建了对应的Looper,Looper初始化时又会创建对应的MessageQueue,一个线程中只有一个Looper和一个MessageQueue,但可以有多个Handler,他们往同一个MessageQueue中发送消息并且Looper从MessageQueue中取出消息后,再交给发送这个Message的Handler去处理。

PS:关于Handler使用可能会内存泄露问题,可以参考这篇文章:Android中Handler引起的内存泄露

参考:
1.[Android 中线程间通信原理分析:Looper, MessageQueue, Handler](https://segmentfault.com/a/1190000006171396)
2.[Android异步消息处理机制完全解析,带你从源码的角度彻底理解](http://blog.csdn.net/guolin_blog/article/details/9991569)
3.[Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系](http://blog.csdn.net/lmj623565791/article/details/38377229/)

相关文章
|
1月前
|
前端开发 编译器 Android开发
构建高效Android应用:探究Kotlin协程的异步处理机制
【4月更文挑战第2天】在现代移动应用开发中,提供流畅且响应迅速的用户体验是至关重要的。随着Android平台的发展,Kotlin语言凭借其简洁性和功能性编程的特点成为了主流选择之一。特别地,Kotlin协程作为一种新型的轻量级线程管理机制,为开发者提供了强大的异步处理能力,从而显著提升了应用程序的性能和响应速度。本文将深入探讨Kotlin协程在Android中的应用,分析其原理、实现以及如何通过协程优化应用性能。
|
6天前
|
Android开发
Android Loader机制
Android Loader机制
12 1
|
1月前
|
消息中间件 安全 数据处理
Android之Handler、Message、MessageQueue、Looper详解2
Android之Handler、Message、MessageQueue、Looper详解
29 0
|
1月前
|
Java Android开发
Android之Handler、Message、MessageQueue、Looper详解1
Android之Handler、Message、MessageQueue、Looper详解
23 0
|
1月前
|
API 调度 Android开发
探索Android应用程序的后台运行机制
在移动应用开发中,了解和掌握Android应用程序的后台运行机制至关重要。本文将深入探讨Android平台上应用程序的后台运行原理及其影响因素,包括后台服务、广播接收器、JobScheduler等关键组件,以及如何有效管理后台任务以提升应用性能和用户体验。
21 3
|
2月前
|
Android开发 开发者
Android异步之旅:探索IntentService
Android异步之旅:探索IntentService
20 0
|
9天前
|
存储 安全 Android开发
安卓应用开发:构建一个高效的用户登录系统
【5月更文挑战第3天】在移动应用开发中,用户登录系统的设计与实现是至关重要的一环。对于安卓平台而言,一个高效、安全且用户体验友好的登录系统能够显著提升应用的用户留存率和市场竞争力。本文将探讨在安卓平台上实现用户登录系统的最佳实践,包括对最新身份验证技术的应用、安全性考量以及性能优化策略。
|
12天前
|
前端开发 Android开发 iOS开发
【Flutter前端技术开发专栏】Flutter在Android与iOS上的性能对比
【4月更文挑战第30天】Flutter 框架实现跨平台移动应用,通过一致的 UI 渲染(Skia 引擎)、热重载功能和响应式框架提高开发效率和用户体验。然而,Android 和 iOS 的系统差异、渲染机制及编译过程影响性能。性能对比显示,iOS 可能因硬件优化提供更流畅体验,而 Android 更具灵活性和广泛硬件支持。开发者可采用代码、资源优化和特定平台优化策略,利用性能分析工具提升应用性能。
【Flutter前端技术开发专栏】Flutter在Android与iOS上的性能对比
|
13天前
|
监控 Java Android开发
安卓应用开发:打造高效用户界面的五大策略
【4月更文挑战第29天】 在安卓应用开发的世界中,构建一个既美观又高效的用户界面(UI)对于吸引和保留用户至关重要。本文将深入探讨五种策略,这些策略可以帮助开发者优化安卓应用的UI性能。我们将从布局优化讲起,逐步过渡到绘制优化、内存管理、异步处理以及最终的用户交互细节调整。通过这些实践技巧,你将能够为用户提供流畅而直观的体验,确保你的应用在竞争激烈的市场中脱颖而出。
|
2天前
|
Java Android开发
Android开发--Intent-filter属性详解
Android开发--Intent-filter属性详解