欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【Android】为什么必须在主线程中执行UI操作

程序员文章站 2022-04-18 10:00:00
https://blog.csdn.net/w690333243/article/details/77803812?locationNum=7&fps=1https://blog.csdn.net/LVXIANGAN/article/details/39504145?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_sourc...
  • View的绘制是单线程模型,view的创建与更新需要在同一个线程(绘制前会checkThread())
    • 每向系统中添加一个view,就会构造一个ViewRoot对象
    • ViewRoot的构造会记录当前线程,之后更新view的时候会检查线程。
    • View的绘制是非线程安全的,多线程下绘制view,容易出错。
  • 在子线程中创建view,并在子线程中更新,是可行的。

Android之为什么只能在UI线程操作View

为什么说android UI操作不是线程安全的

以上都是搜索得来的结论,这里的关键部分还是在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的实例化只有两处:

【Android】为什么必须在主线程中执行UI操作

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();
  • ActivityThread.updateVisibility();
    • ActivityThread.handleStopActivity();
      • StopActivityItem.execute();
        • TransactionExecutor.executeCallbacks();
        • TransactionExecutor.executeLifecycleState();
      • TransactionExecutor.performLifecycleSequence();
    • ActivityThread.handleWindowVisibility();
      • WindowVisibilityItem.execute();
        • TransactionExecutor.executeCallbacks();T
        • ransactionExecutor.executeLifecycleState();
    • ActivityThread.handleSendResult();
      • ActivityResultItem.execute();
        • TransactionExecutor.executeCallbacks();
        • TransactionExecutor.executeLifecycleState();

其最终实现都是在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

为啥会有主线程与子线程之分?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

相关标签: Android开发笔记

上一篇: 徐闻县

下一篇: 北京九龙游乐园