一.Handler的常用用法

 

1.Handler在UI线程接收消息,在子线程或者UI线程发送消息

 

接收消息代码

    private Handler uiHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                case 1:                    int num1 = msg.arg1;                    int num2 = msg.arg2;                    String result = (String) msg.obj;                    Log.d("TAG", "主线程即UI线程 接收 主线程发送的Message num1: " + num1 + "  num2: " + num2 + "  result: " + result);                    break;                case 2:                    int num11 = msg.arg1;                    int num22 = msg.arg2;                    String result1 = (String) msg.obj;                    Log.d("TAG", "主线程即UI线程 接收 子线程发送的Message num1: " + num11 + "  num2: " + num22 + "  result: " + result1);                    break;            }        }    };

 

发送消息代码(子线程)

    private void sendMsgFromNewUIThread() {        new Thread(new Runnable() {            @Override            public void run() {                Message msg = Message.obtain();                msg.what = 2;                msg.arg1 = 33;                msg.arg2 = 44;                msg.obj = "子线程发送的消息";                uiHandler.sendMessage(msg);            }        }).start();    }

 

 

发送消息代码(UI线程) 一般不用

    private void sendMsgFromUIThread() {        Message msg = Message.obtain();        msg.what = 1;        msg.arg1 = 11;        msg.arg2 = 22;        msg.obj = "主线程发送的消息";        uiHandler.sendMessage(msg);    }

 

结果

主线程即UI线程 接收 主线程发送的Message num1: 11  num2: 22  result: 主线程发送的消息主线程即UI线程 接收 子线程发送的Message num1: 33  num2: 44  result: 子线程发送的消息

 

 

2.Handler在子线程接收消息

 

代码

    private void createNewThreadHandler() {        new Thread(new Runnable() {            @Override            public void run() {                new Handler().postDelayed(new Runnable() {                    @Override                    public void run() {                        Log.d("TAG", "延时200毫秒执行的代码...");                    }                }, 200);            }        }).start();    }

 

上述代码在大部分手机会报错,魅族测试机报错信息

即 Can't create handler inside thread that has not called Looper.prepare()

 

修改后的代码

    private void createNewThreadHandler() {        new Thread(new Runnable() {            @Override            public void run() {                new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {                    @Override                    public void run() {                        Log.d("TAG", "延时200毫秒执行的代码...");                    }                }, 200);            }        }).start();    }

 

结果

TAG: 延时200毫秒执行的代码...

 

报错原因

非主线程中默认没有开启Looper,而Handler对象必须绑定Looper对象,需要调用Looper.prepare()来给线程创建一个消息循环,调用Looper.loop()来使消息循环起作用。当然在new Handler对象时,在构造方法中传入Looper.getMainLooper()也可以解决报错的问题。

 

 

 

二.Handler详解

 

1.创建Message注意

在使用Handler进行线程通讯时,必不可少的要创建Message,而为了内存优化在创建Message时,最好不要new一个Message。

 

正确的写法

Message msg = Message.obtain();msg.what = 2;msg.arg1 = 33;msg.arg2 = 44;msg.obj = "Message发送的消息";

 

正确的写法

Message msg = handler.obtainMessage();msg.what = 2;msg.arg1 = 33;msg.arg2 = 44;msg.obj = "Message发送的消息";

备注

handler.obtainMessage();内部调用了Message.obtain();

如下

public final Message obtainMessage(){   return Message.obtain(this);}

 

 

错误的写法

Message msg = new Message();msg.what = 2;msg.arg1 = 33;msg.arg2 = 44;msg.obj = "Message发送的消息";

 

即,创建Message使用Message的缓存池。

 

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();    }

大致可以这样理解。通过Message缓存池方式获取Message对象时,首先判断缓存池中有没有Message,如果有就直接取出使用,如果没有这时才new一个Message。

 

 

2.Handler常用的几个方法

 

发送消息

Handler.sendMessage(msg);

 

