欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Andorid:Handler多种使用方式

程序员文章站 2022-07-14 18:25:42
...

Hanlder的作用
相信做Andorid开发的工程师对Handler的使用都不陌生,那为什么还会有这篇文章呢,只是为了加强自己的记忆,好了言归正传,话说为什么Android要引入Handler呢?我想最本质的目的是为了实现跨线程通信的,那为什么要设计成只能通过Handler机制来更新UI呢?我想最本质的目的就是解决多线程并发的问题。就比如如果没有这套机制,有多个线程去同时更新UI,会出现什么问题,会是界面很混乱,无法控制更新UI的先后顺序,但这时会有人说,那就加锁呗,可加锁势必会影响响应速度,给用户的体验势必会下降。所以Handler机制就应运而生了。Handler机制就是不断的从消息队列中取消息,然后再发送到UI线程进行UI的更新。既然是消息队列,那肯定也就保证了消息的先进先出顺序执行了。
这篇文章的重点是Handler的使用,当然也会简单的分析一下源码, 至于Hanlder的工作原理请关注另一篇文章

Hanlder的使用
首先在Activity中先创建一个Handler对象如下:

private static Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Object object = msg.obj;
                    Bundle bundle = msg.getData();
                    int anInt = bundle.getInt("handler", 0);
                    Log.d("HandlerActivity:", "arg1:" + msg.arg1 + " arg2:" + msg.arg2 + " object:" + object.toString() + " bundleInt:" + anInt);
                    break;
                case 2:
                    Log.d("HandlerActivity:", "Message.obtain()");
                    break;
                case 3:
                    Log.d("HandlerActivity:", "handler.obtainMessage();");
                    break;

                default:
                    Log.d("liuy=default=", "handler");
                    break;
            }
        }
    };

细心的同学可能已经发现了,刚刚创建Handler的代码有一个警告,鼠标放上给出的提示是:This Handler class should be static or leaks might occur (null) more,大致意思是Handler类应该定义成静态类,否则可能导致内存泄露。所以有强迫症的同学要想去除这些警告,那就需要把Handler定义成静态类并加上WeakRefrence的机制,基本上可以避免出现泄漏的情况。但这种写法太繁琐了,所以就有了另一种写法,如下:

private static Handler handler2 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Log.d("HandlerActivity:", "handle---");
            return true;  
        }
    });

这种写法只是创建了一个静态对象,并不是匿名内部类,所以也就不会持有外部类的引用,就不会出现警告了。可这里又有一个返回值,那返回值的作用是什么呢?这就需要看下源码:
首先创建一个有参构造的Handler对象和Callback对象

 public Handler(Callback callback) {
        this(callback, false);
    }
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

再跟进入是:

    public Handler(Callback callback, boolean async) {
       ....省略....
        mCallback = callback;
       ....省略....
    }

这里我们只需知道把我们在外面创建的Callback对象赋值给了Handler的全局变量mCallback了。
当有消息需要处理时,会回调Hanlder的dispatchMessage()方法:

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

if (msg.callback != null)这条语句等到下一遍源码分析再讲解,先专注else里的代码,首先判断了下mCallback是否为空,既然我们在上边已经给他赋值了,那肯定不为空,那就会调用Callback的handleMessage方法,并把消息也传递过去,当消息处理完以后,如果返回false,那就会继续往下执行,执行第10行的handleMessage(msg)方法,当然这时又会出现内存泄漏的警告提示,所以一般我们都返回true就可以了。

Message的使用
Message对象获取有很多种,下面就一一介绍:

第一种:

    private void sendMsg0() {
        Message msg = new Message();
        msg.what = 1;
        msg.arg1 = 1;
        msg.arg2 = 2;
        msg.obj = new Object();
        msg.obj = new People("小明");
        Bundle bundle = new Bundle();
        bundle.putInt("handler", 1);
        msg.setData(bundle);
        handler.sendMessage(msg);
    }

msg.what是消息的识别码,便于在Handler的handleMessage方法中根据what识别出不同的消息
msg.arg1,msg.arg2是Message给我们提供的两个int类型的传递,是setData的低成本替代品
msg.obj可以传递一个对象
msg.setData()可以传递一个Bundle对象
Message对象可以直接new一个,这种方式官方是不建议的,因为可以使用消息池的对象。

