Handler(二)

2021-03-18  本文已影响0人  涛涛123759

Android知识总结

一、Handler的postDelayed() 方法,View的post方法分析

看源码:

    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            // 如果当前View加入到了window中,直接调用UI线程的Handler发送消息
            return attachInfo.mHandler.post(action);
        }
        // View未加入到window,放入ViewRootImpl的RunQueue中
        getRunQueue().post(action);
        return true;
    }

当前View尚未attach到Window时,整个View体系还没有加载完,mAttachInfo就会为null,表现在Activity中,就是onResume()方法还没有执行完。反之,mAttachInfo就不会为null。

    //主要作用是线程的切换
    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    } 

然后将指定Runnable(包装成PostMessage)加入到MessageQueue中,然后Looper不断从MessageQueue中读取Message进行处理。

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

然后会把会把Runnable 放入HandlerActionQueue队列中

    private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }

getRunQueue()是一个单例模式,返回HandlerActionQueue实例mRunQueue。mRunQueue,顾名思义,表示该view的HandlerAction队列,HandlerAction就是对Runnable的封装,所以实际就是一个Runnable的队列。它用于推迟post的调用,直到该view被附着到Window并且拥有了一个handler。

HandlerActionQueue的关键代码如下:

  public class HandlerActionQueue {
      private HandlerAction[] mActions;
      private int mCount;
  
     public void post(Runnable action) {
         postDelayed(action, 0);
     }
     public void postDelayed(Runnable action, long delayMillis) {
          //将Runnable封装成了HandlerAction
         final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
 
         synchronized (this) {
             if (mActions == null) {
                 mActions = new HandlerAction[4];
             }
              //将封装后的Runnable加入到了数组中
             mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
             mCount++;
         }
     }
    ......
     public void executeActions(Handler handler) {
         synchronized (this) {
             final HandlerAction[] actions = mActions;
             for (int i = 0, count = mCount; i < count; i++) {
                 final HandlerAction handlerAction = actions[i];
                 handler.postDelayed(handlerAction.action, handlerAction.delay);
             }
 
             mActions = null;
             mCount = 0;
         }
     }
    ......
     private static class HandlerAction {
         final Runnable action;
         final long delay;
 
         public HandlerAction(Runnable action, long delay) {
             this.action = action;
             this.delay = delay;
         }
        ......
     }
}

该类用于在当前view没有handler附属时,将来自View的挂起的作业(就是Runnable)加入到队列中。

private void performTraversals() {
      ...
      host.dispatchAttachedToWindow(mAttachInfo, 0);
      ...
}

在View的dispatchAttachedToWindow()方法中绑定

   void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
        if (mOverlay != null) {
            mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
        }
        mWindowAttachCount++;
        mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
        if (mFloatingTreeObserver != null) {
            info.mTreeObserver.merge(mFloatingTreeObserver);
            mFloatingTreeObserver = null;
        }

        registerPendingFrameMetricsObservers();

        if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {
            mAttachInfo.mScrollContainers.add(this);
            mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;
        }
        // 通过传进来的Handler,来post掉mRunQueue中存储的所有Runnable
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
        performCollectViewAttributes(mAttachInfo, visibility);
        onAttachedToWindow();

        ListenerInfo li = mListenerInfo;
        final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
                li != null ? li.mOnAttachStateChangeListeners : null;
        if (listeners != null && listeners.size() > 0) {
            for (OnAttachStateChangeListener listener : listeners) {
                listener.onViewAttachedToWindow(this);
            }
        }
        int vis = info.mWindowVisibility;
        if (vis != GONE) {
            onWindowVisibilityChanged(vis);
            if (isShown()) {
                onVisibilityAggregated(vis == VISIBLE);
            }
        }
        onVisibilityChanged(this, visibility);
        if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0) {
            refreshDrawableState();
        }
        needGlobalAttributesUpdate(false);
        notifyEnterOrExitForAutoFillIfNeeded(true);
        notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
    }

    protected void onAttachedToWindow() {
        if ((mPrivateFlags & PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
            mParent.requestTransparentRegion(this);
        }
        mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
        jumpDrawablesToCurrentState();
        AccessibilityNodeIdManager.getInstance().registerViewWithId(this, getAccessibilityViewId());
        resetSubtreeAccessibilityStateChanged();
        // rebuild, since Outline not maintained while View is detached
        rebuildOutline();
        if (isFocused()) {
            notifyFocusChangeToImeFocusController(true /* hasFocus */);
        }
    }

二、ThreadLocal讲解

1)、早期的设计原理

