Handle的使用场景

作者: 北国雪WRG | 来源:发表于2019-03-01 20:15 被阅读10次

    Handler运行机制

    Handler的消息机制如下图所示,主要包含两个消息队列,一个是消息的回收队列,另一个是Message Q队列。

    • 1 消息的回收队列:消息回收队列是为Handler提供消息对象的,当Handler需要发送消息时,首先从消息回收队列中获取已被清空数据的消息对象,若消息对队列中此时没有消息对象,则创建新的消息对象。当消息对象被使用后,不会直接被当做垃圾回收,而是会进入消息的回收队列,在该队列中会将消息对象上的所有数据清空,之后在队列中等待被使用。

    • 2 获取消息 :直接通过以下两种方式中的任一种获取消息。

            Message message = Message.obtain(); // 从消息回收队列中回收
            Message message1 = new Message(); // 创建新的消息
    
    • 3 创建Handler对象: 直接通过如下方式创建Handler对象即可。该Handler默认使用当前线程提供的Looper,主线程和HandlerThread会提供looper其他线程需要手动创建。

                Handler handler = new Handler(); // 使用创建线程的loop管理消息
                Handler handler = new Handler(looper); // 使用指定的looper管理消息
      
    • 4 发送消息:通过如下等方式进行消息的发送。

            handlerUI.sendEmptyMessage(11);//立刻发送空的消息
            handlerUI.sendMessage(message);//立刻发送携带数据的消息
            handlerUI.sendMessageDelayed(message,1000);//延迟1000ms后发送携带数据的消息
            handlerUI.post(runnable);// 切换回主线程执行run方法,注意runnable的run()是可以被单独执行的
    
    • 5 Message Q队列:消息队列,用来管理消息,会根据消息的执行时间对消息进行排序。并通过Looper.loop对消息进行轮询取出,当队列中有消息时就将消息取出交给HandlerhandlerMessage()回调进行消息的处理,当队列中没有消息时就进行阻塞等待,源码中就是个for死循环....

    • 6 消息的处理:通过Looper.loop()取出消息队列中待处理的消息,通过handler的handlerMessage()回调进行消息的处理。处理完成的消息对象会进入消息的回收队列进行回收。

    handler 机制

    使用Handler向主线程发送消息

    这个的关键是使用主线程的looper创建Handler

    • 1 主线程->主线程 发送消息:在主线程中通过创建好的handler调用sendMessage()方法发送消息即可。

    • 2 子线程->主线程 发送消息:在子线程中调用主线程中创建的handler调用sendMessage()方法发送消息即可。

    本次以子线程向主线程发送消息为例:

    public class MainActivity extends AppCompatActivity {
    
        private TextView textView;
        private static final int HANDLER_UI = 1;
    
        //运行在主线程的Handler:使用Android默认的UI线程中的Looper
        public Handler handlerUI = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case HANDLER_UI:
                        String strData = (String) msg.obj;
                        textView.setText(strData);    
                        break;
    
                    default:
                        break;
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            textView = (TextView) findViewById(R.id.tv);
            findViewById(R.id.btn_ui).setOnClickListener(v->{
                new Thread(()->{
                    Message message = Message.obtain();
                    message.what = HANDLER_UI;
                    message.obj = "发送消息的线程名称:" + Thread.currentThread().getName();
                    handlerUI.sendMessage(message);
                }).start();
            });
        }
    }
    
    

    运行结果如下:

    image

    上述代码有一定的隐患,因为如此使用Handler会导致内存泄露,此处是为了举例简洁才如此使用,正常使用Handler时要避免这种方式,解决这种导致内存泄露可通过——WeakReference(弱引用),后面会详细介绍。

    使用Handler向子线程发送消息

    使用子线程的looper创建handler

        private Handler threadHandler;  
        private static final int HANDLER_THREAD = 2;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            createLooperHandler();
    
            Message message = Message.obtain();
            message.obj = "发送的线程:" + Thread.currentThread().getName();
            threadHandler.sendMessage(message);
        }
    
        /**
         * 创建一个可以包含looper的子线程,并开启
         */
        private void createLooperHandler() {
            MyThread myThread = new MyThread();
            myThread.start();
    
            threadHandler = new Handler(myThread.threadLooper) {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    //添加上当前线程名称
                    String thrMsg = (String) msg.obj + "\n 收到的线程:" + Thread
                                  .currentThread().getName();
                    }
                }
            };
        }
    
    // 创建带有looper的线程,可以用HandlerThread替代
        private class MyThread extends Thread {
            private Looper threadLooper;
            @Override
            public void run() {
                Looper.prepare();
                threadLooper = Looper.myLooper();
                Looper.loop();
            }
        }
    }
    
    

    HandlerThread的使用

    HnadlerThread是Thread的子类,是专门向子线程发送消息使用的。使用步骤如下:

     private Handler handlerThreadHandler;
     private static final int HANDLER_THREAD_HANDLER = 3;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            createHandlerThread();
    
            message.obj = "发送的线程:" + Thread.currentThread().getName();
            handlerThreadHandler.sendMessage(message);
        }
    
        private void createHandlerThread() {
            HandlerThread handlerThread = new HandlerThread("handler_name1");
            handlerThread.start();
    
            handlerThreadHandler = new Handler(handlerThread.getLooper()) {
    
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    String thrMsg = (String) msg.obj + "\n 收到的线程:" + Thread.currentThread().getName();
                }
            };
        }
    
    

    Handler内存泄露

    前面说到,使用Handler时最需要注意的就是内存泄露问题,内存泄露通俗来讲就是:对象使用完成将要回收,但是对象的引用还被其他类所持有,导致对象无法被GC回收。当前通用的解决方案是:Handler使用静态类,内部通过弱引用的方式来持有对象的引用。具体如下:

    public static class MyHandler extends Handler {
            WeakReference<Activity> mWeakReference;
    
            public MyHandler(Activity activity) {
                mWeakReference = new WeakReference<Activity>(activity);
            }
    
            @Override
            public void handleMessage(Message msg) {
                Activity activity = mWeakReference.get();
                if (activity == null) {
                    return;
                }
    
                switch (msg.what) {
                    case 101:
                        adapter.notifyDataSetChanged();
                        break;
    
                    default:
                        break;
                }
            }
        }
    
    

    Handler 中post和send的区别

    有的时候我们想把一个动作传到主线程中执行,比如一个方法。

    1. 我们可以用Handler.post(new Runnable(){})来发送。执行的动作写在run方法中即可。
    2. 如果传入的是runnable,handler会自动将其封装为message
    3. looper获取到了message之后,会判断是否存在runnable对象。如果存在则直接执行runnable.run()。注意这里执行的不是runnable.start()
    4. 所以最后run是在looper所在线程执行的,而不是在子线程中执行。

    Android开发中对Handler使用的一些总结 删改自该文

    从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露) 引用该文

    相关文章

      网友评论

        本文标题:Handle的使用场景

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