Handler学习

参考博客:Android Handler消息机制原理最全解读(持续补充中)

1.什么是Handler?

Handler是Android向我们提供的一种消息传递机制,来帮助我们将子线程的数据传递给主线程.因为在我们的开发中,耗时的操作是要放在子线程中执行,我们需要将子线程中执行的结果通知到主线程,来去做一些UI的更新.

2.原理及使用.

2.1. 常规使用

private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    Toast.makeText(mContext,"收到来自非主线程的消息1",Toast.LENGTH_SHORT).show();
                    break;
                case 2:
                    Toast.makeText(mContext,"收到来自非主线程的消息2",Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };


private Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            Message message = mHandler.obtainMessage();
            message.what = 1;
            message.arg1 = 2;
            message.obj = "收到来自非主线程的消息1";
            mHandler.sendMessage(message);
        }
    });

① 主线程中创建Handler

② 重写handleMessage()方法,该方法传入了Message参数.(来自其他线程的message)

③ 子线程通过sendMessage()方法发送信息.该方法需要传入一个Message参数.

     通过Handler的obtainMessage()方法获取到一个Message实例.  Message的几个属性:

     Message.what    --------->      用来标识信息的int值,主线程可据此判断来自不同地方的信息源. 

     Message.org1/Message.org2   ---------------->    如果只需要存储几个整数值,arg1和arg2是较低成本的替代方法.

     Message.obg  ------------------->   传递消息的任意对象.

④ 消息发出后,Handler所在的线程通过handleMessage()方法就可以收到具体的消息了,通过msg.what区分信息源.

2.2 线程间通信

Handler不仅仅能将子线程的数据发送给主线程,还适用于任意两个线程之间的通信.创建两个线程进行通信.

private Handler mHandler;

    private Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
//            Looper.prepare();
            mHandler = new Handler(){
                @Override
                public void handleMessage(final Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what){
                        case 1:
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    Toast.makeText(mContext, (String) msg.obj,Toast.LENGTH_SHORT).show();
                                }
                            });
                            break;
                        case 2:
                            break;
                    }
                }
            };
//            Looper.loop();
        }
    });
    private Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            Message message = mHandler.obtainMessage();
            message.what = 1;
            message.arg1 = 2;
            message.obj = "收到来自非主线程的消息1";
            mHandler.sendMessage(message);
        }
    });

创建了两个Thread,thread接收处理消息,thread1进行发送消息.运行发现报错,说我们创建的handler没有调用Looper.prepare()方法,在handler实例化前加上该方法,发现虽然不报错,但是thread并没有接收到消息,在handler实例化之后加上Looper.loop()方法,运行后可以接收到消息.接着进行Handler原理分析.

为什么在子线程中实例化Handler不调用Looper.prepare()就会报错呢?

当我们在new Handler时.

mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;

要想在线程里创建handler对象,必须先调用Looper.prepare()方法.