让ThreadLocal维护一个ThreadLocalMap的队列,存储是以Thread作为Key。

2)、JDK8 的设计原理

让Thread维护一个ThreadLocalMap的队列,存储是以ThreadLocal作为Key。ThreadLocalMap是以数组和链表的方式存储的。

这样的好处是:

3)、ThreadLocal出现内存泄漏的原因

综上,ThreadLocal的内存泄漏根源是:因为ThreadLocal被WeakReference引用,ThreadLocal遇到GC会自动释放时。Thread中持有的ThreadLocalMap的生命周期和Thread一样长,ThreadLocalMap中Entry中的value没有及时的释放,所以需要手动remove。如果没有手动删除Entry可能会导致内存泄漏。

Handler的内存泄漏,就是由于当前消息没有发生完成,ThreadLocalMap中的Entry依然存在导致引用依然持有,造成Handler的内存泄漏。

4)、ThreadLocal的线程安全问题
public class ThreadLocalUnsafe implements Runnable {

    public static Number number = new Number(0);//不安全,静态变量在内存中只能有一份。
//   public  Number number = new Number(0); //安全

    public void run() {
        //每个线程计数加一
        number.setNum(number.getNum()+1);
      //将其存储到ThreadLocal中
        value.set(number);
        SleepTools.ms(2);
        //输出num值
        System.out.println(Thread.currentThread().getName()+"="+value.get().getNum());
    }

    public static ThreadLocal<Number> value = new ThreadLocal<Number>() {
    };

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new ThreadLocalUnsafe()).start();
        }
    }

    private static class Number {
        public Number(int num) {
            this.num = num;
        }

        private int num;

        public int getNum() {
            return num;
        }

        public void setNum(int num) {
            this.num = num;
        }

        @Override
        public String toString() {
            return "Number [num=" + num + "]";
        }
    }
}

5 个线程中保存的是同一 Number 对象的引用,在线程睡眠的时候, 其他线程将 num 变量进行了修改,而修改的对象 Number 的实例是同一份,因 此它们最终输出的结果是相同的。
而上面的程序要正常的工作,应该的用法是让每个线程中的 ThreadLocal 都 应该持有一个新的 Number 对象。

5)、ThreadLocal中的方法

get()方法
该方法返回当前线程所对应的线程局部变量。

    public T get() {
        Thread var1 = Thread.currentThread();
        //获取当前Thread持有的ThreadLocalMap 
        ThreadLocal.ThreadLocalMap var2 = this.getMap(var1);
        if (var2 != null) {
            ThreadLocal.ThreadLocalMap.Entry var3 = var2.getEntry(this);
            if (var3 != null) {
                Object var4 = var3.value;
                return var4;
            }
        }

        return this.setInitialValue();
    }

    private T setInitialValue() {
        Object var1 = this.initialValue();
        Thread var2 = Thread.currentThread();
        ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
        if (var3 != null) {
            //ThreadLocal作为key
            var3.set(this, var1);
        } else {
            this.createMap(var2, var1);
        }

        return var1;
    }

set()方法
设置当前线程的线程局部变量的值。

    public void set(T var1) {
        Thread var2 = Thread.currentThread();
        ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
        if (var3 != null) {
            var3.set(this, var1);
        } else {
            //ThreadLocalMap 不存在重新创建
            this.createMap(var2, var1);
        }

    }

    void createMap(Thread var1, T var2) {
        var1.threadLocals = new ThreadLocal.ThreadLocalMap(this, var2);
    }

remove() 方法
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

    public void remove() {
        ThreadLocal.ThreadLocalMap var1 = this.getMap(Thread.currentThread());
        if (var1 != null) {
            var1.remove(this);
        }

    }
上一篇 下一篇

猜你喜欢

热点阅读