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。
- 1)、View已经attach到window
当View已经attach到window时。会直接调用Handler中的post()方法:
//主要作用是线程的切换
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队列中
- 2)、View还未attach到window
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)加入到队列中。
- 3)、View 和 window绑定
在 ViewRootImpl 的 performTraversals() 方法中调用。
(ViewGroup的 addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout);也会调用)
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 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是以数组和链表的方式存储的。
这样的好处是:
- 每个Map存储的Entry数量减少;
- 当Thread销毁时,ThreadLocalMap也会随之销毁,减少内存的使用。
3)、ThreadLocal出现内存泄漏的原因
- 内存泄漏有两个原因
1)、没有手动删除这个Entry
只有当使用完ThreadLocal时,调用器remove方法删除这个Entry,就能避免内存泄漏。
2)、CurrentThread依然运行。
由于ThreadLocalMap是Thread的一个属性,被当前线程所引用,他的生命周期跟Thread一样长。那么使用完ThreadLocal的使用,如果当前Thread也随之执行结束,那么ThreadLocalMap自然也会被GC回收,从根源上避免内存泄漏。
综上,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 value = new ThreadLocal() {
};
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 t = Thread.currentThread();
//获取当前Thread持有的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
//ThreadLocal作为key
map.set(this, value);
else
createMap(t, value);
return value;
}
set()方法
设置当前线程的线程局部变量的值。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//ThreadLocalMap 不存在重新创建
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
remove() 方法
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}