Android笔记--View绘制流程源码分析

View绘制之前框架流程分析

View绘制的分析始终是离不开Activity及其内部的Window的。在Activity的源码启动流程中,一并包含

着Activity中window的创建以及view的绘制流程。

在Activity启动流程进行到ActivityThread.performLaunchActivity时,利用反射创建了activity实例。

并且紧接着调用了activity的attach方法。进行activity的成员初始化工作。其中就包括对window对象的

初始化。

这段源码如下:(为了逻辑清晰,对其中有代码删减)

ActivityThread.performLaunchActivity(ActivityClientRecord r, Intent customIntent)方法

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); ...省略 Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(//1 这里是activity实例化
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
} try {//2 拿到已经实例化过的app对象
Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (activity != null) { .... 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);//3 对activity初始化 ..... //4 调用onCreate回调
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
} ..... if (!r.activity.mFinished) {
activity.performStart();//5 onStart回调
r.stopped = false;
} ..... } catch (SuperNotCalledException e) {
throw e; } catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
} return activity;
}

步骤3 对activity初始化 | Activity.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) {
attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this);//得到Window对象 ...省略 mWindow.setWindowManager(//给当前activity的 Window对象设置WM,mToken是一个IBinder,
//WMS就是通过这个IBinder来管理Activity里的View
(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();
mCurrentConfig = config;
}
步骤4 调用onCreate回调

第四步以后会逐步调用到activity的onCreate方法。这个方法就是我们自己的Activity.onCreate方法中

的setContentView那一套。

源码调用如下:

Activity.setContentView方法:

public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);//getWindow拿的是attach方法中的PhoneWindow
//setContentView就是PhoneWindow的方法
initWindowDecorActionBar();
}

PhoneWindow.setContentView

@Override
public void setContentView(int layoutResID) { if (mContentParent == null) {
installDecor();//这里生成一个DecorView
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
} 。。。省略
}

PhoneWindow.installDecor

 private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
//...省略
} //省略
}

到onCreate完成为止,window和decorview还并未产生联系

直到ActivityThread.handleResumeActivity执行

ActivityThread.handleResumeActivity会触发Activity.onResume方法回调

final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) { 。。。省略 //拿到acitivty记录,执行activity.performResume,最终回调onResume
ActivityClientRecord r = performResumeActivity(token, clearHide); if (r != null) {
final Activity a = r.activity; 。。。省略 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);
} } else if (!willBeVisible) {
...省略
} if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) { 。。。省略 r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();//这里将window和decorview关联
}
} 。。。省略 } else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null, false);
} catch (RemoteException ex) {
}
}
}

Activity.makeVisiable

void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}

可见onResume方法回调时并不会显示UI,而是随后调用的mDecor.setVisibility(View.VISIBLE);

让界面可见。

wm.addView(mDecor, getWindow().getAttributes());让decor和phonewindow关联起来

WindowManager的addView的具体实现在WindowManagerImpl中。而WindowManagerImpl是对WindowManagerGlobal

的一个静态代理类

WindowManagerGlobal.addView

public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) { 。。。省略 ViewRootImpl root;
View panelParentView = null; 。。。省略 root = new ViewRootImpl(view.getContext(), display);//这里真正new出了负责绘制流程的类 view.setLayoutParams(wparams);//对传入的decorview设置布局参数 mViews.add(view);
mRoots.add(root);
mParams.add(wparams); try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
...省略
}
}

WindowManagerGlobal.addView之后真正开始绘制流程

root.setView(view, wparams, panelParentView);

这个方法的调用意味着decorl利用window的布局参数开始了绘制的管理工作。

ViewRootImpl继承了ViewParent接口。实现了requestLayout invalidateChild等方法。实现对视图的操作。

正如ViewRootImpl类的描述信息所说:

/**
* The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
* detail of {@link WindowManagerGlobal}.
* 这个类管理着视图结构。实现了View和WM之间必要的联系
* {@hide}
*/

ViewRootImpl会得到decorview中的viewparent对象。当某些view的viewparent相同时意味着他们在

同一个view树下。

所以每次调用view.requestLayout 其实就是在调用ViewRootImpl。因此整个的绘制流程都在由ViewRootImpl把控

因为在ViewRootImpl的setView方法调用的时候,decorView已经把自己的ViewParent设置为ViewRootImpl:

/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view; ...省略 mAttachInfo.mRootView = view;
mAttachInfo.mScalingRequired = mTranslator != null;
mAttachInfo.mApplicationScale =
mTranslator == null ? 1.0f : mTranslator.applicationScale; 。。。省略 try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
。。。省略
} finally {
if (restore) {
attrs.restore();
}
} ...省略 view.assignParent(this);//在这里decorView把自己的ViewParent设置为ViewRootImpl
。。。省略
}
}
}

Window WindowManager WindowManagerService View四方的联系可以简化如下:

这里利用WindowManager.addView添加的mView使用的ViewParent是不同的ViewRootImpl提供的。

所以他们属于不同的view树。所以当你调用DecorView中某个View的requestLayout并不会对WM.addView的

视图产生影响。

