Android 消息机制之深入学习Handler

前言:前面几篇博客分析了消息机制大家庭中的MessageQueue,Looper。其中还有一个必不可少的成员--Handler。Handler在消息机制中的作用的发送消息。下面我们就来认识一下Handler。

一,使用场景

      Handler的实用场景算是老生常谈的问题了,说的较多的是,系统不允许在子线程中访问UI。为什么尼?主要还是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。那么问题又来了,为什么系统不对UI控件的访问加上锁机制尼?主要缺点有两个:第一,加上锁机制会让UI访问的逻辑变得复杂;第二,锁机制会降低UI的访问效率,因为锁机制会阻塞某些线程的执行。所以Android系统还是使用了单线程模型(常说的主线程)来操作UI控件。

二,源码分析

1,构造函数

public Handler(Callback callback, boolean async) {
           //可能发生内存泄漏警告,Handler的使用稍不留神就有可能发生内存泄漏,具体的留在后面分析
            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());
            }
        }
        //获取Looper对象,主线程在ActivityThread中创建了MainLooper,在子线程中创建Handler之前必须调用Looper.prepare();
        //为当前线程指定Looper对象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
       //获取消息队列MessageQueue,在初始化Looper的时候会生成与之对应的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }//可能发生内存泄漏警告,Handler的使用稍不留神就有可能发生内存泄漏,具体的留在后面分析
            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());
            }
        }
        //获取Looper对象,主线程在ActivityThread中创建了MainLooper,在子线程中创建Handler之前必须调用Looper.prepare();
        //为当前线程指定Looper对象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
       //获取消息队列MessageQueue,在初始化Looper的时候会生成与之对应的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
  public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
  public interface Callback {
        public boolean handleMessage(Message msg);
    }

Handler的构造函数比较多,但是归根结底只有上面两个。默认async是false也就是非异步的,callback是一个接口,有一个Boolean类型的方法handleMessage();处理接收到的消息,返回true表示消息不需要进一步处理

2,发送消息

 public final boolean post(Runnable r)
 public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean postAtFrontOfQueue(Runnable r)
public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis) 
public final boolean sendMessageAtFrontOfQueue(Message msg)

一眼看下去是不是觉得Handler发送消息的方法这么多,仔细看一下源码其实只有两种sendMessageAtTime(Message msg,long uptimeMillis);,sendMessageAtFrontOfQueue(Message msg);。

   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);
    }
  public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }
  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

通过源码可以发现sendMessageAtTime(Message msg,long upTimeMillis);和sendMessageAtFrontOfQueue(Message msg);几乎是一样的最后都是调用了enqueueMessage(Message queue,Message msg, long uptimeMillis);唯一的不同是sendMessageAtFrontOfQueue(Message msg);中调用enqueueMessage();的第三个参数为0。在enqueueMessage(Message queue,Message msg, long uptimeMillis);调用了消息队列MessageQueue的enqueueMessage();前面的文章中详细分析了MessageQueue,详细分析了enqueueMessage();点击打开链接。enqueueMessage();是向MessageQueue中插入消息,uptimeMillis为0表示这个消息插入队列中队首的位置。但是sendMessageAtFrontOfQueue(Message msg);这个方法使用时得谨慎,系统给出的注释是

 

This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects

意思是这个方法使用在非常特殊的情况下,因为它很容易导致消息队列排序问题,会产生意想不到的副作用。uptimeMillis >0时即使成功将消息插入消息队列中即返回true消息也不一定会被处理,如果looper被退出了而消息还没有到预定的处理时间,消息就会别丢弃掉。

3,处理消息

  /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
  /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
 private static void handleCallback(Message message) {
        message.callback.run();
    }

Android 消息机制之深入学习Looper中分析loop();方法时有着一段代码:

 

msg.target.dispatchMessage(msg);

当时说了这里的msg.target是指Handler,这段代码其实是调用了Handler的dispatchMessage();可以发现Looper获取消息交给Handler来处理。

dispatchMessage(msg);方法也比较简单,先时获取Message对象的callback(Handler调用post系列方法发送消息时传入的Runnable),如果不为null调用Runnable的run方法处理消息,如果为null调用handleMessage(msg);方法处理消息。handleMessage(msg);是一个空方法是子类必须实现的,在这个方法中进行消息处理逻辑。

三,内存泄漏

1,原因

上面对Handler源码分析的时候提到过消息泄漏。是的,Handler在使用过程中很容易产生消息泄漏这已经不是陌生的话题了。主要原因是Handler创建时是内部类,内部类会持有外部类的强引用导致外部类不能够被GC及时回收。Android 的消息机制体系中存在着阻塞循环,MessageQueue中如果一直有消息,Handler就一直不会被释放从而外部类就会一直被Handler所持有导致无法被释放。

2,解决方法 

   1>将非静态内部类Handler和Runnable转为静态内部类;

   2>对外部类的使用改成弱引用

3,示例

public class SecondActivity extends Activity {

    private Handler mHandler = new MyHandler(this);
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handlerTest();
    }

    private void handlerTest() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 0;
                message.obj = "我是要发送的消息";
                mHandler.sendMessage(message);
            }
        }).start();
    }

    public static class MyHandler extends Handler{
        WeakReference<SecondActivity> mWeakReference;

        private MyHandler(SecondActivity activity) {
            mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    String obj = (String) msg.obj;
                    Toast.makeText(mWeakReference.get(), obj, Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值