Android基础篇 Handler

Posted 彭老希

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android基础篇 Handler相关的知识,希望对你有一定的参考价值。

一、Handler作用

1)传递消息Message

Message 的构造函数是公共的,但获得其中之一的最佳方法是调用Message.obtain()或Handler.obtainMessage()方法之一,这将从回收对象池中提取它们

//1.通过handler实例获取
Handler handler = new Handler();
Message message = handler.obtainMessage();

//源码 :第一种获取方式其实也是内部调用了第二种
public final Message obtainMessage(){
    return Message.obtain(this);
}
//2.通过Message获取
Message message = Message.obtain();

不建议直接new Message,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后系统放入缓存池,会占用很多内存的

//传递的数据
Bundle bundle = new Bundle();
bundle.putString("msg", "传递我这个消息");
//发送数据
Message message = Message.obtain();
message.setData(bundle);   //message.obj=bundle  传值也行
message.what = 0x11;
handler.sendMessage(message);
 
 
 
//数据的接收
final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0x11) {
            	//直接获取,不需要在new
                Bundle bundle = msg.getData();
                String date = bundle.getString("msg");
            }
        }
};

2)子线程通知主线程更新ui

        //创建handler
        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (msg.what == 0x11) {
                    //更新ui
                          ......
                }
            }
        };
 
        new Thread(new Runnable() {
            @Override
            public void run() {
                //FIXME 这里直接更新ui是不行的
                //还有其他更新ui方式,runOnUiThread()等          
                message.what = 0x11;     
                handler.sendMessage(message);  
            }
        }).start();

二.常用API

    //消息
   		Message message = Message.obtain();
    //发送消息
        new Handler().sendMessage(message);
    //延时1s发送消息
        new Handler().sendMessageDelayed(message, 1000);
    //发送带标记的消息(内部创建了message,并设置msg.what = 0x1)
        new Handler().sendEmptyMessage(0x1);
    //延时1s发送带标记的消息
        new Handler().sendEmptyMessageDelayed(0x1, 1000);
    //延时1秒发送消息(第二个参数为:相对系统开机时间的绝对时间,而SystemClock.uptimeMillis()是当前开机时间)
        new Handler().sendMessageAtTime(message, SystemClock.uptimeMillis() + 1000);
 
    //避免内存泄露的方法:
    //移除标记为0x1的消息
        new Handler().removeMessages(0x1);
    //移除回调的消息
        new Handler().removeCallbacks(Runnable);
    //移除回调和所有message
        new Handler().removeCallbacksAndMessages(null);

三.handler使用避免内存泄露

1)handler怎么使用会产生内存泄露?

public class MainActivity extends AppCompatActivity {
 
    final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
                ......
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //activity被执行时,被延迟的这个消息存于主线程消息队列中1分钟,
        //此消息包含handler引用,而handler由匿名内部类创建,持有activity引用,
        //activity便不能正常销毁,从而泄露
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                ......
            }
        }, 1000 * 60);
    }
}

2)如何避免handler的内存泄露?

public class MainActivity extends AppCompatActivity {
 
    private static final Runnable myRunnable = new Runnable() {
        @Override
        public void run() {
            //执行我们的业务逻辑
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyHandler myHandler=new MyHandler(this);
        //延迟5分钟后发送
        myHandler.postDelayed(myRunnable, 1000 * 60 * 5);
    }

	//创建静态内部类
    private static class MyHandler extends Handler{
        //持有弱引用MainActivity,GC回收时会被回收掉.
        private final WeakReference<MainActivity> mAct;
        public MyHandler(MainActivity mainActivity){
            mAct =new WeakReference<MainActivity>(mainActivity);
        }
        @Override
        public void handleMessage(Message msg) {
            MainActivity mainAct=mAct.get();
            super.handleMessage(msg);
            if(mainAct!=null){
                //执行业务逻辑
            }
        }
    }
}

3)雷区

a)Handler.post(Runnable)其实就是生成一个what为0的Message,调用

myHandler.removeMessages(0);

会使runnable任务从消息队列中清除。

b) 子线程直接创建Handler,抛异常Can’t create handler inside thread that has not called Looper.prepare()

原因是非主线程没有Loop对象,所以要调用Looper.prepare()方法,而且如果主线程给子线程发送消息,还要调用一个Looper.loop()的方法(此方法保证消息队列中的消息被不停的拿出,并被处理)

class MyThread extends Thread{
        @Override
        public void run() {
            super.run();
            Looper.prepare();
            Handler handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    //处理消息
                }
            };
            Looper.loop();
        }
}

c)activity如被finish,但是handler刚好还在处理消息,如果需要用的资源已被释放,则会出现空指针异常。

所以在ondestory中去remove掉我们要处理的事件,还是有必要的。不想处理就直接try catch或者判空。

d)有时候你会发现removeCallbacks会失效,不能从消息队列中移除。

出现这情况是activity切入后台,再回到前台,此时的runnable由于被重定义,就会和原先的runnable并非同一个对象。所以这么做,加上static即可

static Handler handler = new Handler();
static Runnable myRunnable = new Runnable() {
        @Override
        public void run() {
            //执行我们的业务逻辑
        }
    };

以上是关于Android基础篇 Handler的主要内容,如果未能解决你的问题,请参考以下文章

Android Studio - 如何从片段中停止 handler.postDelayed?

Android之WIFI-基础篇NativeDaemonConnector分析

从Handler+Message+Looper源代码带你分析Android系统的消息处理机制

salesforce 零基础学习(五十二)Trigger使用篇

Android多线程编程之Handler篇(消息机制)

Android多线程编程之Handler篇(消息机制)