从上面的源码分析可以得出以下结论:

  1. activity中 getWindowManager().addView(mView,wl)和 addContentView(mView,wmParams)是有区别的

    前者会调用WindowManagerGlobal的addView.重新产生一个ViewRootImpl给mView

    后者是利用已经建立的ViewRootImpl给mView

  2. ViewRootImpl是实际管理Window中所有View的类,每个Activity中ViewRootImpl数量取决于调用mWindowManager.

    addView的调用次数

  3. Window存在的意义在于优化代码结构:

    从上面的分析可以得出Window或者说PhoneWindow没有做具体的View操作。

    因为在整个过程中:

    activity提供app进程和system_server进程中WMS通信的token.而activity的DecorView则利用

    实现了ViewParent接口的ViewRootImpl负责view树中绘制的整个流程。

    期间没有Window的作用。但是window从代码结构上解开了activity和view的耦合。把从view的创建

    到和viewrootimpl的交互中脱离出来。减轻activity的任务量。

    把acitivty和view有关的操作交给window去做。而window把任务交给viewRootImpl去完成

  4. Dialog的使用过程中也会走和activity创建Window DecorView 关联两者 为DecorView添加ViewRootImpl

    的整个流程。

    PopupWindow和dialog不同他不会创建一个新的Window.他的整个流程是利用的WindowManager.addView(decorView, p);

    PopupWindow会生成一个新的DecorView ,并会重新产生一个ViewRootImpl给decorView

Android笔记--View绘制流程源码分析(一)的更多相关文章

  1. Android笔记--View绘制流程源码分析(二)

    Android笔记--View绘制流程源码分析二 通过上一篇View绘制流程源码分析一可以知晓整个绘制流程之前,在activity启动过程中: Window的建立(activit.attach生成), ...

  2. Android之View绘制流程源码分析

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 对于稍有自定义View经验的安卓开发者来说,onMeasure,onLayout,onDraw这三个方法都不会陌生,起码多少都有所接触吧. 在安卓中 ...

  3. Android开发——View绘制过程源码解析(二)

    0. 前言   View的绘制流程从ViewRoot的performTraversals开始,经过measure,layout,draw三个流程,之后就可以在屏幕上看到View了.上一篇已经介绍了Vi ...

  4. Android开发——View绘制过程源码解析(一)

    )UNSPECIFIED:表示View可以设置成任意的大小,没有任何限制.这种情况比较少见. 2. MeasureSpec的生成过程 2.1 顶级View的MeasureSpec // desired ...

  5. Android Touch事件派发流程源码分析

    分native侧事件派发到java侧和Framework派发事件到UI,流程看源码即可,此处不赘叙, Native侧派发事件的干活类图如下:

  6. [Android]从Launcher开始启动App流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...

  7. [Android]Android系统启动流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...

  8. Spring加载流程源码分析03【refresh】

      前面两篇文章分析了super(this)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码, Spring加载流程源码分析01[su ...

  9. Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)

    上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...

随机推荐

  1. Unique Binary Search Trees-计算表示相同序列的不同BST个数

    题目描述: 给定整数n,计算存储序列为1...n的结构唯一的BST的个数 题目来源: http://oj.leetcode.com/problems/unique-binary-search-tree ...

  2. css3中变形处理

    transfrom功能 在css3 中可以使用transfrom功能实现文字或图像的旋转,缩放,倾斜,移动等变形处理 deg是css3中使用的一种角度单位. 旋转: 使用rotate方法,在参数中加入 ...

  3. Fran&ccedil;ois&nbsp;Hollande’s&amp;…

    EVER since President François Hollande was elected last May, things have not gone right for him. He ...

  4. CentOS6下安装Java jdk1.7.0_10和 maven

    安装步骤如下: 1. 下载JDK7.0_10 (jdk-7u10-linux-i586.tar.gz) 地址: 2. 卸载系统自带的开源JDK 查看是否安装JDK rpm -qa | grep jav ...

  5. 7.20实习培训日志-Java基础程序设计结构

    Java基础程序设计结构 在 Math 类中,为了达到最快的性能,所有的方法都使用计算机浮点单元中的例程,如果得到一个完全可预测的结果比运行速度更重要的话,那么就应该使用StrictMath类,它使用 ...

  6. sqlserver2012——逻辑运算符

    ALL 如果一组的比较都为TRUE,则结果为true ANY如果玉足比较中任何一个为true,则结果为true AND 两个boll都为TRUE,则结果为TRUE OR 两个BOLL任何一个TRUE, ...

  7. Python之将Python字符串生成PDF

      笔者在今天的工作中,遇到了一个需求,那就是如何将Python字符串生成PDF.比如,需要把Python字符串'这是测试文件'生成为PDF, 该PDF中含有文字'这是测试文件'.   经过一番检索, ...

  8. SpringMVC中的常用注解

    RequestParam 作用: 用于  将请求参数区数据  映射到  功能处理方法的参数上. 属性: value  请求参数中的名称 required   请求参数中是否必须提供此参数. 默认值: ...

  9. python_文件目录的操作

    Python文件的主要操作 主要包括: 1,创建一个文件 2,删除一个文件 3,创建一个目录 4,删除一个目录 5,拷贝,重命名,查看文件大小 6,列出某个目录下文件的数量 7,递归打印某个目录下的所 ...

  10. solidity 学习笔记(6)call 函数

    call() 方法 call()是一个底层的接口,用来向一个合约发送消息,也就是说如果你想实现自己的消息传递,可以使用这个函数.函数支持传入任意类型的任意参数,并将参数打包成32字节,相互拼接后向合约 ...