• View绘制流程


    一、View什么时候绘制到屏幕上的

    是在onResume之后

    1.onResume是通过在ActivityThread的handleResumeActivity方法,我们从handleResumeActivity方法开始分析,View是如何绘制到屏幕上的

    1. public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
    2. String reason) {
    3. ......
    4. // TODO Push resumeArgs into the activity for consideration
    5. final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); // ⭐
    6. ......
    7. final Activity a = r.activity;
    8. ......
    9. if (r.window == null && !a.mFinished && willBeVisible) { //⭐
    10. r.window = r.activity.getWindow();
    11. View decor = r.window.getDecorView();
    12. decor.setVisibility(View.INVISIBLE);
    13. ViewManager wm = a.getWindowManager();
    14. WindowManager.LayoutParams l = r.window.getAttributes();
    15. a.mDecor = decor;
    16. l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
    17. l.softInputMode |= forwardBit;
    18. if (r.mPreserveWindow) {
    19. a.mWindowAdded = true;
    20. r.mPreserveWindow = false;
    21. // Normally the ViewRoot sets up callbacks with the Activity
    22. // in addView->ViewRootImpl#setView. If we are instead reusing
    23. // the decor view we have to notify the view root that the
    24. // callbacks may have changed.
    25. ViewRootImpl impl = decor.getViewRootImpl();
    26. if (impl != null) {
    27. impl.notifyChildRebuilt();
    28. }
    29. }
    30. if (a.mVisibleFromClient) {
    31. if (!a.mWindowAdded) {
    32. a.mWindowAdded = true;
    33. wm.addView(decor, l);
    34. } else {
    35. // The activity will get a callback for this {@link LayoutParams} change
    36. // earlier. However, at that time the decor will not be set (this is set
    37. // in this method), so no action will be taken. This call ensures the
    38. // callback occurs with the decor set.
    39. a.onWindowAttributesChanged(l);
    40. }
    41. }
    42. // If the window has already been added, but during resume
    43. // we started another activity, then don't yet make the
    44. // window visible.
    45. } else if (!willBeVisible) {
    46. if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
    47. r.hideForNow = true;
    48. }
    49. }

    2.先分析handleResumeActivity方法是如何调用到Activity的omResume方法的

    从上面贴出的代码,我们看到handleResumeActivity会调用performResumeActivity这个方法:

    1. public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
    2. String reason) {
    3. ......
    4. r.activity.performResume(r.startsNotResumed, reason);
    5. ......
    6. return r;
    7. }

    然后,调用Activity的PerformResume方法:

    1. final void performResume(boolean followedByPause, String reason) {
    2. ......
    3. // mResumed is set by the instrumentation
    4. mInstrumentation.callActivityOnResume(this);
    5. .....
    6. }

    PerformResume方法调用mInstrumentation.callActivityOnResume(this)方法

    1. public void callActivityOnResume(Activity activity) {
    2. activity.mResumed = true;
    3. activity.onResume();
    4. if (mActivityMonitors != null) {
    5. synchronized (mSync) {
    6. final int N = mActivityMonitors.size();
    7. for (int i=0; i
    8. final ActivityMonitor am = mActivityMonitors.get(i);
    9. am.match(activity, activity, activity.getIntent());
    10. }
    11. }
    12. }
    13. }

    callActivityOnResume方法中调用了activity的onResume方法,到此handleResumeActivity方法是如何调用到Activity的omResume方法说清楚了,上面提到了View是在onResume方法后绘制在手机上的,那具体是在哪里,我们继续分析handleResumeActivity的第二个重点代码

    4.handleResumeActivity的第二个重点代码

    1. if (r.window == null && !a.mFinished && willBeVisible) {
    2. r.window = r.activity.getWindow();
    3. View decor = r.window.getDecorView();//获取setContentView方法创建的DecorVieww
    4. decor.setVisibility(View.INVISIBLE);
    5. ViewManager wm = a.getWindowManager(); //a是Activity,通过调用getWindoManager方
    6. //法获取WindowManager,ViewManager是一个接口,WindowManager实现了ViewManager这个接口
    7. WindowManager.LayoutParams l = r.window.getAttributes(); // 获取window的LayoutParams
    8. a.mDecor = decor;
    9. ......
    10. if (a.mVisibleFromClient) {
    11. if (!a.mWindowAdded) { // 一个Activity中只会添加一次⭐
    12. a.mWindowAdded = true;
    13. wm.addView(decor, l); //⭐
    14. } else {
    15. // The activity will get a callback for this {@link LayoutParams} change
    16. // earlier. However, at that time the decor will not be set (this is set
    17. // in this method), so no action will be taken. This call ensures the
    18. // callback occurs with the decor set.
    19. a.onWindowAttributesChanged(l);
    20. }
    21. }
    22. // If the window has already been added, but during resume
    23. // we started another activity, then don't yet make the
    24. // window visible.
    25. }

    可以看到先通过Activity的getWindowManager方法获取了WindowManager,WindowManager是一个接口它的实现类是WindowManagerImpl,所以wm.addView(decor,l)实际上是执行的WindowManagerImpl的addView方法,通过这个方法的名字也可以猜到是把DecorView和PhoneWindow联系起来的,我们也知道了是在handelResumeActivity方法中把window和View联系起来的。

     在上篇setContentView方法详解一文中我们知道Window是在Activity的attach方法中new出来的,

    同样在Activity的attach方法中对WindowManager做的处理:

    1. final void attach(Context context, ActivityThread aThread,
    2. Instrumentation instr, IBinder token, int ident,
    3. Application application, Intent intent, ActivityInfo info,
    4. CharSequence title, Activity parent, String id,
    5. NonConfigurationInstances lastNonConfigurationInstances,
    6. Configuration config, String referrer, IVoiceInteractor voiceInteractor,
    7. Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    8. mWindow.setWindowManager(
    9. (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
    10. mToken, mComponent.flattenToString(),
    11. (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    12. if (mParent != null) {
    13. mWindow.setContainer(mParent.getWindow());
    14. }
    15. mWindowManager = mWindow.getWindowManager();
    16. }
    17. public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
    18. boolean hardwareAccelerated) {
    19. mAppToken = appToken;
    20. mAppName = appName;
    21. mHardwareAccelerated = hardwareAccelerated;
    22. if (wm == null) {
    23. wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    24. }
    25. mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    26. }
    27. public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
    28. return new WindowManagerImpl(mContext, parentWindow); //可以看到WindowManager的实现类是WindowManagerImpl
    29. }

    5.分析WindowManagerImpl的addView方法 

    1. public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    2. applyDefaultToken(params);
    3. mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
    4. mContext.getUserId());
    5. }

    WindowManagerImpl中的对Window的操作,都是通过WindowManagerGlobal来完成的,这个WindowManagerGlobal是在创建WindowManagerImpl对象的时候创建:

    1. public final class WindowManagerImpl implements WindowManager {
    2. private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    3. }

    所以WindowManagerImpl的addView方法实际是调用的WindowManagerGlobal的addView的方法:

    1. @UnsupportedAppUsage
    2. private final ArrayList mViews = new ArrayList();
    3. @UnsupportedAppUsage
    4. private final ArrayList mRoots = new ArrayList();
    5. @UnsupportedAppUsage
    6. private final ArrayList mParams =
    7. new ArrayList();
    8. public void addView(View view, ViewGroup.LayoutParams params,
    9. Display display, Window parentWindow, int userId) {
    10. ......
    11. final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    12. ......
    13. ViewRootImpl root;
    14. root = new ViewRootImpl(view.getContext(), display);//⭐
    15. view.setLayoutParams(wparams);
    16. mViews.add(view); //⭐
    17. mRoots.add(root); //⭐
    18. mParams.add(wparams); //⭐
    19. // do this last because it fires off messages to start doing things
    20. try {
    21. root.setView(view, wparams, panelParentView, userId); //⭐
    22. } catch (RuntimeException e) {
    23. // BadTokenException or InvalidDisplayException, clean up.
    24. if (index >= 0) {
    25. removeViewLocked(index, true);
    26. }
    27. throw e;
    28. }
    29. }
    30. }

    一个Activity中wm.addView方法只会执行一次,ViewRootImpl是在addView方法中new出来的,所以一个Activity中只会有一个ViewRootImpl,在WindowManagerGlobal中维护着三个ArrayList集合,mViews是用来存储DecorView,mRoots是用来存储ViewRootImpl,mParams是用来存储window的LayoutParams,WindowManagerGlobal是一个单例,它是一个进程所有的窗口的管理类,WindowManagerGlobal通过ViewRootImpl是和WMS交互

    所以我们知道了,一个Activity对应一个Window(在Activity的attach方法中创建的PhoneWindo),一个Window对应一个WindowManagerImpl对象(一个window对应一个windowManager的实例,windowManager的实现类就是WindowManagerImpl),一个WindowManagerImpl对应一个ViewRootImpl,ViewRootImpl是和WMS交互来管理自己的窗口,WindowManagerGlobal是一个单例,它是一个进程所有的窗口的管理类,WindowManagerGlobal通过ViewRootImpl是和WMS交互的,View的绘制、事件分发都由它来控制

     6.分析ViewRootImpl的构造方法

    1. public ViewRootImpl(Context context, Display display, IWindowSession session,
    2. boolean useSfChoreographer) {
    3. mContext = context;
    4. mWindowSession = session; // 从wMS中创建的一个session对象,可以当成WMS的一个代理
    5. ......
    6. mThread = Thread.currentThread(); // ⭐这个mThread后面要用到
    7. ......
    8. mDirty = new Rect();
    9. ......
    10. mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
    11. context);// 用来保存当前窗口信息
    12. ......
    13. mChoreographer = useSfChoreographer // 编舞者
    14. ? Choreographer.getSfInstance() : Choreographer.getInstance();
    15. ......
    16. }

    7.分析ViewRootImpl的setView方法

    1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
    2. int userId) {
    3. synchronized (this) {
    4. if (mView == null) {
    5. ......
    6. requestLayout(); // ⭐真正开始绘制流程,单独分析
    7. ......
    8. res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
    9. getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
    10. mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
    11. mAttachInfo.mDisplayCutout, inputChannel,
    12. mTempInsets, mTempControls); // 将窗口添加到WMS上面
    13. ......
    14. mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
    15. Looper.myLooper()); // 接收事件
    16. ......
    17. view.assignParent(this); // view是DecorView,这个是把DecorView的父亲设置成当前的ViewRootImpl,
    18. //DecorView已经是View树的顶层,现在在上面加了一层,ViewRootImpl变成了View树的顶层了
    19. }
    20. }
    21. }

    总结:到此其实是View树的根DecorView是如何添加到Window上的流程,首先我们知道了是在ActivityThread的handleResumeActivity方法中且在Activity的onResume方法后添加的,从本质上来说DecorView其实并没有真正的像View那样Draw在Window上,而是通过WindowManagerImpl最终调用WindowManagerGlobal的addView方法,在这个方法中创建了一个VIewRootImpl作为DecorView的父亲,并且来管理DecorVIew(也就包括View树)的绘制、事件处理等,所以phoneWIndow通过WindowManager来管理ViewRootImpl,ViewRootImpl和WMS打交道来管理View和事件的处理。

    8.分析ViewRootImpl的requestLayout方法,真正的View绘制流程

    1. public void requestLayout() {
    2. if (!mHandlingLayoutInLayoutRequest) {
    3. checkThread(); // ⭐
    4. mLayoutRequested = true;
    5. scheduleTraversals(); // ⭐
    6. }
    7. }

    先分析checkThread:

    1. void checkThread() {
    2. if (mThread != Thread.currentThread()) {
    3. throw new CalledFromWrongThreadException(
    4. "Only the original thread that created a view hierarchy can touch its views.");
    5. }
    6. }

    mThread上面提到了,在ViewRootImpl的构造方法中获取的当前线程,ViewRootImpl默认是在主线程创建的,所以我们对UI的操作都必须在主线程,这样能够保证操作UI时的线程安全,这个才是为什么一定要在主线程绘制UI的本质,换句话说,如果在子线程创建的ViewRootImpl那对UI的操作必须在这个子线程进行,总之ViewRootImpl在哪个线程创建就只能在哪个线程更新UI

    9.分析ViewRootImpl的ScheduleTraversals方法

    1. void scheduleTraversals() {
    2. if (!mTraversalScheduled) {
    3. mTraversalScheduled = true;
    4. mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//handler机制的异步消息屏障开启
    5. mChoreographer.postCallback(
    6. Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); notifyRendererOfFramePending();
    7. pokeDrawLockIfNeeded();
    8. }
    9. }

    mTraversalRunable是要执行的任务:

    1. final class TraversalRunnable implements Runnable {
    2. @Override
    3. public void run() {
    4. doTraversal();
    5. }
    6. }
    7. final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    8. void doTraversal() {
    9. if (mTraversalScheduled) {
    10. mTraversalScheduled = false;
    11. mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);//移除异步消息屏障
    12. if (mProfile) {
    13. Debug.startMethodTracing("ViewAncestor");
    14. }
    15. performTraversals(); //开始绘制,⭐下面重点分析
    16. if (mProfile) {
    17. Debug.stopMethodTracing();
    18. mProfile = false;
    19. }
    20. }
    21. }

    10.分析ViewRootImpl中的performTraversals

    1. private void performTraversals() {
    2. ......
    3. windowSizeMayChange |= measureHierarchy(host, lp,
    4. mView.getContext().getResources(),
    5. desiredWindowWidth, desiredWindowHeight);
    6. ......
    7. performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    8. ......
    9. performLayout(lp, mWidth, mHeight);
    10. ......
    11. performDraw();
    12. }

  • 相关阅读:
    MySQL半同步复制源码解析
    R3300L, Q7 ATV Android9固件
    windows下载安装JavaJDK配置环境变量
    有没有matlab师傅可以看看为什么各向同性弹性波波场快照看起来不圆啊
    python barplot 比例bili scanpy
    数据分析师 医学数据分析 spss ,二项检验的概念和优缺点,适用场景 卡方检验和二相检验的区别
    功能测试、自动化测试、性能测试的区别
    Hvv--知攻善防应急响应靶机--Web2
    网络数据采集-免费网络数据采集软件
    【线性表 - 数组和矩阵】
  • 原文地址:https://blog.csdn.net/m0_37707561/article/details/126288573