Andorid:Handler多种使用方式

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()方法,也没什么好说的。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值