Android系列:Activity的显示原理

312 阅读3分钟

1.相关问题

  • Activity的显示原理(window/DecorView/Viewroot)
  • Activity的UI刷新机制(Vsync/Choreographer)
  • UI绘制原理(Messure/Layout/Draw)
  • Surface原理(Surface/SurfaceFlinger)

2.Activity的显示原理

我们从setContentView入手,Activity里面的Window实质是PhoneWindow,这里的是PhoneWindow的里面的

increase() # setContentView
    @Override
    public void setContentView(int layoutResID) {
        
        if (mContentParent == null) {
           // 1 构建DecorView
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        // 3加载布局文件到DecorView
        mLayoutInflater.inflate(layoutResID, mContentParent);

    }
    
    private void installDecor() {
        //new DecorView
        mDecor = new DecorView(context, featureId, this, getAttributes());
        mDecor.addView();
        // 2
        mContentParent = findViewById(R.id.content)
    }

小结:

  • 1.创建DecorView
  • 2.inflate我们的布局文件
  • 将我们的布局文件添加到DecorView里面去。

注意 setContentView之后 页面并没有展示,它仅仅是构建了一下数据结构而已吗,接着看,这里 oncreate就走完了。接着看;

resume
    final void handleResumeActivity(IBinder token) {
            // onResume 回调
            r = performResumeActivity(token, clearHide, reason);
            
            if (r.window == null && !a.mFinished && willBeVisible) {
            //拿到PhoneWindow
                r.window = r.activity.getWindow();
                //拿到Decorview
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                //拿到WindowManager
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                //添加view
                wm.addView(decor, l);
                }
            }
            

接着我们看看WindowManager是怎么添加View的

public interface WindowManager extends ViewManager {}


public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;
    
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
}

可知,WindowManager是一个接口,其实现类是在WindowManagerImpl,WindowManagerImpl里面的使用了桥接器的模式,将操作window的任务交接给了WindowManagerGlobal。

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {

            ViewRootImpl root;
            View panelParentView = null;
            //1创建ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);
            
            view.setLayoutParams(wparams);
            //2 将View添加到View root里面去
            mViews.add(view);
            //3
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
            //4 调用ViewRootImpl的setView
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            
              if (mView == null) {
                mView = view;
                  
                // Schedule the first layout -before- adding tothe window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                //调用刷新机制 这个是View绘制的开始
                requestLayout();
                //IPC 调用 到WMS里面去
                res = mWindowSession.addToDisplay(mWindow);
              }
        }
   @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
           //注意这里的checkThread 
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    
       void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
    
     void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    
  

这里高度注意,==checkThread里面的作用,为啥不能在子线程里面刷新ui,这里是原因所在,==

==mChoreographer== 这个是干什么的,在卡顿布局优化里面有用到。 Choreographer是用获取FPS的,可以带到线上去,具备实时性,在API16后可以使用

  private void performTraversals() {
    //向WMS申请surface
      Surface surface = new Surface();
        
      performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
      
      performLayout(lp, mWidth, mHeight);
      
      performDraw();
  }

这里要注意,==在绘制之前首先向WMS申请surface==,这一步非常重要,因为后面的所以绘制和显示都是在surface上进行的。

绘制结束后,会通过Session 这个Binder对象调用到WMS的addWindow里面去,对WMS而言,它管理这Window的显示层级。

==ViewRootImpl是DecorView和WMS通信的桥梁,里面的重点是session==

WMS的作用

  • 分配Surface
  • 掌管surface显示顺序以及位置尺寸
  • 控制窗口动画
  • 输入事件分发

3.问题:Activity的显示原理

  • 1.PhoneWindow是什么,怎么创建
  • 2.setContentView的原理,DecorView是什么
  • 3.ViewRoot是什么,有什么作用
  • 4.View的显示原理是什么,WMS发挥的作用