将当前线程初始化为循环程序。这使您有机会在实际开始循环之前创建引用该循环程序的处理程序。请确保在调用此方法后调用{@link #loop()},并通过调用{@link #quit()}结束该方法。

也就是说只有调用该方法后,才有机会创建该线程的handler.
 public static void prepare() {
        prepare(true);
    }

    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.

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

Looper的构造函数里又为我们创造了一个MessageQueue对象.

主线程中创建Handler不需要调用Looper.prepare()方法和Looper.loop()是因为在App初始化的时候都会执行ActivityThread的main方法.

Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

在创建主线程的时候Android已经调用了Looper.prepareMainLooper()方法和Looper.loop()方法,所以我们在主线程中可以直接创建handler使用.

Handler发送消息的过程.

在绝对时间(以毫秒为单位)uptimeMillis之前的所有挂起消息之后将消息排队到消息队列中。时基是{@link android.os.SystemClock#uptimeMillis}。你将收到它在{@link #handleMessage},在线程附加到这个处理程序。

个人理解: 该方法就是将消息添加到消息队列中 在设置的时间发出 在我们的handler线程中我们将会收到该消息

参数: Message对象 uptimeMillis 传递消息的绝对时间

如果成功地将消息放入消息队列,则返回true。失败时返回false,通常是因为处理消息队列的looper正在退出。注意,true的结果并不意味着该消息将被处理——如果looper在消息的交付时间之前退出,那么该消息将被删除。

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

sendMessage关键在于enqueueMessage()方法.

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

enqueueMessage()方法内部调用了MessageQueue的enqueueMessage()方法.

boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

        synchronized (this) {
            if (mQuitting) {
                RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            }

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

没太看懂,有点懵.借助参考博客的理解:
Message被存入到MessageQueue时,是将Message存入到了一个Message.next上.形成了一个链式的列表,同时也保证了Message列表的时序性.

Message的发送实际是放入到了Handler对应线程的MessageQueue中,Message如何被取出的.看下Looper.loop()方法.

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        // 调用该方法前 需先调用Looper.prepare()方法,否则抛出异常.
        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();

        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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(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.recycle();
        }
    }

该方法的注释: 在这个线程中运行消息队列.意思是handler发送消息会把消息加入到消息队列当中,但需要调用该方法让消息队列运行起来,不然我们收不到消息.
myLooper()方法里取出了当前线程的Looper对象,然后在Looper对象内开启了一个死循环 不断从Looper内的MessageQueue中取出Message,只要有Message对象,就会通过Message的target调用dispatchMessage去分发消息,target就是我们创建的handler

分发消息

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,则会直接调用handleCallback()方法,即直接调用run方法.如果没有设置,则直接调用handler本身的handleMessage方法,由于handler是在主线程中创建的,所以handleMessage()方法也会执行在主线程中.

3. 总结

1.使用handler时候,首先要保证调用了Looper.prepare()方法,主线程中已经默认调用过,可以直接创建handler.该方法会将我们当前的线程初始化为一个循环程序,即创建出唯一的Looper对象.每个线程只有一个Looper.Looper对象内部又维护有唯一的一个MessageQueue,所以一个线程可以有多个handler,但只能有一个Looper和一个MessageQueue.,

2.Message在MessageQueue不是通过一个列表来存储的,而是将传入的Message传入到上一个Message的next中,在取出的时候通过顶部的Message就能按放入的顺序依次取出Message.

3.Looper对象通过loop()方法让消息队列在该线程中运行起来,不断从消息队列中取出Message.然后将消息分发给handler.message.target即为创建的handler,这样消息就传到了handler所在的线程.

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android handler机制是一种在Android中用于处理异步消息和线程通信的重要机制。其主要作用是将消息和任务抛到主线程的消息队列中,以便主线程按照一定的规则按照队列中的顺序处理这些消息和任务。 1. Handler的实例化:在Android中,创建一个Handler的实例可以通过以下两种方式实现: 直接实例化: 创建一个Hanlder对象,代码示例: Handler handler = new Handler(); 绑定Looper:在子线程中创建Handler的话,需要先通过以下代码获取Looper对象,然后再将其传入Handler的构造函数中: Looper looper = Looper.myLooper(); Handler handler = new Handler(looper); 2. Handler的使用:当一个线程需要将一个耗时的任务交给主线程去执行的时候,它可以使用Handler来完成。以下是Handler的常见用法示例: 使用Handler处理消息: Handler myHandler = new Handler() { public void handleMessage(Message msg) { //在这里处理消息 } }; Message msg = myHandler.obtainMessage(); Bundle bundle = new Bundle(); bundle.putString("my_key", "my_string"); msg.setData(bundle); myHandler.sendMessage(msg); 使用Handler更新UI: Handler myHandler = new Handler() { public void handleMessage(Message msg) { //更新UI } }; myHandler.sendEmptyMessage(0); 3. Handler的原理:在Android系统中,所有的UI操作都必须在主线程上完成。这时,Handler就扮演了一个传递任务的中介角色。它把来自子线程的消息和任务封装成Message对象,最后以一定的顺序扔到主线程的消息队列中等待执行。主线程通过Looper不断地循环读取消息队列中的消息和任务,然后依次执行。这就是Handler机制的基本原理。 4. Handler配合其他机制使用:为了更好地发挥Handler的作用,还可与一些其他机制有效配合使用,例如:AsyncTask、MessageQueue等等。通过多种机制的相互配合,可以实现复杂的异步消息处理和并发控制,提升了Android应用程序的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值