Android WMS分析一:基础介绍

146 阅读4分钟

一、wms体系

  • 1.Window:android视图架构中,window就是一个窗口概念,android中所有的视图都是依赖窗口显示的

  • 2.WindowManager:管理类负责对window的管理:新增、更新、移除

  • 3.WMS:窗口的系统管理者,负责窗口的启动、添加、移除,也负责管理窗口的大小和层级(window的显示层级)

未命名绘图.drawio.png

窗口包括什么?

未命名绘图.drawio.png

窗口分类及优先级?

未命名绘图.drawio.png

窗口整体架构

未命名绘图.drawio.png

添加一个window的流程

addwindow.drawio.png

流程一:创建window######
//onstart生命周期 onresume有关 和executeLifecycleState相关
private Activity performLaunchActivity(ActivityClientRecord r,
Intent customIntent) {
         // 执行 Activity 的 attach、初始化 Window 等
         activity.attach(appContext, this, getInstrumentation(), r.token,
               r.ident, app, r.intent, r.activityInfo, title, r.parent,
               r.embeddedID, r.lastNonConfigurationInstances, config,
               r.referrer, r.voiceInteractor, window, r.configCallback,
               r.assistToken);
                   @UnsupportedAppUsage
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
        //创建phonewindow
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        //----设置window相关属性
        //与windowsManagerService进行关联
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
流程二:创建DecorView
//phonewindow的方法中
   @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            //安装Decor
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        
        private void installDecor() 
        if (mDecor == null) {
            //创建decorview
            mDecor = generateDecor(-1);
        
         protected DecorView generateDecor(int featureId) {
        Context context;
        //return new DecorView
        return new DecorView(context, featureId, this,getAttributes());
    }
        
流程三:addView
  //ActivityThread.java
  @Override
    public void handleResumeActivity(IBinder token, 
    boolean finalStateRequest, boolean isForward,String reason) {
    //调用addview方法
    wm.addView(decor, l);
  //WindowManagerImpl.java
   @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //实际调用到了WindowManagerGlobal.java实现
        mGlobal.addView(view, params, 
        mContext.getDisplayNoVerify(),      mParentWindow,mContext.getUserId());
    }
    
  //WindowManagerGlobal.java
  
    //全局的管理者。 是WindowManagerImpl的真正实现
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        ViewRootImpl root;
            //创建ViewRootimpl对象
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            try {
                root.setView(view, wparams, panelParentView, userId);
  //ViewRootImpl.java
  public void setView(View view, WindowManager.LayoutParams attrs, 
  View panelParentView, int userId) {
  //一系列的检查判断
  
  //调用addToDisplay
  mWindowSession.addToDisplayAsUser
  }
  
  //会调用到WMS的addWindow方法 
  public int addWindow

WindowManagerGlobal管理DecorView,创建ViewRootImpl实例,来进行对view的渲染工作。

ViewRootImpl用来做什么?

  • 1.管理View树
  • 2.出发view的测量、布局、绘制
  • 3.输入事件响应的接受
  • 4.负责与wms进行进程间通信

未命名绘图.drawio.png

ViewRootImpl接收到同步信号会出发view树的绘制。

未命名绘图.drawio.png

与wms进行通信

未命名绘图.drawio.png

更新window的流程

未命名绘图.drawio.png

//看下代码 WindowManagerImpl会调用到Global中的updateViewLayout
  @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //调用到WindowManagerGlobal.java
        mGlobal.updateViewLayout(view, params);
    }
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        //窗口参数
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        //view设置参数
        view.setLayoutParams(wparams);
        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            //更新参数
            mParams.remove(index);
            mParams.add(index, wparams);
            //调用到ViewRootImpl方法
            root.setLayoutParams(wparams, false);
        }
    }
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
     //触发绘制
     scheduleTraversals();
}

  @UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = 
            //发送同步消息
            mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

2.UI刷新

摘自百度: 电影放映的标准是每秒放映24帧,每秒遮挡24次,刷新率是每秒48次。这里的帧就是画面,也就是说电影每秒放映24幅画面,以达到动画的效果。

研究表明,人眼承受的极限为每秒55帧,还有研究表明,每秒60帧以上可以明显提升观众的观影感受。每秒120帧是每秒24帧的5倍,采用这样的拍摄技术可以让画面更加栩栩如生,让观众仿佛置身其中,给人一种似真似幻的感觉。

在手机上,帧数真是达到120hz。通常以60帧为主流。1秒60幅画面的切换。1000/60平均约16ms就会更新一幅画面。

当我们代码调用settext去设置一个值,就会触发ui的刷新。看一下刷新的流程。

ui绘制vsync交互图.drawio.png 跟踪下代码

/最终调用到setText方法中
    @UnsupportedAppUsage
    private void setText(CharSequence text, BufferType type,
                         boolean notifyBefore, int oldlen) {
       if (mLayout != null) {
            //进行布局检测
            //检查全新的文本是否需要新的视图布局或者仅仅是一个新的文本布局。
            checkForRelayout();
        }
   }
   
 private void checkForRelayout() {
     //判断是否是更改textview的布局还是只是设置值
     //然后调用invalidate()方法
     invalidate();
 }
 
//调用到父类view的invalide方法中
public void invalidate(Rect dirty) {
        final int scrollX = mScrollX;
        final int scrollY = mScrollY;
        //实际调用
        invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,
                dirty.right - scrollX, dirty.bottom - scrollY, true, false);
    }
    
//继续调用到viewgroup的invalidateChild
//这是一个过渡方法会调用到
public final void invalidateChild(View child, final Rect dirty) {
if (mParent != null) {
            //调用到ViewRootImpl的方法中
            mParent.onDescendantInvalidated(this, target);
        }
}

//ViewRootImpl.onDescendantInvalidated 调用到invalidate
 @UnsupportedAppUsage
    void invalidate() {
        mDirty.set(0, 0, mWidth, mHeight);
        if (!mWillDrawSoon) {
            //触发绘制
            scheduleTraversals();
        }
    }
    
    
    @UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            //发送同步屏障消息
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //执行代码
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
 
 //调用代码Choreographer.postCallbackDelayedInternal
 private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        synchronized (mLock) {
            if (dueTime <= now) {
                //等待执行命令
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }
    
    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            //等待同步垂直信号vsync
            if (USE_VSYNC) {
            //省略
        }
    }
    
//接收到vsync信号后,会执行到
rivate final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
            long now = System.nanoTime();
            if (timestampNanos > now) {
                Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                        + " ms in the future!  Check that graphics HAL is generating vsync "
                        + "timestamps using the correct timebase.");
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            //执行run方法 doFrame
            doFrame(mTimestampNanos, mFrame);
        }
   }
   //doFrame会封装一系列的消息,然后执行
   doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
   //执行到CallbackRecord.run方法
   //又调用回ViewRootImpl.TraversalRunnable方法
      final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
  //简化下调用链
  doTraversal()
          -->performTraversals()
                  -->measureHierarchy
                      -->performMeasure
                        -->mView.measure(childWidthMeasureSpec,childHeightMeasureSpec);
 //最终触发布局的测量、绘制。setText的流程完成

ui更新代码流.drawio.png 用一个简单的代码调用顺序图表示,通信用了handler发送消息。

本篇就到此结束。