研究在布局文件加载之后,View是如何被显示到屏幕上来的,也就是研究View的显示过程。下面将从以下两个方面做介绍:1、从加载layout文件到view的测量、布局和绘制的全过程。2、surface、surfaceFlinger与view是如何关联起来的。
在Activity的onCreate方法中,我们通过setContentView(view)来初始化定义好的一个view,而此时view还没有显示出来,在Activity执行了onResume方法后,界面才显示完成,这说明了view的显示经历了Activity的这两个过程。下面是启动Activity与加载view的一个简单过程图。

一、过程分析
下面将会以ActivityThread类为起点,一步步跟踪view的显示过程。
在ActivityThread类中有一个Handler对象,它负责处理服务端传过来的消息。在接收到LAUNCH_ACTIVITY消息后,执行handleLaunchActivity方法,也就是启动一个Activity。
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart")
ActivityClientRecord r = (ActivityClientRecord)msg.obj
r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo)
handleLaunchActivity(r, null)
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER)
} break
在此方法中,首先解析package信息,然后调用handleLaunchActivity方法来执行启动Activity。
我们继续跟踪handleLaunchActivity方法。
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(TAG, "Handling launch of " + r);
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
if (!r.activity.mFinished && r.startsNotResumed) {
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
if (r.isPreHoneycomb()) {
r.state = oldState;
}
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPause()");
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to pause activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
r.paused = true;
}
} else {
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null);
} catch (RemoteException ex) {
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
此方法注释比较多,过程非常简单,教容易理解,主要是执行performLaunchActivity和handleResumeActivity方法,他们分别执行Activity的onCreate,onStart和onResume生命周期。
我们继续跟踪performLaunchActivity方法
Activity activity = null
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader()
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent)
StrictMode.incrementExpectedActivityCount(activity.getClass())
r.intent.setExtrasClassLoader(cl)
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 {
Application app = r.packageInfo.makeApplication(false, mInstrumentation)
if (localLOGV) Slog.v(TAG, "Performing launch of " + r)
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir())
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity)
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager())
Configuration config = new Configuration(mCompatConfiguration)
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config)
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config)
if (customIntent != null) {
activity.mIntent = customIntent
}
r.lastNonConfigurationInstances = null
activity.mStartedActivity = false
int theme = r.activityInfo.getThemeResource()
if (theme != 0) {
activity.setTheme(theme)
}
activity.mCalled = false
mInstrumentation.callActivityOnCreate(activity, r.state)
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()")
}
r.activity = activity
r.stopped = true
if (!r.activity.mFinished) {
activity.performStart()
r.stopped = false
}
if (!r.activity.mFinished) {
if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state)
}
}
if (!r.activity.mFinished) {
activity.mCalled = false
mInstrumentation.callActivityOnPostCreate(activity, r.state)
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()")
}
}
}
r.paused = true
mActivities.put(r.token, r)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
在此方法中,首先调用mInstrumentation.newActivity来创建一个Activity的实例,然后调用makeApplication创建application对象,接着通过activity.attach来调用activity对象的attach方法,然后执行mInstrumentation.callActivityOnCreate,也就是调用activity对象的onCreate方法。在onCreate里我们调用setContentView设置我们预定义好的View,我们便接着跟踪Activity的setContentView方法。
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
方法中getWindow()返回的是一个android.app.Window对象,这个对象就是刚刚在attach方法中赋值的mWindow成员变量,它的类型是PhoneWindow,继续查看PhoneWindow的setContentView方法。
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
PhoneWindow中有两个和视图相关的成员变量,一个是DecorView mDecor,另一个是ViewGroup mContentParent。mDecor是一个FrameLayout,而mContentParent是mDecor中的一个子视图,我们继续跟踪下面的代码便会很清晰了。
继续查看installDecor方法
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
继续查看generateLayout方法的代码
该方法会做如下事情:
根据窗口的风格修饰类型为该窗口选择不同的窗口布局文件(根视图)。这些窗口修饰布局文件指定一个用来存放Activity自定义布局文件的ViewGroup视图,一般为FrameLayout 其id 为: android:id=”@android:id/content”。例如窗口修饰类型包括FullScreen(全屏)、NoTitleBar(不含标题栏)等。选定窗口修饰类型有两种:1、指定requestFeature()指定窗口修饰符,PhoneWindow对象调用getLocalFeature()方法获取值;2、为我们的Activity配置相应属性,即android:theme=“”,PhoneWindow对象调用getWindowStyle()方法。
确定好窗口风格之后,选定该风格对应的布局文件,这些布局文件位于 frameworks/base/core/res/layout/ ,典型的窗口布局文件有:R.layout.dialog_titile_icons 、R.layout.screen_title_icons、R.layout.screen_progress 、R.layout.dialog_custom_title、R.layout.dialog_title 、R.layout.screen_title (最常用的Activity窗口修饰布局文件)、R.layout.screen_simple (全屏的Activity窗口布局文件)。
下面是Activity最常用的一种窗口布局文件,R.layout.screen_title
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
布局中上面部分,id为title的TextView显示窗口的标题,下面部分id为content的FrameLayout是装载上层应用的layout的容器,也就是代码中的mContentParent对象。
在这儿小结一下Activity的层级结构
1.一个Activity对应着一个PhoneWindow对象,是一对一的关系,如果从Activity A启动到Activity B,那么Activity B会创建一个自己的PhoneWindow对象。
2.PhoneWindow管理着整个屏幕的内容,不包括屏幕最顶部的系统状态条。所以,PhoneWindow或者Window是与应用的一个页面相关联。
3.PhoneWindow同时管理着ActionBar和下面的内容主题,setContentView()方法是用来设置内容主体的,而setTitle()等其他方法就是操作ActionBar的,Window中定义的requestFeature()等方法,有很多与ActionBar属性相关的设置。
4.PhoneWindow自己并不是一个视图(View),它的成员变量mDecor才是整个界面的视图,mDecor是在generateLayout()的时候被填充出来的,而actionBar和contentParent两个视图都是通过findViewById()直接从mDecor中获取出来的。
第一部分performLaunchActivity就执行完成了,下面继续执行android.app.ActivityThread.handleResumeActivity方法。在此方法中,首先调用performResumeActivity执行Activity的onResume方法。下面接着执行这段代码非常关键
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)
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set")
r.hideForNow = true
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
其中wm是a.getWindowManager();获取到的,a是Activity,getWindowManager()返回它的mWindowManager对象,而这个对象是WindowManagerImpl类型,它的内部方法大部分是代理的WindowManagerGlobal。通过跟踪wm.addView(decor, l)这句代码,下面我们继续查看WindowManagerGlobal类中的addView方法。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
其中root从名称可以看出,它是根节点,它是ViewRootImpl类型,也就是说把传进来的DecorView设置给根节点,继续查看ViewRootImpl的setView方法。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
...
view.assignParent(this);
...
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
首先将传进来的参数view赋值给mView,这里要说明一下,ViewRootImpl其实并不是一个View的子类,mView将是这个对象所认识的root节点,也是整个Activity的root节点。现在整个view的树形结构中有了根节点,也就是ViewRootImpl,那么requestLayout()就有效了,就可以进行后面的measure,layout,draw三步操作了。
下面继续查看requestLayout方法
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
首先检查了是否在主线程,然后就执行scheduleTraversals()方法。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
scheduleConsumeBatchedInput();
}
}
这里主要是post一个mTraversalRunnable成员变量。
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
不多说了,接着执行doTraversal()方法,在doTraversal方法中只是简单的调用performTraversals方法,这个方法非常长,可以研究一下源码,依次调用了performMeasure(),performLayout(),performDraw()三个方法,终于开始了控件层的测量,布局,绘制三个步骤。
完成draw后整个view就显示出来了,在View的onDraw里实际操作的是Canvas对象,那么Canvas对象是哪儿来的?它是怎么通知SurfaceFlinger来显示图形的呢? 下面将介绍surface与surfaceFlinger。
二、Surface与SurfaceFlinger
1、什么是Surface、SurfaceFlinger?
Surface:surface相当于一个容器,存放需要显示的内容。surface是双缓冲,一个buffer用于绘制,一个buffer用于显示。
SurfaceFlinger:SurfaceFlinger是一个系统服务,主要是负责合成各窗口的Surface,然后通过OpenGLES显示到硬件帧缓冲区上。
2、为什么需要SurfaceFlinger?
系统的硬件帧缓冲区一般只有一个,并且不应该是随便访问的,需要一个服务来统一管理访问它,surfaceFlinger就是这个角色。
3、canvas如何和surface关联起来的?
我们查看ViewRootImpl类的源码
private final Surface mSurface = new Surface();
Canvas canvas;
canvas = mSurface.lockCanvas(dirty);
....
mView.draw(canvas);
4、surface如何和surfaceFlinger联系起来的?
我们首先看一下ViewRootImpl的构造函数
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
...
其中,getWindowsession将建立和WindowManagerService的关系,我们简单看一下getWindowSession方法的源码
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
imm.getClient(), imm.getInputContext());
float animatorScale = windowManager.getAnimationScale(2);
ValueAnimator.setDurationScale(animatorScale);
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
我们接着上面的流程中doTraversal之后执行performTraversals方法
private void performTraversals() {
final View host = mView;
boolean initialized = false;
boolean contentInsetsChanged = false;
boolean visibleInsetsChanged;
try {
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
ViewRoot也有一个Surface成员变量,叫mSurface,这个就是代表SurfaceFlinger的客户端。ViewRoot在这个Surface上作画,最后将由SurfaceFlinger来合成显示。
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
int relayoutResult = sWindowSession.relayout(
mWindow, params,
(int) (mView.mMeasuredWidth * appScale + 0.5f),
(int) (mView.mMeasuredHeight * appScale + 0.5f),
viewVisibility, insetsPending, mWinFrame,
mPendingContentInsets, mPendingVisibleInsets,
mPendingConfiguration, mSurface);
}
下面就进入到服务端查看WindowManagerService的relayoutWindow方法。
public int relayoutWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending,
Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
Configuration outConfig, Surface outSurface){
.....
try {
Surface surface = win.createSurfaceLocked();
if (surface != null) {
outSurface.copyFrom(surface);
win.mReportDestroySurface = false;
win.mSurfacePendingDestroy = false;
} else {
outSurface.release();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
客户端的surface终于和服务端分配的surface联系起来了,接下来就是view使用surface的mCanvas绘制了。
好了,view在上层的显示过程就串联起来了,其余的工作就交给底层的Display系统展示出来即可。