【Android】为什么必须在主线程中执行UI操作
- View的绘制是单线程模型,view的创建与更新需要在同一个线程(绘制前会checkThread())
- 每向系统中添加一个view,就会构造一个ViewRoot对象
- ViewRoot的构造会记录当前线程,之后更新view的时候会检查线程。
- View的绘制是非线程安全的,多线程下绘制view,容易出错。
- 在子线程中创建view,并在子线程中更新,是可行的。
以上都是搜索得来的结论,这里的关键部分还是在ViewRoot,接下来看源码,确认下其内部的UI操作与线程有怎样的联系?
从源码中可以看出:
- mThread的赋值是在ViewRootImpl的构造函数中确定的。
- 在ViewRootImpl在调用requestLayout,invalideteChildInParent等似乎与UI相关的函数时,都执行了checkThread();
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
public ViewRootImpl(Context context, Display display) {
......
mThread = Thread.currentThread();
......
}
......
@Override
public void requestFitSystemWindows() {
checkThread();
mApplyInsetsRequested = true;
scheduleTraversals();
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
......
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
......
}
......
@Override
public void requestTransparentRegion(View child) {
// the test below should not fail unless someone is messing with us
checkThread();
if (mView == child) {
......
requestLayout();
}
}
......
@Override
public void requestChildFocus(View child, View focused) {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "Request child focus: focus now " + focused);
}
checkThread();
scheduleTraversals();
}
@Override
public void clearChildFocus(View child) {
if (DEBUG_INPUT_RESIZE) {
Log.v(mTag, "Clearing child focus");
}
checkThread();
scheduleTraversals();
}
@Override
public void focusableViewAvailable(View v) {
checkThread();
if (mView != null) {
......
}
}
@Override
public void recomputeViewAttributes(View child) {
checkThread();
if (mView == child) {
......
}
}
......
/**
* {@inheritDoc}
*/
@Override
public void playSoundEffect(int effectId) {
checkThread();
......
}
......
/**
* {@inheritDoc}
*/
@Override
public View focusSearch(View focused, int direction) {
checkThread();
if (!(mView instanceof ViewGroup)) {
return null;
}
return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
}
/**
* {@inheritDoc}
*/
@Override
public View keyboardNavigationClusterSearch(View currentCluster,
@FocusDirection int direction) {
checkThread();
return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
mView, currentCluster, direction);
}
......
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
......
}
......
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
......
}
以requestLayout()为例,确认下checkThread()之后,是否执行了UI操作?
在checkThread()之后,仅执行了scheduleTraversals(),深入查看后发现TraversalRunnable.run() > doTraversal(); > performTraversals();中有大量的UI相关的操作。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//测量?
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();//计划消耗批量输入?
}
notifyRendererOfFramePending();//通知帧渲染器挂起?
pokeDrawLockIfNeeded();//如有需要,插入绘制锁?
}
}
确认了ViewRoot中的UI操作与线程的相关性,也就是说ViewRoot在哪个线程实例化,那么与之相关的View的UI操作就必须在这个线程中处理。
接下来我们继续看,ViewRoot是在哪里实例化的?
经过搜索,发现ViewRoot的实例化只有两处:
AttachInfo_Accessor不知道是干嘛的,先去看下WindowManagerGlobal都做了什么?
据搜索了解WindowManagerGlobal的作用:
- 主要用于管理ViewRoot、创建并提供IWindowManager的proxy实例。
- 管理所有的View
- 实现跟View有关的接口,比如addView、removeView、updateViewLayout等
再来看下WindowManagerGlobal的源码中,ViewRootImpl的构造函数是在哪里调用的?
PhoneWindowManager WindowManagerGlobal WindowManagerImpl的作用和关系
从源码中可以看出:
- 每调用一次WindowManagerGlobal.addView(),都会实例化一个ViewRoot
- 而通过ViewRoot.setView()可知ViewRoot与View是一对一的关系
public final class WindowManagerGlobal {
......
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
......
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
......
}
什么地方调用了WindowManagerGlobal.addView()?
只在WindowManager的唯一实现类WindowManagerImpl中使用:
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
public WindowManagerImpl(Context context) {
this(context, null);
}
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
return new WindowManagerImpl(displayContext, mParentWindow);
}
......
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
WindowManagerImpl.addView()调用的地方很多,那么在Activty中WindowManagerImpl.addView()是在哪个线程?
看下Activity中的实现:
- 通过WindowManagerImpl.addView()新建一个ViewRoot与Activity中的mDecorView绑定
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient {
......
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
......
}
Activity.makeVisible()有三处调用,以下节点依次向上层查询:
- Activity.setVisible();//没有找到上层调用
- ActivityThread.handleResumeActivity();
- ResumeActivityItem.execute()
- TransactionExecutor.executeCallbacks();
- TransactionExecutor.executeLifecycleState();
- ResumeActivityItem.execute()
- ActivityThread.updateVisibility();
- ActivityThread.handleStopActivity();
- StopActivityItem.execute();
- TransactionExecutor.executeCallbacks();
- TransactionExecutor.executeLifecycleState();
- TransactionExecutor.performLifecycleSequence();
- StopActivityItem.execute();
- ActivityThread.handleWindowVisibility();
- WindowVisibilityItem.execute();
- TransactionExecutor.executeCallbacks();T
- ransactionExecutor.executeLifecycleState();
- WindowVisibilityItem.execute();
- ActivityThread.handleSendResult();
- ActivityResultItem.execute();
- TransactionExecutor.executeCallbacks();
- TransactionExecutor.executeLifecycleState();
- ActivityResultItem.execute();
- ActivityThread.handleStopActivity();
其最终实现都是在TransactionExecutor类中,找到TransactionExecutor类的实例化所在的线程,也就找到了Activity中的mDecorView的UI操作所在的线程:
- TransactionExecutor只有一个构造函数,且只在ActivityThread中有一次调用
public class TransactionExecutor {
......
private ClientTransactionHandler mTransactionHandler;
......
public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
mTransactionHandler = clientTransactionHandler;
}
}
public final class ActivityThread extends ClientTransactionHandler {
......
private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
......
}
找出ActivityThread的实例化在哪个线程?
public final class ActivityThread extends ClientTransactionHandler {
......
public static ActivityThread systemMain() {
......
ActivityThread thread = new ActivityThread();
thread.attach(true, 0);
return thread;
}
......
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
......
}
- static Activity.main();//没有找到上层调用 o.0
- static ActivityThread.systemMain();
- SystemServer.createSystemContext();
- SystemServer.run();
- static SystemServer.main();//未找到上层调用 o.0
- SystemServer.run();
- SystemServer.createSystemContext();
为啥会有主线程与子线程之分?Looper.prepareMainLooper()与Looper.prepare()有什么区别?
- 关键在于quitAllowed的值(ActivityThread所在的线程的Looper赋值为false),而quitAllowed最后传给了MessageQueen。
- MessageQueen.quit()调用的时候,如果quitAllowed为false,则抛出异常:IllegalStateException("Main thread not allowed to quit.")
public final class Looper {
......
public static void prepare() {
prepare(true);
}
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));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
......
}
由以上可知Activity.main()运行所在的线程就是主线程了,可是为什么没有看到创建Thread的过程啊?
仅在Thread的类注释中发现:
- 当Java虚拟机启动时,通常有一个非守护进程线程(通常调用名为“main”)
- Java虚拟机持续执行线程,直到出现以下任一情况
- 调用exit();
- 所有非守护线程均已终止;或者在run()中返回;或者抛出异常;
/**
* <p>
* When a Java Virtual Machine starts up, there is usually a single
* non-daemon thread (which typically calls the method named
* <code>main</code> of some designated class). The Java Virtual
* Machine continues to execute threads until either of the following
* occurs:
* <ul>
* <li>The <code>exit</code> method of class <code>Runtime</code> has been
* called and the security manager has permitted the exit operation
* to take place.
* <li>All threads that are not daemon threads have died, either by
* returning from the call to the <code>run</code> method or by
* throwing an exception that propagates beyond the <code>run</code>
* method.
* </ul>
*/
public
class Thread implements Runnable {
......
}
本文地址:https://blog.csdn.net/u014040795/article/details/107369097