美文网首页
Handler Message分析

Handler Message分析

作者: migill | 来源:发表于2019-11-30 21:00 被阅读0次
    1、什么是handler?

    handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另一个部分在消息队列中逐一将消息取出,然后对消息进行处理。


    1、handler内存泄漏的测试?
        private Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                startActivity(new Intent(MainActivity.this, PersonalActivity.class));
                return false;
            }
        });
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            test();
        }
    
        private void test() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message message = new Message();
                    message.obj = "handler内存泄漏测试";
                    message.what = 3;
                    SystemClock.sleep(3000);
                    handler.sendMessage(message);//跳转的第二个界面
                }
            }).start();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.e("handler内存泄漏测试 ", "onDestroy");
        }
    

    如上面代码,在进入MainActivity后,退出后,但是还会跳转到PersonalActivity界面。这个怎么解决呢?
    方法1:在发送消息的时候判断是否为空。在销毁activity的时候,handler移除对应的message和handler置空

      private void test() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message message = new Message();
                    message.obj = "handler内存泄漏测试";
                    message.what = 3;
                    SystemClock.sleep(3000);
                    //不为空的时候调用
                    if (handler != null) handler.sendMessage(message);
                }
            }).start();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.e("handler内存泄漏测试 ", "onDestroy");
            handler.removeMessages(3);
            handler = null;
        }
    

    方法2:延时发送消息,销毁activity的时候,移除对应的message

       private void test() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message message = new Message();
                    message.obj = "handler内存泄漏测试";
                    message.what = 3;
                    //延时发送消息
                    handler.sendMessageDelayed(message,3000);
                }
            }).start();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.e("handler内存泄漏测试 ", "onDestroy");
            handler.removeMessages(3);
        }
    

    2、为什么不能在子线程创建Handler

    //错误的使用     
    private void test() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        return false;
                    }
                });
                }
            }).start();
        }
    
      //正确的使用
      private void test2(){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Looper.prepare();//为子线程创建Looper  
                    new Handler() {
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            //子线程消息处理
                        }
                    };
                    Looper.loop(); //开启消息轮询
                }
            }).start();
        }
    

    直接在子线程创建Handler,会报如下错误,因为没有给子线程创建一个Looper对象。

    java.lang.RuntimeException: Can't create handler inside thread >Thread[Thread-2,5,main] that has not called Looper.prepare()
    at android.os.Handler.<init>(Handler.java:207)
    at android.os.Handler.<init>(Handler.java:119)
    at com.netease.handler.sample.MainActivity$4.run(MainActivity.java:98)
    at java.lang.Thread.run(Thread.java:919)




    主线程是什么时候创建Looper的呢?


    3、new Handler()两种写法有什么区别?

        private Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                startActivity(new Intent(MainActivity.this, PersonalActivity.class));
                return false;
            }
        });
    
        // 这是谷歌备胎的api,不推荐使用
        private Handler handler2 = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                textView.setText(msg.obj.toString());
            }
        };
    




    4、ThreadLocal用法和原理

    public class ThreadLocalTest {
    
        @Test
        public void test() {
            // 创建本地线程(主线程)
            final ThreadLocal<String> threadLocal = new ThreadLocal<String>() {
                @Override
                protected String initialValue() {
                    // 重写初始化方法,默认返回null,如果ThreadLocalMap拿不到值再调用初始化方法
                    return "冯老师";
                }
            };
            // 从ThreadLocalMap中获取String值,key是主线程
            System.out.println("主线程threadLocal:" + threadLocal.get());
    
            //--------------------------thread-0
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    // 从ThreadLocalMap中获取key:thread-0的值?没有,拿不到值再调用初始化方法
                    String value1 = threadLocal.get();
                    System.out.println("thread-0:" + threadLocal.get());
    
                    // ThreadLocalMap存入:key:thread-0  value:"熊老师"
                    threadLocal.set("熊老师");
                    System.out.println("thread-0  set  >>> " + threadLocal.get()); 
                    // 使用完成建议remove(),避免大量无意义的内存占用
                    threadLocal.remove();
                }
            });
            thread.setName("thread-0");
            thread.start();
    
            //--------------------------thread-1
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    // 从ThreadLocalMap中获取key:thread-1的值?没有,拿不到值再调用初始化方法
                    String value1 = threadLocal.get();
                    System.out.println("thread-1:" + threadLocal.get());
    
                    // ThreadLocalMap存入:key:thread-1  value:"刘老师"
                    threadLocal.set("刘老师");
                    System.out.println("thread-1  set  >>> " + threadLocal.get()); // 刘老师
                    // 使用完成建议remove(),避免大量无意义的内存占用
                    threadLocal.remove();
                }
            });
            thread.setName("thread-1");
            thread2.start();
        }
    }
    


    2、Handler+Message原理分析
    • Handler.sendMessage()发送消息时,会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息;
    • 通过Looper.loop()开启循环后,不断轮询调用MessageQueue.next();
    • 调用目标Handler.dispatchMessage()方法,然后调用handler中的handleMessage方法

    handler原理流程图


    sendMessage发送消息


    1、为什么主线程用Looper死循环不会引起ANR异常?
    因为在Looper.next()开启死循环的时候,一旦需要等待时 或 还没有执行到执行的时候,会调用NDK里面的JNI方法,释放当前时间碎片,这样就不会引发ANR异常了。
    2、为什么handler构造方法里面的Looper不是直接new?
    如果在Handler构造方法里面newLooper,怕是无法保证Looper的唯一,只有用Looper.prepare()才能保证唯一性,具体去看prepare方法。
    3、MessageQueue为什么要放在Looper私有的构造方法里初始化?
    因为一个线程只绑定一个Looper,所以在Looper构造方法李曼初始化就可以保证mQueue也是唯一的的,Thread对应一个Looper对应一个mQueue

    相关文章

      网友评论

          本文标题:Handler Message分析

          本文链接:https://www.haomeiwen.com/subject/fvsowctx.html