之前有分析過子線程中直接更新ui
眾所周知CalledFromWrongThreadException是檢查original thread
,也就是創(chuàng)建ui的線程亥贸。那么在子線程中創(chuàng)建ui厌殉,自然也可以在此線程中更新ui。
要維護(hù)一個(gè)子線程,首先想到的就是HandlerThread
下面寫個(gè)demo
class MainActivity : AppCompatActivity() {
private val handlerThread = HandlerThread("AsyncHandlerThread")
class H(looper: Looper, private val weak: WeakReference<MainActivity>) : Handler(looper) {
var tvMain: TextView? = null
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
10086 -> {
val root = LayoutInflater.from(weak.get())
.inflate(R.layout.activity_main, null)
val wm: WindowManager =
weak.get()?.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val param = WindowManager.LayoutParams()
param.width = WindowManager.LayoutParams.MATCH_PARENT
param.height = 300
wm.addView(root, param)
tvMain = root.findViewById(R.id.tvMain)
tvMain?.setOnClickListener {
tvMain?.text = "${Thread.currentThread()}"
}
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handlerThread.start()
val handler = H(handlerThread.looper, WeakReference(this@MainActivity))
handler.sendEmptyMessage(10086)
}
override fun onDestroy() {
handlerThread.quitSafely()
super.onDestroy()
}
}
Run,點(diǎn)擊TextView驗(yàn)證一下
onClick回調(diào)在HandlerThread所在線程。通過這個(gè)思路逗堵,可以將部分ui挪到子線程中,減少主線程耗時(shí)眷昆。
追本溯源蜒秤,WindowManager的實(shí)現(xiàn)類是WindowManagerImpl汁咏,
WindowManagerImpl.addView
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
WindowManagerGlobal.addView
...
ViewRootImpl root;
root = new ViewRootImpl(view.getContext(), display);
root.setView(view, wparams, panelParentView, userId);
...
ViewRootImpl構(gòu)造方法中為mThread賦值、初始化Choreographer
mThread = Thread.currentThread();
mChoreographer = useSfChoreographer
? Choreographer.getSfInstance() : Choreographer.getInstance();
Choreographer.getSfInstance()
作媚,從ThreadLocal中取對應(yīng)線程的Choreographer
public static Choreographer getSfInstance() {
return sSfThreadInstance.get();
}
private static final ThreadLocal<Choreographer> sSfThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
return new Choreographer(looper, VSYNC_SOURCE_SURFACE_FLINGER);
}
};
回看ViewRootImpl.setView()
...
requestLayout();
...
ViewRootImpl.requestLayout()
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
ViewRootImpl.scheduleTraversals()
final ViewRootHandler mHandler = new ViewRootHandler();
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
mHandler調(diào)用Handler無參構(gòu)造方法初始化取的是當(dāng)前線程looper梆暖,如此一來mHandler、mChoreographer都在demo中HandlerThread線程所在的事件循環(huán)掂骏。
mTraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
doTraversal大家都懂,后面就是繪制流程了厚掷。
繼續(xù)跟一下Choreographer相關(guān)邏輯弟灼,回看Choreographer.postCallback()
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
...
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
Choreographer.scheduleFrameLocked()
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
// If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
到這里就差不太多。上述注釋說明冒黑,在有Looper的線程立即發(fā)出vsync田绑;否則post 一個(gè)帶vsync的message到ui線程。我們的HandlerThread中當(dāng)然是有Looper事件循環(huán)的啦抡爹。
Choreographer.scheduleVsyncLocked()
private final FrameDisplayEventReceiver mDisplayEventReceiver;
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable{}
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
調(diào)用父類DisplayEventReceiver.scheduleVsync()
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
nativeScheduleVsync()掩驱,VSync信號(hào)最終調(diào)到C層。筆者比較懶冬竟,就不跟C層代碼了欧穴,搜索一番得到結(jié)論,VSync信號(hào)接受回調(diào)的方法是onVsync()
FrameDisplayEventReceiver.onVsync()
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
...
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
Message.obtain(mHandler, this)泵殴,this也就是FrameDisplayEventReceiver這個(gè)Runnable涮帘,那么就調(diào)到了run方法。
FrameDisplayEventReceiver.run()
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
doFrame()最終就是執(zhí)行前面ViewRootImpl.mTraversalRunnable
驗(yàn)證一下笑诅,profile運(yùn)行record一次看調(diào)用棧调缨。
繪制流程確實(shí)都在AsyncHandlerThread這個(gè)子線程中了。