Actvity绘制流程

本文详细探讨了Android Activity的绘制流程,从setContentView()开始,经过PhoneWindow的创建,到handleResumeActivity的调用,进一步分析了WindowManager的设置与获取,最后进入ViewRootImpl进行布局的绘制。在onCreate()方法中,主要处理setContentView,而在handleResumeActivity中,利用WindowManager和ViewRootImpl完成了最终的绘制工作。


本文的绘制流程也是基于之前的几篇文章的(同上基于API——15)
1. 安卓应用启动流程分析
2. ActivityThread启动页面分析
3. Activity创建流程分析
上片 Activity创建流程分析说到Activity已经创建了并通过Instrumentation调用了Activity的onCreate方法,那么接下来需要干的事情就很明显了吧,我们在onCreate 的时候一般就是通过setContentView()方法设置布局,设置之后呢,那不知道谁反正是绘制出来了,本文的目的就在于,紧接上文寻找Activity是通过哪些类渲染页面的

setContentView()

public void setContentView(int layoutResID) {
  // 各位大爷 真是基于api 15 的,高版本的 不一样的
  		//getWindow  获取到当前绑定的Window  window本身就是一套接口,我们看的是具体实现的地方,上文attach方法提到了
  		//创建的window是PhoneWindow  源码在Policy类里面,
        getWindow().setContentView(layoutResID);
        initActionBar();
    }

进入PhoneWindow

@Override
public void setContentView(int layoutResID) {
     if (mContentParent == null) {
         installDecor();
     } else {
         mContentParent.removeAllViews();
     }
     //这个逻辑已经不能再清晰了,最终我们写的View要添加到mContentParent中,他是一个ViewGroup
     mLayoutInflater.inflate(layoutResID, mContentParent);
     final Callback cb = getCallback();
     if (cb != null && !isDestroyed()) {
         cb.onContentChanged();
     }
   }

首先查看mContentParent 是如何创建的

private void installDecor() {
        if (mDecor == null) {
        // 这里就是直接new DecorView(getContext(), -1); new了一个DecorView
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
        }
        if (mContentParent == null) {
        //到这个方法才生成,下面进入查看
            mContentParent = generateLayout(mDecor);
            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
            if (mTitleView != null) {
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                    View titleContainer = findViewById(com.android.internal.R.id.title_container);
                    if (titleContainer != null) {
                        titleContainer.setVisibility(View.GONE);
                    } else {
                        mTitleView.setVisibility(View.GONE);
                    }
                    if (mContentParent instanceof FrameLayout) {
                        ((FrameLayout)mContentParent).setForeground(null);
                    }
                } else {
                    mTitleView.setText(mTitle);
                }
            } else {
             //处理id是ActionBar 这里就不看了
            }
        }
    }

protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        TypedArray a = getWindowStyle();
        //根据当前的主题设置窗口属性
        ......
        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();

        //根据当前的窗口属性选择相对应的布局
        ......
        //将相应的布局文件转成view添加到窗口视图对象decor中
        View in = mLayoutInflater.inflate(layoutResource, null);
        //这里我们可以断定 DecoreView(本身就是一个Framelayout)就是我们的顶层View 了,
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;
		//这个不就是我们的容器吗 我们最终的View是添加到这个View里面的
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        ......
        return contentParent;
    }

以上的分析我们只是看到了 setContentView最终是把我们的View填充了,但是我们来此的目的并不只是如此吧,我们还想知道 具体的绘制流程,谁绘制的还记得我们的performMeasure 。。 等那三个方法吗,现在还没有看到啊,大胸弟,onCreate方法里面就这么多,说明还没有到绘制的那一步啊,那么在哪里进行绘制的呢?当然是生命周期的onReuse之前绘制完成不就好了,那我们去看看。

handleResumeActivity

