Handler使用的整理

作者: 蜡笔小州 | 来源:发表于2017-10-30 15:14 被阅读66次
    介绍
    1. Message
      消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。
    2. Message Queue
      消息队列,用来存放通过Handler发布的消息,//按照先进先出执行(这句话是错的!)。
    3. Handler
      有俩用途:1、用于子线程与主线程之间的通讯 2、用于向子线程发出消息请求。Handler是Message的主要处理者,是Android提供的一套ui处理机制,负责将Message添加到消息队列以及对消息队列中的Message进行处理。
    4. Looper
      循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。
    5. 线程
      UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue。
    异常
    • Only the original thread that created a view hierarchy can touch its views.
      这个异常就是在子线程中进行更新ui操作抛出的,android的设计的时候,就封装了一套消息创建、传递、处理机制。比如我们不按要求在子线程去更新ui的话就会抛出以上异常。不过我们看一下下面的代码:
    public class MyActivity extends Activity {
        private TextView tvText;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            tvText = (TextView) findViewById(R.id.main_tv);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    tvText.setText("OtherThread");
                }
            }).start();
        }
    }
    
    

    我们知道这段代码中在子线程进行了ui的操作,不过我们的代码是可以运行的,我们看一下定义异常的地方就知道了。

    public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { ………………………… 
          void checkThread() { 
          if (mThread != Thread.currentThread()) { 
                throw new CalledFromWrongThreadException( "Only the original        thread that created a view hierarchy can touch its views.");
           }
        } 
    …………………… 
    }
    

    setText在源码中会调用checkforlayout方法然后invalidate。然后在checkThread里面判断thread和uiThread是否相等然后抛异常。
    不能更新ui是因为ViewRootImpl的checkThread()的检查,而ViewRootImpl是在onResume()的方法中创建的,所以在onCreate中没有穿件ViewRootImpl,所以不抛异常,如果耗时的话才会抛出异常。比如我们在setText前面加上一个延迟。

    • 内存泄漏
    @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);  
            mHandler.sendMessageDelayed(Message.obtain(), 10000);  
            finish();  
        }  
    

    为什么会泄漏?
    Handler 的生命周期与Activity 不一致;
    引用 Activity 阻止了GC对Acivity的回收。

    如何防止?
    静态内部类,外部类
    WeakReference

    private static class MyHandler extends Handler{
    }
    

    然后

    mHandler.removeCallbacks(mRunnable);  
    //或者
    mHandler.removeCallbacksAndMessages(null);
    
    用法

    -post

    private Handler handler = new Handler();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            
                        }
                    }))
                }
            }).start();
    这里直接使用的post进行处理,run回调下可以进行ui更新,更加轻便,postDelay还可以实现延迟效果。
    

    -sendMessage

            Handler handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
    这里使用了handlerMessage的回调,通过msg来传递信息,更加实用。
    
    Message message = new Message();
    handler.sendMessage(message);
    
    Message msg = mHandler.obtainMessage();
    msg.sendToTarget();
    

    obtainmessage()是从消息池中拿来一个msg 不需要另开辟空间;
    new需要重新申请,效率低,obtianmessage可以循环利用;
    removeCallback 从队列中移除消息。

    子线程间的通讯

    在子线程中使用时需要手动添加代码loop,主线程其实也需要,只不过我们的android架构已经帮我们实现了。
    handler与子线程的关联:

    class Thread extends Thread{
          public Handler handler;
          @Override
          public void run(){
                Looper.prepare();
                handler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                          super.handleMessage(msg);
                      }
                 };
                Looper.loop();
          } 
    }
    
    private MyThread thread;
    thread = new MyTread();
    thread.start();
    thread.handler.sendEmptyMessage();
    

    handler在哪个线程定义,而looper特殊传入的话,则回调在哪个线程,所以在主线程的handlerMessage不要进行耗时处理。

    handlerThread

    在子线程定义thead

    class Thread extends Thread{
          public Handler handler;
          public Looper looper;
          @Override
          public void run(){
                Looper.prepare();
                looper = Looper.myLooper();
                handler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                          super.handleMessage(msg);
                      }
                 };
                Looper.loop();
          } 
    }
    

    将loop传递进来:

    Handler handler = new Handler(thread.looper){
           @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
           }
    };
    handler.sendEmptyMessage();
    

    这里涉及到了一个多线程并发的问题,我们主线程中new Handler()中使用子线程的looper而子线程中looper还没有创建成功,而出现没有looper的问题。这时候需要使用handlerThread。handlerThread使用了wait来等待looper的创建。

    thread = HandlerThread("handler thread");
    thread.start();
    
    handler = new Handler(thread.getlooper()){
          public void handlerMessage(android.os.Message msg){
          }
    }
    handler.sendEmptyMessage();
    

    用handlerthread线程的话,可以等待looper实例创建好再发送。

    子线程主线程通讯
    Handler threadHandler;
    Handler handler = new Handler(){
         @Override
          public void handleMessage(Message msg) {
                 super.handleMessage(msg);
                  threadHandler.sendMessageDelayed(msg1,1000);
          }
    };
    
    HandlerThread thread = new HandlerThread("handler thread");
    thread.start();
    threadHandler = new Handler(thread.getlooper()){
          @Override
          public void handleMessage(android.os.Message msg){
               handler.sendMessageDelayed(msg1,1000);
          }
    }
    
    更新ui的方式
    • handler.post
    • sendMessage
    • runOnUiThread
    • view.post
      这个很实用比如某些动画需要实现,而view没有初始化成功导致动画或者其他效果失效,这里可以使用这个方法。
    WeakHandler

    weakHandler是一个避免内存泄漏的handler库,使用方法和handler基本一致,但是我们要理解一下weakReference,就是弱引用,我们知道java中我们开发人员虽然不需要在意内存回收,可是在使用方面可以做到尽可量的优化,在我们使用弱引用的时候,我们的这个对象,如果他没有没其他对象引用,就会被gc,反着如果强引用的话就不会gc了。
    这里我们拓展一下:

    • 强引用(StrongReference)
      只要引用存在,垃圾回收器永远不会回收,也是我们默认的引用。
    • 软引用(SoftReference)
      非必须引用,内存溢出之前进行回收。
    • 弱引用(WeakReference)
      谷歌更推荐这个使用,这个在垃圾搜索器搜索到这个对象时就会进行回收。
      软引用和弱引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。
    • 虚引用(PhantomReference)
      虚引用用处不详..我理解为gc时候就会把虚引用一起回收了。
      总结为,强引用只要有引用就绝不回收,弱引用是oom前,软引用是垃圾回收的时候回收。

    这里再介绍一个消息通知的框架 可以用于换肤等:

    public class UIMessageCenter {
    
        private final static int UI_MSG_CHANFE_SKIN = 1;
        private static UIMessageCenter Intance = null;
        private List<WeakReference<IonMessage>> mOnMsgList = null;
        private WeakHandler mHanderUI = null;
    
        private UIMessageCenter() {
            mOnMsgList = new ArrayList<WeakReference<IonMessage>>();
            createHandler();
        }
    
        public static UIMessageCenter getIntance() {
            if (Intance == null) {
                Intance = new UIMessageCenter();
            }
            return Intance;
        }
    
        private void createHandler() {
            mHanderUI = new WeakHandler() {
                @Override
                public void handleMessage(Message msg) {
    
                    if (mOnMsgList != null && mOnMsgList.size() > 0) {
                        for (int i = 0; i < mOnMsgList.size(); i++) {
                            WeakReference<IonMessage> fun = mOnMsgList.get(i);
                            if (fun != null) {
                                IonMessage ionMsg = fun.get();
                                if (ionMsg != null) {
                                    HttpLog.d("handleMessage", "createHandler");
                                    ionMsg.onMessage(msg);
                                }
                            }
                        }
                    }
                }
            };
        }
    
        public void registerFun(IonMessage fun) {
            if (mOnMsgList != null && fun != null) {
                mOnMsgList.add(new WeakReference<IonMessage>(fun));
            }
        }
    
        public void removeFun(IonMessage fun) {
            if (mOnMsgList != null && fun != null) {
                mOnMsgList.remove(new WeakReference<IonMessage>(fun));
            }
        }
    
        /**
         * 通知换肤
         *
         * @param arg1
         * @param arg2
         * @param obj
         */
        public void notifyChangeSkin(int arg1, int arg2, Object obj) {
            if (mHanderUI != null) {
                mHanderUI.sendMessage(mHanderUI.obtainMessage(UI_MSG_CHANFE_SKIN, arg1, arg2, obj));
            }
        }
        
    }
    
    public interface IonMessage {
        void onMessage(Message msg);
    }
    

    使用方法:

    class MyActivity extend Activity implements IonMessage{
          @Override
           public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              UIMessageCenter.getIntance().registerFun(this);
           }
          @Override
           public void onDestroy() {
              super.onDestroy();
              UIMessageCenter.getIntance().removeFun(this);
           }
          @Override
          public void onMessage(Message msg) {
            switch (msg.what) {
                case UIMessageCenter.UI_MSG_CHANFE_SKIN:
                    changeSkin();
                    break;
                default:
                    break;
            }
        }
    }
    

    转载请注明出处:http://www.jianshu.com/p/e3248d737ced

    相关文章

      网友评论

        本文标题:Handler使用的整理

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