4.1 初始ViewRoot和DecorView

1.一切从setContentView说起以下源码均取自Android API 23。

public class ViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view);
    }
}

AppCompatActivity中

public class AppCompatActivity extends FragmentActivity implements AppCompatCallback{
    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }
}

AppCompatDelegate中

public abstract class AppCompatDelegate {
    public abstract void setContentView(@LayoutRes int resId);
}

其实现AppCompatDelegateImplV7:

class AppCompatDelegateImplV7 extends AppCompatDelegateImplBase{.
    private ViewGroup mSubDecor;
    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mOriginalWindowCallback.onContentChanged();
    }
}
// AppCompatDelegateImplBase继承自AppCompatDelegate
abstract class AppCompatDelegateImplBase extends AppCompatDelegate

上面的代码除了第一行外,都很好理解。mSubDecor拿到contentParent,并把传入的resId以子View的形式添加到contentParent。可见contentparent是我们布局文件的父View,也是decor的子View。而第一行代码就是初始化mSubDecor.

private void ensureSubDecor() {
    if (!mSubDecorInstalled) {
        mSubDecor = createSubDecor();
        ...
    }
}
private ViewGroup createSubDecor() {
    
    mWindow.getDecorView();

    final LayoutInflater inflater = LayoutInflater.from(mContext);
    ViewGroup subDecor = null;

    if (!mWindowNoTitle) {
        if (mIsFloating) {
            subDecor = (ViewGroup) inflater.inflate(
                    R.layout.xxx, null);
            mHasActionBar = mOverlayActionBar = false;
        } else if (mHasActionBar) {
            subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                    .inflate(R.layout.xxx, null);
        }
    } else {
        subDecor = (ViewGroup) inflater.inflate(
                    R.layout.xxx, null);
    }

    mWindow.setContentView(subDecor);

    return subDecor;
}

PhoneWindow是Window的实现类,看里面的上文出现的两个方法

@Override
public final View getDecorView() {
    if (mDecor == null) {
        installDecor();
    }
    return mDecor;
}
private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor();
    }
}        
protected DecorView generateDecor() {
    return new DecorView(getContext(), -1);
}
@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    mLayoutInflater.inflate(layoutResID, mContentParent);
}

到这里为止,我们的布局文件已经加载到了Window中。下面看布局文件如何显示。

2. ViewRootImpl

先了解Activity的创建过程。我们知道java程序的执行是从main方法开始的,而Activity的main方法在ActivityThread中,Activity的生命周期也是在这里完成的。其他的先不多讲,先看Activity的启动方法

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed);
    }
}    
final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
   if (r.window == null && !a.mFinished && willBeVisible) {
        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;
            wm.addView(decor, l);
        }
    } 
}    

看ViewManager的addView方法

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

WindowManager继承ViewManager,也是接口。其实现类WindowManagerImpl

public final class WindowManagerImpl implements WindowManager{
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
}

Global的addVeiw方法

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);
}

最终ViewRootImpl把WindowManager和DecorView连在一起。

3. ViewRootImpl

ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。

View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout、draw三个过程才能最终将一个View绘制出来。其中measure测量View的宽高,layout确定View在父容器中放置的位置,而draw负责将View绘制在屏幕上。

performTraversals会依此调用performMeasure、performLayout和performDraw三个方法,这三个方法分别完成顶级View的measure、layout、draw三大流程。其中performMeasure会调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到了子元素中,这样就完成了一次measure过程。performLayout和performDraw的流程类似,唯一不同的是performDraw的传递过程是dispatchDraw来实现的。

4. setContentView

我们知道我们的布局文件和DecorView之间有一个ContentView。这个ContentView的id是android.R.content。所以是setContentView,而不是setView().

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容