什么是Handler机制?
handler是一种消息处理机制,用于线程之间通信。由于主线程不能做耗时操作,会导致ANR,子线程不能刷新ui,我们在做网络请求,或数据库操作等时,因为这些原因,所以我们就需要使用handler进行线程之间通信。
handler机制分为三部分: handler looper messagequeue
looper:消息轮询,不断在这里取消息,交给handler去处理
hander:发送消息,处理消息
messagequeue:消息队列,存储消息,遵循先进先出原则
我们创建出handler实例,通过sendmessage方法将消息发送出去,该方法调用了sendMessageDelayed(消息延时)的方法,此方法中做了一个延时判断,如果delayMillis<0,给他赋值为0表示立即发送,因为是延时发送,不可能出现负值。然后返回sendMessageAtTime方法,这个方法会帮我们拿到一个消息队列(mqueue,在looper的构造方法取出),通过enqueueMessage方法帮我们把拿到的queue,msg,以及uptimeMillis时间传递下去,在这个方法中又对msg.tager=this赋值,让它等于本类对象(handler),然后调用queue.enqueueMessage(msg, uptimeMillis)消息队列enqueueMessage方法,做判断对消息排序,谁优先谁执行,遵循先进先出原则。我们把消息发送出去,存放到messagequeue中,然后通过looper.loop()不断轮询取消息,通过msg.targer.dispatchMessage,也就是handler进行处理消息。而我们的looper在prepare方法中创建我们的looper实例,存放到ThreadLocal中(ThreadLocal相当于存放我们loop实例的一个容器,可以保证多个线程并发不相互干扰),同时做了一个判空处理,不为空就会抛一个异常,也保证了我们一个线程只有一个looper实例,在looper的构造方法中,创建了一个消息队列,同时创建了一个当前线程。looper.loop()方法,我们会拿到myloop的实列,通过实例拿到queue,消息队列通过.next方法把消息取出来,没有消息就结束,有消息交由msg.targer.dispatchMessage来处理(targer就是我们的handler),在处理的时候,我们会拿到系统当前开始的一个时间,进行处理,处理完给他结束时间。dispatchMessage()做了一个判断,msg.callback==null会走handleMessage,这是一个空方法,我们在创建handler时,会重写这个方法,所以我们的操作是在这里实现的(我们在handler的构造方法中可以看到我们的callback为null);如果我们做定时器时,使用postDelayed时它也是调用了sendMessageDelayed这个延时方法,它的参数getPostMessage当中把Runnable赋值给了我的callback,所以当它不为空时,会调用handleCallback方法,在run方法做我们的具体操作。
注意:在子线程创建handler,我们首先需要调用loop.parper()方法,创建完成后,调用looper.loop()方法,否则会报“没有loop.parper()方法”的异常,而在主线程创建handler时,系统已经帮我们创建好looper实列,在activitythread的main方法中,looper.prepareMainLooper()中知道prepareMainLooper可以拿到myLooper,而这个方法就是通过threadlocal拿到我们保存的looper对象。所以主线程可以不调用loop.parper()这个方法。
在使用handler出现的问题:内存泄漏
首先,handler很多时候也是作为一个内部类使用,改为静态内部类,因为静态内部类的实例对象不会和外部类的实例对象绑定。它只能访问外部类的静态成员变量或者静态方法,而静态的生命周期和application一样长,所以不会影响activity的回收。其次,handler经常执行一些耗时的操作,如果耗时任务还没有执行完成,而这个时候activity已经销毁,当gc回收的时候发现activity仍被持有,导致不能被回收,所以一般在activity销毁的时候(也就是onDestroy的时候)将所有消息清空(removeCallbacksAndMessages)并将handler置空。
内存泄漏的检测:在Androidstudio3.0以后,通过Android profiler(3.0以前使用Android monitor)中的memery选项,查看内存使用情况,如果检测某一个activity中是否存在内存泄漏,可以将该activity进行finish,然后手动触发GC,再对项目的包进行捕捉查看,如果gc后发现在对应包下,仍然存在改activity引用,说明该activity已经发生了泄漏,可以根据引用的对象,进行初步排查,当然不管使用studio自带的检测工具,还是使用MAT工具,只能检测出大部分的泄漏问题,或者有时候即使发现泄漏,很难确定位置,这个时候主要还是靠开发人员通过经验判断逐步排查。