Android之为什么只能在UI线程操作View
为什么说android UI操作不是线程安全的
以上都是搜索得来的结论,这里的关键部分还是在ViewRoot,接下来看源码,确认下其内部的UI操作与线程有怎样的联系?
从源码中可以看出:
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()之后,还做了什么工作?
在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的作用:
再来看下WindowManagerGlobal的源码中,ViewRootImpl的构造函数是在哪里调用的?
PhoneWindowManager WindowManagerGlobal WindowManagerImpl的作用和关系
从源码中可以看出:
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中的实现:
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()有三处调用,以下节点依次向上层查询:
其最终实现都是在TransactionExecutor类中,找到TransactionExecutor类的实例化所在的线程,也就找到了Activity中的mDecorView的UI操作所在的线程:
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");
}
......
}
为啥会有主线程与子线程之分?Looper.prepareMainLooper()与Looper.prepare()有什么区别?
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的类注释中发现:
/**
*
* When a Java Virtual Machine starts up, there is usually a single
* non-daemon thread (which typically calls the method named
* main
of some designated class). The Java Virtual
* Machine continues to execute threads until either of the following
* occurs:
*
* - The
exit
method of class Runtime
has been
* called and the security manager has permitted the exit operation
* to take place.
* - All threads that are not daemon threads have died, either by
* returning from the call to the
run
method or by
* throwing an exception that propagates beyond the run
* method.
*
*/
public
class Thread implements Runnable {
......
}