首页 > 学院 > 开发设计 > 正文

浅谈Handler

2019-11-09 17:19:21
字体:
来源:转载
供稿:网友

1.什么是Handler?

Handler是android 中的一种功能处理机制,它既是一套UI更新机制,也是一种消息处理机制

常用场景:

        通知主线程更新UI

        MVP中进行传递数据

        ...

       每一个Handler实例是与拥有MessageQueue( 消息队列 )的线程关联的。每当你创建一个Handler,系统会默认绑定到对应的线程,并且它拥有MessageQueue。

       像对于一些新手小白在做应用时,通常会用一些封装好的网络框架去做后台服务器的数据请求。我们知道请求数据是一个耗时操作,是不能放在主线程去执行的,否则就等着ANR吧。他们呢就会在使用这些框架时,将获取的数据直接进行UI的更新。于是就会出现这个错误

ERROR/javaBinder(1029):android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.

这个错误意思就是不能在非主线程更新UI。

我们常常听到这句话,Android主线程是线程不安全的。——所以要在主线程来更新

       为什么呢?因为子线程可能会有多个,多个线程同时操作一个控件可能会有冲突发生,所以android就限定了只有主线程可以操作UI。子线程想操作UI就必须要通知主线程来更新。

这里可能有人会问,什么是线程安全,什么是线程不安全?

       简单的说,也就是你的代码如果对于线程来说原子操作或者多个线程之间的切换不会导致执行结果存在二义性,也就是说我们不用考虑同步的问题也就是线程安全的。

      线程安全问题往往都是由全局变量及静态变量引起的。

举个大学里老师常说的例子:

比如一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1; 而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。 那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。

Handler其实如果用熟悉了之后,能做很多有趣强大的事情。比如很多商场里的广告Banner轮播图,轮播时间也就是那个时延,都可以用它来实现,也很方便。

这里它的基本用法我就不讲解了,不过里面有一个需要提及下:

        Message message=new Message();        message.what=0;        message.obj=bean;        handler.sendMessage(message);而有的人这样写

        Message message=handler.obtainMessage();        message.what=0;        message.obj=bean;        handler.sendMessage(message);那么这两个message有什么区别呢,还是那句话,点进去看源码。。

/**     * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than     * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).     *  If you don't want that facility, just call Message.obtain() instead.     */    public final Message obtainMessage()    {        return Message.obtain(this);    }
/**     * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.     * @param h  Handler to assign to the returned Message object's <em>target</em> member.     * @return A Message object from the global pool.     */    public static Message obtain(Handler h) {        Message m = obtain();        m.target = h;        return m;    }上面的注释已经说的很清楚,它是从整个Messge池中返回一个新的Message实例,在许多情况下使用它,因为它能避免分配新的对象

那么通过调用obtainMessage方法获取Message对象就能避免创建对象,从而减少内存的开销了。既然Handler这么好,是不是随便使用,多多使用啊?我只能说好好用它,因为它还会可能导致你内存泄漏,这个词大家应该并不陌生。Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。一句话就是,该释放的没被释放常出现的场景:    当使用内部类来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用。而此时有一个耗时后台操作,比如网络请求,然后通过消息机制通知Handler,然后Handler把数据更新到界面。突然,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束。内存泄漏的危害:

  内存泄露的危害就是会使虚拟机占用内存过高,导致OOM(内存溢出)

  对于Android应用来说,就是你的用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;反复几次,程序占用内存超过系统限制,FC。

解决方法:   1.我们知道,静态的内部类不会持有外部类的引用,所以你需要将Handler声明为静态类,在Handler中增加一个对Activity的弱引用(WeakReference)。  
static class MyHandler extends Handler    {        WeakReference<Activity> mWeakReference;        public MyHandler(Activity activity)         {            mWeakReference=new WeakReference<Activity>(activity);        }        @Override        public void handleMessage(Message msg)        {            final Activity activity=mWeakReference.get();            if(activity!=null)            {                if (msg.what == 1)                {                    adapter.notifyDataSetChanged();                }            }        }    }2.在关闭Activity的时候停掉你的后台线程。如果你的Handler是被delay的Message持有了引用,使用removeCallbacks()方法,把消息对象从队列移除。

最常使用第一种方法。

这就是简单的一些Handler的介绍,希望对你们有用。

本人原创,图有雷同,纯属巧合,如转载或CV请标明出处,尊重原创,谢谢!


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表