第二种:

    private void sendMsg2() {
        Message msg = Message.obtain();
        msg.what = 2;
        handler.sendMessage(msg);
    }

通过obtain()先从消息池中获取Message对象,如果池中没有则创建一个对象并返回,空说无凭来看下源码:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

sPool是消息池中的一个对象
第3行先判断消息池中是否有对象,没有则直接new一个Message,有则把sPool赋值给Message并把消息池中的上一条赋值给sPool,然后再把消息池中的数量减一。
看到这肯定会有一个疑问,那什么时候赋值的呢,那就简单的介绍一下,具体介绍请看下一篇Handler的源码解析。
首先在Looper.loop()方法中对消息进行循环,消息执行完以后会放到消息池进行缓存

    public static void loop() {
        ....省略......
        for (;;) {
            ....省略......
            msg.recycleUnchecked();//消息进行回收
        }
    }
  void recycleUnchecked() {
        ....省略......
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;//把使用完的消息赋值给next
                sPool = this;
                sPoolSize++;//消息池数量+1
            }
        }
    }

看到这里应该能明白点了吧,其实就是把不用的Message放到消息池里边

第三种:

 private void sendMsg3() {
        Message msg = handler.obtainMessage();
        msg.what = 3;
        //只有通过handler获取的的Message对象,才能使用下边这种
        //方法发送消息
        msg.sendToTarget();
    }

这种写法其实就是把第二种写法又封装了下,可以看到里边还是调用了obtain()方法

    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;//把hanlder赋值给target
        return m;
    }

再来看一下sendToTarget()方法

    public void sendToTarget() {
        target.sendMessage(this);
    }

是不是和hanlder发送消息差不多呢,其实就是一样的,target就是handler。

Handler的post理解与用法
先看一下用法:

    private void sendMsg6() {
        handler.post(new Runnable() {
            @Override
            public void run() {
                Log.d("HandlerActivity:", "postRunnable");
            }
        });
    }

咋一看像是开了一个子线程,其实静心一想不对,开线程调用的是start()方法啊,这里什么都没调用,所以并没有改变当前的线程。先看下源码吧

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

再看下getPostMessage()方法

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

第2行还是从消息池中获取Message
第3行把Runnable对象赋值给callback
第4行返回Message

再看下sendMessageDelayed方法

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    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);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //把消息放到消息队列中
        return queue.enqueueMessage(msg, uptimeMillis);
    }

这里其实就是把Runnable对象封装到Message对象中并压入到消息队列,当消息队列中有消息后,就会循环消息,当消息执行完以后就会回调dispatchMessage方法

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

还记得开头说的这个if语句吗,当我们发送一个post方法时,已经给callback赋值了,所以这里走if语句,那再看下handleCallback方法

    private static void handleCallback(Message message) {
        message.callback.run();
    }

还记得message.callback是什么吗,就是把Runnable压入到Message对象中的Runnable对象,所以这个方法就是执行了下Runnable的run方法。说的有些啰嗦,大家理解就好。

Handler其他使用

    private void sendMsg4() {
        Message msg = handler.obtainMessage();
        handler.sendEmptyMessage(4);
        //SystemClock.uptimeMillis():从开机到现在的毫秒数(手机深度睡眠的时间不包括在内)
        //System.currentTimeMillis():获取的是系统的时间,可以使用SystemClock.setCurrentTimeMillis(long millis)进行设置
        //如果人为设置这个时间,那发送的延迟任务就不准了
        //sendMessageAtTime->enqueueMessage
        handler.sendEmptyMessageAtTime(5, 2000);//2000:延迟时间
        //sendMessageDelayed-> sendMessageAtTime->enqueueMessage
        handler.sendEmptyMessageDelayed(6, 2000);
        //enqueueMessage
        handler.sendMessageAtTime(msg, 2000);
        //sendMessageAtTime->enqueueMessage
        handler.sendMessageDelayed(msg, 2000);
        //删除消息
        handler.removeMessages(1);
    }

这几种发送消息的方法其实最终都是调用了enqueueMessage()方法,也没什么好说的。