分析View

setContentView

首先我们直接在Android Studio中找到一个Activity(请注意,本文分析的是Activity,如果你看的是AppCompatActivity,实际代码会有出入),然后找到setContent方法然后点进去,我们可以看到

public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
···
}

然后查找getWindow()方法

private Window mWindow;
public Window getWindow() {
return mWindow;
}

得知调用的是Window类的setContent()方法。然后再全类搜索mWindow,在attach方法中找到了赋值语句。

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) {
···
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
···
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());
}
mWindowManager = mWindow.getWindowManager();
···
}

然后就引出了我们今天主要分析的对象PhoneWindow。

PhoneWindow

查找PhoneWindow的setContentView方法,可以看到有三个重载方法。

@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
} if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
} @Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
} @Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
} if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}

三个方法大体的处理流程是:

  1. 初始化DecorView
  2. 检查并处理转场动画
  3. 将实际要显示的Layout或者View添加到mContentParent中
  4. 通知Callback(即Activity)调用onContentChanged方法

DecorView

在上面的流程里面,相对比较重要的就是第一步的初始化DecorView,也就是installDecor()方法,下面我们继续分析这个方法。

private void installDecor() {
···
if (mDecor == null) {
mDecor = generateDecor(-1);
···
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
···
}
···
}

installDecor()方法里,主要做的就是两件事,一个是generateDecor,生成mDecor,还有一个是generateLayout,生成mContentParent。generateDecor中,没有太多复杂逻辑,就是做一些判断,然后实例化出来一个DecorView,这里需要说一点的就是在Android7.0以前,DecorView是PhoneWindow的内部类,7.0以后,DecorView单独提出来变成了一个类,所以如果有用到反射的话,这里可能会出现问题,需要做好版本判断。然后我们看下generateLayout的逻辑:

protected ViewGroup generateLayout(DecorView decor) {
//通过系统获取样式
TypedArray a = getWindowStyle();
//然后一系列的判断,获取样式里的属性,然后设置features
//例如是否悬浮,是否有Title等等
···
int layoutResource;
int features = getLocalFeatures();
//拿到刚才设置的features,作出一系列if eles判断,找出对应的resourceId
//然后调用DecorView的onResourcesLoaded,对这个layout进行inflate
···
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //通过com.android.internal.R.id.content找到对应的mContentParent
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
//在经过一系列后续设置
···
mDecor.finishChanging();
//最后返回contentParent
return contentParent;
}

在generateLayout中,大概的流程是这样的。

  1. 先获取样式
  2. 设置样式到Window的features里
  3. 拿到features,判断对应的resourceId
  4. 通过resourceId,inflate出来一个ViewGroup,添加到mDecor中
  5. 再通过findViewById,找到刚刚inflate的ViewGroup中的 com.android.internal.R.id.content,作为mContentParent
  6. 在经过一系列设置
  7. 返回contentParent

小结

到此,我们的setContentView就已经基本走完了,剩下的就等着Activity、WindowManager、WindowManagerGlobal、ViewRootImpl去调用了,这些类的调用,涉及到了Activity的启动流程,我们会在其他笔记中详细分析这一过程。

下面会上一张整个setContentView的时序图,用来巩固一下刚才的流程。

上图为Android setContentView的时序图

系列文章

Android 视图及View绘制分析笔记之setContentView
View绘制分析笔记之onMeasure
View绘制分析笔记之onLayout
View绘制分析笔记之onDraw

