1、ThreadLocal为什么不直接使用WeakHashMap
Entry
是一个以弱引用为key
的K-V结构
;ThreadLocal内部持有的数据结构是一个数组;getEntry
获取index
的方式也是通过位运算代替取余
//1
private Entry[] table;
//2
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//3
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
StackOverflow
有回答说是单纯因为ThreadLocal出来得早,Doug Lee后来发现这套结构通用性强,才写了HashMap
(存疑)
2、HashMap构造函数为什么将长度赋给阈值
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: "
+ initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
//这里
this.threshold = tableSizeFor(initialCapacity);
}
猜测
:
其实应该是this.threshold = tableSizeFor(initialCapacity) * this.loadFactor;
才对,但是table
的初始化在第一次put的时候,threshold
又会被重新赋值。猜想是与resize
里面的第二个判断if (oldThr > 0) { newCap = oldThr; }
有关。
3、ViewRootImpl # setView为什么先调用requestLayout后调用addToDisplay
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
try {
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
setFrame(mTmpFrame);
待验证:
猜测可能像view.post
一样,当前处于一个Runnable的执行过程中,在request
通过Choreographer
去post一个Runnable
后,最快也要这个Runnable
执行完,再加上这次IPC
不是oneway
的?所以会阻塞等待服务端返回结果才继续执行。(但又解释不通它的注释)
4、BroadcastReceiver发送流程(已解决
)
performReceiveLocked
为什么要分两种情况
if (app != null) {
if (app.thread != null) {
try {
//第一种?
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
} catch (RemoteException ex) {
}
}
} else {
//第二种?
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
因为
scheduleRegisteredReceiver
是动态广播的发送,它最终调用到ActivityThread
的scheduleRegisteredReceiver
;而performReceiver
是静态广播的发送流程,它最终调用到ActivityThread
的handleReceiver
。它们之间有一个最大的区别是Context
的创建,动态是从ReceiverDIspatcher
里面获取的,静态是通过context.getReceiverRestrictedContext()
创建一个以Application
为mBase
的Context
5、bindService
流程
在最后阶段,
标记2
里面为什么要通过Handler
而不是直接调用罗升阳老师的《Android系统源代码情景分析》里面提到有两个原因:
- 1、当前线程需要尽快返回
Binder
线程池- 2、
onServiceConnected
可能有ui
操作,需要回到主线程
但我去看了一下
IServiceConnection.aidl
,它是被标注成oneway
的,这样不是就不会阻塞到系统服务了吗?
private static class InnerConnection extends IServiceConnection.Stub {
@UnsupportedAppUsage
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
}
public void connected(ComponentName name, IBinder service, boolean dead)
throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
//标记1
sd.connected(name, service, dead);
}
}
}
//---------------------------------------------------------------//
static final class ServiceDispatcher {
...
public void connected(ComponentName name, IBinder service, boolean dead) {
if (mActivityExecutor != null) {
mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
} else if (mActivityThread != null) {
//标记2
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}
}