延时XXX毫秒发送消息

Handler.sendMessageDelayed(msg,100);

 

UI线程刷新UI

Handler.post(new Runnable() {  @Override  public void run() {                          }});

 

UI线程延时XXX毫秒刷新UI

uiHandler.postDelayed(new Runnable() {    @Override    public void run() {                            }},100);

 

 

 

3.使用Handler时,注意内存泄露问题

 

为什么会出现内存泄露的问题

首先Handler使用是用来进行线程间通信的,所以新开的线程会持有Handler引用。如果在Activity等中创建Handler,并且是非静态内部类的形式,就有可能造成内存泄露。

 

首先,非静态内部类是会隐式持有外部类的引用,所以当其他线程持有了该Handler,线程没有被销毁,则意味着Activity会一直被Handler持有引用而无法导致回收Activity。

 

同时,MessageQueue中如果存在未处理完的Message,Message的target也是对Activity等的持有引用,也会造成内存泄露。

 

举例

匿名内部类 方式声明Handler

    private Handler uiHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {            }        }    };

 

这样的话,就有可能造成内存泄露。

 

解决方法

 

静态内部类+弱引用方式+Activity销毁时销毁Handler

 

静态内部类

    static class MyHandler extends Handler {        private WeakReference activityWeakReference;        MyHandler(Activity activity) {            activityWeakReference = new WeakReference<>(activity);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            if (null != activityWeakReference) {                Activity act = activityWeakReference.get();                if (ActivityUtils.isActivityIsAlive(act)) {                    switch (msg.what) {                        case 1:                            String result = (String) msg.obj;                            Log.d("TAG", "内部静态类方式声明Handler获取Msg----:" + result);                            break;                    }                }            }        }    }

 

 

Activity全局声明

private Handler handler = new MyHandler(this);

 

 

发送Message

    private void sendMsgFromNewThread() {        Message message = Message.obtain();        message.what = 1;        message.obj = "子线程发送Message";        handler.sendMessage(message);    }

 

 

销毁Handler

    @Override    protected void onDestroy() {        super.onDestroy();        if (null != handler) {            handler.removeCallbacksAndMessages(null);            handler = null;            Log.d("TAG", "销毁Handler");        }    }

 

 

4.Handler+Looper+MessageQueue+Message

 

上面提到在子线程中声明Handler时,要手动添加Looper.prepare()和Looper.loop()方法【也可 new Handler(Looper.getMainLooper())】那么在主线程(UI线程)中为什么不需求呢。

 

因为APP初始化的时候都会执行ActivityThread的main方法,代码如下

public static void main(String[] args) {    ...    Looper.prepareMainLooper();    ActivityThread thread = new ActivityThread();    thread.attach(false);    if (sMainThreadHandler == null) {        sMainThreadHandler = thread.getHandler();    }    ...    Looper.loop();}

 

  图解


 

  

 

更多相关文章

  1. 在Android中使用ExecutorService、Executors、Feature
  2. Android故障积累----(1/N)
  3. 监听方法Android之Home键监听封装
  4. (转)Android中Handler引起的内存泄露
  5. Android(安卓)- webview通过js调用Android方法
  6. Android(安卓)Handler removeMessages引发postDelayed失效的问题
  7. android UEventObserver的用法
  8. PackageManagerService(Android5.1)深入分析(四)安装应用
  9. Android跨进程通信IPC之16——Binder之native层C++篇--获取服务

随机推荐

  1. 如何部署Bower安装的软件包?
  2. 用WordPress决定jQuery的版本?
  3. jQuery无法从localhost检索数据
  4. 如何使用jquery ajax获取api数据?
  5. 用jquery 绑定一个按钮click事件后,第一次
  6. Jquery中的队列函数quene()、dequene()、
  7. JQUERY中做表单验证,谁有带时间的日期选择
  8. 请问jquery如何选择tr下的n个tr?
  9. 如何检测jQuery中的水平滚动?
  10. jquery自定义事件