1.Android 视图及View绘制分析笔记之setContentView的更多相关文章

  1. 3.View绘制分析笔记之onLayout

    上一篇文章我们了解了View的onMeasure,那么今天我们继续来学习Android View绘制三部曲的第二步,onLayout,布局. ViewRootImpl#performLayout pr ...

  2. 4.View绘制分析笔记之onDraw

    上一篇文章我们了解了View的onLayout,那么今天我们来学习Android View绘制三部曲的最后一步,onDraw,绘制. ViewRootImpl#performDraw private ...

  3. 2.View绘制分析笔记之onMeasure

    今天主要学习记录一下Android View绘制三部曲的第一步,onMeasure,测量. 起源 在Activity中,所有的View都是DecorView的子View,然后DecorView又是被V ...

  4. Android之View绘制流程开胃菜---setContentView(...)详细分析

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 1 为什么要分析setContentView方法 作为安卓开发者相信大部分都有意或者无意看过如下图示:PhoneWindow,DecorView这些 ...

  5. Android面试,View绘制流程以及invalidate()等相关方法分析

    整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为 根据之前设置的状态,判断是否需要重新计算视图大小(measu ...

  6. Android视图组成View

    视图组成View 创建时间: 2013-9-13 10:51 更新时间: 2013-9-13 11:04

  7. Android视图控件架构分析之View、ViewGroup

    在Android中,视图控件大致被分为两类,即ViewGroup和View,ViewGroup控件作为父控件,包含并管理着子View,通过ViewGroup和View便形成了控件树,各个ViewGou ...

  8. android事件分发源码分析—笔记

    昨天晚上从源码角度复习了一下android的事件分发机制,今天将笔记整理下放在网上.其实说复习,也是按着<android开发艺术探索>这本书作者的思路做的笔记. 目录 事件是如何从Acti ...

  9. Android GUI之View绘制流程

    在上篇文章中,我们通过跟踪源码,我们了解了Activity.Window.DecorView以及View之间的关系(查看文章:http://www.cnblogs.com/jerehedu/p/460 ...

随机推荐

  1. 初识pipeline

    1.pipeline的产生 从一个现象说起,有一家咖啡吧生意特别好,每天来的客人络绎不绝,客人A来到柜台,客人B紧随其后,客人C排在客人B后面,客人D排在客人C后面,客人E排在客人D后面,一直排到店面 ...

  2. ruby 基础知识(二)

    ruby  中的动态方法 http://singleant.iteye.com/blog/1680382 Rails 大量使用了符号(symbol).符号看上去很像变量名,不过以冒号作为前缀.符号的例 ...

  3. spring jdbc 查询结果返回对象、对象列表

    首先,需要了解spring jdbc查询时,有三种回调方式来处理查询的结果集.可以参考 使用spring的JdbcTemplate进行查询的三种回调方式的比较,写得还不错. 1.返回对象(queryF ...

  4. Sublime Text3 BracketHighlighter高亮色彩配置

    今天玩Sublime ,结果安装了BracketHighlighter插件后,没效果,郁闷,度娘后总结如下: BracketHighlighter插件能为Sublime Text提供括号,引号这类高亮 ...

  5. 关于MapReduce中自定义带比较key类、比较器类(二)——初学者从源码查看其原理

    Job类 /**   * Define the comparator that controls    * how the keys are sorted before they   * are pa ...

  6. Apache服务器安装过程及问题的解决(for windows system32bit)

    在使用Hbuilder设计网站时,在制作本站搜索时,用到了Php文件,而Hbuilder的内置web服务器不支持php的解析, 所以需要安装配置外部服务器,有多个选择,我安装的apache服务器,并遇 ...

  7. Go 项目的目录结构 及 安装技巧

    项目目录结构如何组织,一般语言都是没有规定.但 Go 语言这方面做了规定,这样可以保持一致性 1.一般的,一个 Go 项目在 GOPATH 下,会有如下三个目录: |--bin |--pkg |--s ...

  8. 页面解耦—— 统跳协议和Rewrite引擎

    原文: http://pingguohe.net/2015/11/24/Navigator-and-Rewrite.html 解耦神器 —— 统跳协议和Rewrite引擎 Nov 24, 2015 • ...

  9. Android app AOP添加埋点技术总结

    目标:通过面向切面编程来实现对源代码无侵入的埋点.     方式 能力 缺点 学习曲线   XPosed 运行期hook 能hook自己应用进程的方法: 能hook别的应用的方法: 能hook系统方法 ...

  10. visual stuido 跨解决方案调试

    visual stuido 跨解决方案调试 一个解决方案是一个第三方库,另一个是单独的程序.调试的时候要同时跟踪源码.因为第三方库并没有直接使用它的源码,而是使用生成的dll,直接进行调试比较麻烦,会 ...