上文Activity创建流程分析 走了handleLaunchActivity中我们是不是又handleResumeActivity 对吧,这个很明确了,首先handleLaunchActivity 调用了onCreate方法,里面把布局什么的如上添加好,但只是吧资源文件加载了,还没有进行最终的绘制,下面看 handleResumeActivity如何绘制

 final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
 // 略去。。。
 r.window = r.activity.getWindow();
   View decor = r.window.getDecorView();
   decor.setVisibility(View.INVISIBLE);
   ViewManager wm = a.getWindowManager();
   WindowManager.LayoutParams l = r.window.getAttributes();
   a.mDecor = decor;
   l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
   l.softInputMode |= forwardBit;
   if (a.mVisibleFromClient) {
       a.mWindowAdded = true;
       //获取到WindowManager 然后通过他添加了一个View,那么说明绘制不在Activity 中,而是交给了WindowManager
       wm.addView(decor, l);
   }    
 // 略去。。。

//还记得之前activity 的attach方法吗,有提到 PhoneWindow 而获取WindowManager mWindow.getWindowManager(); 不就是他

我们进入到 PhoneWindow的这个方法看她如何操作
 mWindow.setWindowManager(null, mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
    mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();

WindowManager设置与获取

  public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        if (wm == null) {
        //从这里获取到一个WindowManager的实例
            wm = WindowManagerImpl.getDefault();
        }
        mWindowManager = new LocalWindowManager(wm, hardwareAccelerated);
    }

那么我们的WindowManager 的对象找到了,紧接着上文的addView方法,我们继续追踪

 private void addView(View view, ViewGroup.LayoutParams params,
            CompatibilityInfoHolder cih, boolean nest) {
      
        final WindowManager.LayoutParams wparams
                = (WindowManager.LayoutParams)params;
        
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (this) {ocked(view, false);
            if (index >= 0) {
                root = mRoots[index];
                root.mAddNesting++;
                view.setLayoutParams(wparams);
                root.setLayoutParams(wparams, true);
                return;
            }
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews != null ? mViews.length : 0;
                for (int i=0; i<count; i++) {
                    if (mRoots[i].mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews[i];
                    }
                }
            }
            //创建了ViewRootImpl 他也是真正的执行者
            root = new ViewRootImpl(view.getContext());
            root.mAddNesting = 1;
            view.setLayoutParams(wparams);
            
            if (mViews == null) {
                index = 1;
                mViews = new View[1];
                mRoots = new ViewRootImpl[1];
                mParams = new WindowManager.LayoutParams[1];
            } else {
                index = mViews.length + 1;
                Object[] old = mViews;
                mViews = new View[index];
                System.arraycopy(old, 0, mViews, 0, index-1);
                old = mRoots;
                mRoots = new ViewRootImpl[index];
                System.arraycopy(old, 0, mRoots, 0, index-1);
                old = mParams;
                mParams = new WindowManager.LayoutParams[index];
                System.arraycopy(old, 0, mParams, 0, index-1);
            }
            index--;

            mViews[index] = view;
            mRoots[index] = root;
            mParams[index] = wparams;
        }
        // root ? 是什么 他是 ViewRootImpl  最终的操作又交给了 他
        root.setView(view, wparams, panelParentView);
    }

进入ViewRootImpl

 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
        //除了这行其他的全部省略,其中包括和WindowManagerService的通信,留着以后讲,今天就看绘制流程。
          requestLayout();
        }
    }
 public void requestLayout() {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
//最终会进入到这个方法 ,我天是不是很熟悉,我们最常说道的 performMeasure   performLayout  performDraw ,没错就是在这里进行的
private void performTraversals() {  
        ......  
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ...
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
        ......  
        performDraw();
        }
        ......  
    }

见到这里的时候我们就已经很清楚了

  • Activity attach 方法创建了PhoneWindow 以及WindowManager
  • Activity的onCreate 方法里面主要是对setContentView的处理,把布局挂到根布局上
  • handleResumeActivity 利用了WindowManager 以及ViewRootImpl 对已经设置好的布局进行绘制。到这里,我们Activity的启动流程到最终绘制 已经完成了!

以上几篇文章都是本人用sourseInsight 边看源码一边撸的,如果有错误的地方还请指出来!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值