美文网首页
Android Handler笔记

Android Handler笔记

作者: AArman | 来源:发表于2021-05-04 16:15 被阅读0次

    消息机制模型

    1. Message:需要传递的消息,可以传递数据;
    2. MessageQueue:消息队列,并不是队列,实际上是一个单链表的数据结构来 管理和维护Message,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);因为有延时消息的存在。所以没设计成队列,因为并不能完全保证先进先出,而是需要根据时间来判断,所以Android中采用了链表的形式来实现这个队列,也方便了数据的插入
    3. Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
    4. Looper:不断循环执行(Looper.loop),负责关联线程以及消息的分发,从 MessageQueue 中获取 Message 分发给 Handler。

    Handler 的面目

    Handler handler = new Handler() {
        @Overridse
        public void handleMessage(Message msg) {
        }
    };
    Message msg = new Message();
    handler.sendMessage(msg);
    
    1. 创建Handler时会 获取当前线程的 Looper(Looper.myLooper();)(一个Handler仅一个),Looper 内部创建 MessageQueue;
    2. sendMessage(msg)或post(Runnable) 会将本Handler的引用打包到Message.target并发送给 Looper 的消息队列 MessageQueue 中(通过单向链表的数据结构来存储消息);
    3. Looper 的loop方法中拿到当前的消息队列,创建一个死循环不断取消息queue.next();,拿到就分发给 Handler 执行 msg.target.dispatchMessage(msg);,执行完之后会将用完的消息回收掉msg.recycleUnchecked();;
    4. MessageQueuenext()有for(;;)取消息,当前时间没有消息就处于阻塞状态nativePollOnce(ptr, nextPollTimeoutMillis);,取到了消息即退出循环。
    //之前必须调用 prepare ; 
    public static @Nullable Looper myLooper() {
       return sThreadLocal.get(); 
    }
    
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    private static void prepare(boolean quitAllowed) {
      if (sThreadLocal.get() != null) {
          throw new RuntimeException("Only one Looper may be created per thread");
      }
      sThreadLocal.set(new Looper(quitAllowed));
    }
    

    ActivityThread 和Handler的关系

    1. 通过在main方法中运行一个死循环(Looper.loop())让 main 一直执行。
    2. 本质上Android就是事件驱动的程序,界面刷新也好,交互也好,本质上都是事件。这些事件最后通通被作为了Message发送到了MessageQueue中。
    3. 主线程做耗时操作本质上不是阻塞了主线程,而是阻塞了Looper的loop方法。导致loop方法无法处理其他事件,导致出现了ANR
    public static void main(String[] args) {
      AndroidOs.install();
    
      Looper.prepareMainLooper();
    
      ActivityThread thread = new ActivityThread();
      thread.attach(false, startSeq);//建立Binder通道 (创建新线程)
    
      if (sMainThreadHandler == null) {
          sMainThreadHandler = thread.getHandler();
      }
    
      // End of event ActivityThreadMain.
      Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
      Looper.loop();
    
      throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    
    主线程Looer会不会消耗cpu资源
    • 这就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。
    • 这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
    MessageQueue没有消息时候会怎样?阻塞之后怎么唤醒呢?说说pipe/epoll机制?
    1. 当消息不可用或者没有消息的时候就会阻塞在next方法,而阻塞的办法是通过pipe/epoll机制。
    2. epoll机制是一种IO多路复用的机制,具体逻辑就是一个进程可以监视多个描述符,当某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作,这个读写操作是阻塞的。在Android中,会创建一个Linux管道(Pipe)来处理阻塞和唤醒。
    • 当消息队列为空,管道的读端等待管道中有新内容可读,就会通过epoll机制进入阻塞状态。
    • 当有消息要处理,就会通过管道的写端写入内容,唤醒主线程。
    同步屏障

    2.自定义Handler时如何避免内存泄漏

    一般非静态内部类持有外部类的引用的情况下,造成外部类在使用完成后不能被系统回收内存,从而造成内存泄漏。为了避免这个问题,我们可以自定义的Handler声明为静态内部类形式,然后通过弱引用的方式,让Handler持有外部类的引用,从而可避免内存泄漏问题。

    以下是代码实现

    private WeakReference<MainActivity> activityWeakReference;
    private MyHandler myHandler;
    
    static class MyHandler extends Handler {
    private MainActivity activity;
    
    MyHandler(WeakReference<MainActivity> ref) {
    this.activity = ref.get();
    }
    
    @Override
    public void handleMessage(Message msg) {
    super.handleMessage(msg);
    switch (msg.what) {
    case 1:
    //需要做判空操作
    if (activity != null) {
    activity.mTextView.setText("new Value");
    }
    break;
    default:
    Log.i(TAG, "handleMessage: default ");
    break;
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //在onCreate中初始化
    activityWeakReference = new WeakReference<MainActivity>(this);
    myHandler = new MyHandler(activityWeakReference);
    
    myHandler.sendEmptyMessage(1);
    mTextView = (TextView) findViewById(R.id.tv_test);
    }
    

    相关文章

      网友评论

          本文标题:Android Handler笔记

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