Android深入浅出之Surface[1] 收藏

转自http://blog.csdn.net/Innost/archive/2011/02/05/6172893.aspx

Android 深入浅出之Surface

目的

本节的目的就是为了讲清楚 Android 中的 Surface 系统,大家耳熟能详的 SurfaceFlinger 到底是个什么东西,它的工作流程又是怎样的。当然,鉴于 SurfaceFlinger 的复杂性,我们依然将采用情景分析的办法,找到合适的切入点。

一个 Activity 是怎么在屏幕上显示出来的呢?我将首先把这个说清楚。

接着我们把其中的关键调用抽象在 Native 层,以这些函数调用为切入点来研究 SurfaceFlinger 。好了,开始我们的征途吧。

Activity 是如何显示的

最初的想法就是, Activity 获得一块显存,然后在上面绘图,最后交给设备去显示。这个道理是没错,但是 Android SurfaceFlinger 是在 System Server 进程中创建的, Activity 一般另有线程,这之间是如何 ... 如何挂上关系的呢?我可以先提前告诉大家,这个过程还比较复杂。呵呵。

好吧,我们从 Activity 最初的启动开始。代码在

framework/base/core/java/android/app/ActivityThread.java 中,这里有个函数叫 handleLaunchActivity

[---->ActivityThread:: handleLaunchActivity()]

private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {

Activity a = performLaunchActivity(r, customIntent);

if (a != null) {

r.createdConfig = new Configuration(mConfiguration);

Bundle oldState = r.state;

handleResumeActivity(r.token, false, r.isForward);

----> 调用handleResumeActivity

}

handleLaunchActivity 中会调用 handleResumeActivity

[--->ActivityThread:: handleResumeActivity]

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {

boolean willBeVisible = !a.mStartedActivity;

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;

if (a.mVisibleFromClient) {

a.mWindowAdded = true;

wm.addView(decor, l); // 这个很关键。

}

上面 addView 那几行非常关键,它关系到咱们在 Activity setContentView 后,整个 Window 到底都包含了些什么。我先告诉大家。所有你创建的 View 之上,还有一个 DecorView ,这是一个 FrameLayout ,另外还有一个 PhoneWindow 。上面这些东西的代码在

framework/Policies/Base/Phone/com/android/Internal/policy/impl 。这些隐藏的 View 的创建都是由你在 Acitivty onCreate 中调用 setContentView 导致的。

[---->PhoneWindow:: addContentView]

public void addContentView(View view, ViewGroup.LayoutParams params) {

if (mContentParent == null) { // 刚创建的时候mContentParent 为空

installDecor();

}

mContentParent.addView(view, params);

final Callback cb = getCallback();

if (cb != null) {

cb.onContentChanged();

}

}

installDecor 将创建mDecormContentParentmDecorDecorView 类型,

mContentParentViewGroup 类型

private void installDecor() {

if (mDecor == null) {

mDecor = generateDecor();

mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

mDecor.setIsRootNamespace(true);

}

if (mContentParent == null) {

mContentParent = generateLayout(mDecor);

那么, ViewManager wm = a.getWindowManager() 又返回什么呢?

PhoneWindow Window 中派生, Acitivity 创建的时候会调用它的 setWindowManager 。而这个函数由 Window 类实现。

代码在 framework/base/core/java/android/view/Window.java

public void setWindowManager(WindowManager wm,IBinder appToken, String appName) {

mAppToken = appToken;

mAppName = appName;

if (wm == null) {

wm = WindowManagerImpl.getDefault();

}

mWindowManager = new LocalWindowManager(wm);

}

你看见没,分析 JAVA 代码这个东西真的很复杂。 mWindowManager 的实现是 LocalWindowManager ,但由通过 Bridge 模式把功能交给 WindowManagerImpl 去实现了。

真的很复杂!

好了,罗里罗嗦的,我们回到 wm.addView(decor, l) 。最终会由 WindowManagerImpl 来完成

addView 操作,我们直接看它的实现好了。

代码在 framework/base/core/java/android/view/WindowManagerImpl.java

[---->addView]

private void addView(View view, ViewGroup.LayoutParams params, boolean nest)

{

ViewRoot root; //ViewRoot ,我们的主人公终于登场!

synchronized (this) {

root = new ViewRoot(view.getContext());

root.mAddNesting = 1;

view.setLayoutParams(wparams);

if (mViews == null) {

index = 1;

mViews = new View[1];

mRoots = new ViewRoot[1];

mParams = new WindowManager.LayoutParams[1];

} else {

}

index--;

mViews[index] = view;

mRoots[index] = root;

mParams[index] = wparams;

}

root.setView(view, wparams, panelParentView);

}

ViewRoot 是整个显示系统中最为关键的东西,看起来这个东西好像和 View 有那么点关系,其实它根本和 View UI 关系不大,它不过是一个 Handler 罢了,唯一有关系的就是它其中有一个变量为 Surface 类型。我们看看它的定义。 ViewRoot 代码在

framework/base/core/java/android/view/ViewRoot.java

public final class ViewRoot extends Handler implements ViewParent,

View.AttachInfo.Callbacks

{

private final Surface mSurface = new Surface();

}

它竟然从handler 派生,而ViewParent 不过定义了一些接口函数罢了。

看到 Surface 直觉上感到它和 SurfaceFlinger 有点关系。要不先去看看?

Surface 代码在 framework/base/core/java/android/view/Surface.java 中,我们调用的是无参构造函数。

public Surface() {

mCanvas = new CompatibleCanvas(); // 就是创建一个Canvas

}

如果你有兴趣的话,看看 Surface 其他构造函数,最终都会调用 native 的实现,而这些 native 的实现将和 SurfaceFlinger 建立关系,但我们这里 ViewRoot 中的 mSurface 显然还没有到这一步。那它到底是怎么和 SurfaceFlinger 搞上的呢?这一切待会就会水落石出的。

另外,为什么 ViewRoot 是主人公呢?因为 ViewRoot 建立了客户端和 SystemServer 的关系。我们看看它的构造函数。

public ViewRoot(Context context) {

super();

....

getWindowSession(context.getMainLooper());

}

getWindowsession 将建立和WindowManagerService 的关系。

ublic static IWindowSession getWindowSession(Looper mainLooper) {

synchronized (mStaticInit) {

if (!mInitialized) {

try {

//sWindowSession 是通过Binder 机制创建的。终于让我们看到点希望了

InputMethodManager imm = InputMethodManager.getInstance(mainLooper);

sWindowSession = IWindowManager.Stub.asInterface(

ServiceManager.getService("window"))

.openSession(imm.getClient(), imm.getInputContext());

mInitialized = true;

} catch (RemoteException e) {

}

}

return sWindowSession;

}

}

上面跨 Binder 的进程调用另一端是 WindowManagerService ,代码在

framework/base/services/java/com/android/server/WindowManagerService.java 中。我们先不说这个。

回过头来看看 ViewRoot 接下来的调用。

[-->ViewRoot::setView()] ,这个函数很复杂,我们看其中关键几句。

public void setView(View view, WindowManager.LayoutParams attrs,

View panelParentView) {

synchronized (this) {

requestLayout();

try {

res = sWindowSession.add(mWindow, mWindowAttributes,

getHostVisibility(), mAttachInfo.mContentInsets);

}

}

requestLayout 实现很简单,就是往 handler 中发送了一个消息。

public void requestLayout() {

checkThread();

mLayoutRequested = true;

scheduleTraversals(); // 发送DO_TRAVERSAL 消息

}

public void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

sendEmptyMessage(DO_TRAVERSAL);

}

}

我们看看跨进程的那个调用。 sWindowSession.add 。它的最终实现在 WindowManagerService 中。

[--->WindowSession::add()]

public int add(IWindow window, WindowManager.LayoutParams attrs,

int viewVisibility, Rect outContentInsets) {

return addWindow(this, window, attrs, viewVisibility, outContentInsets);

}

WindowSession 是个内部类,会调用外部类的 addWindow

这个函数巨复杂无比,但是我们的核心目标是找到创建显示相关的部分。所以,最后精简的话就简单了。

[--->WindowManagerService:: addWindow]

public int addWindow(Session session, IWindow client,

WindowManager.LayoutParams attrs, int viewVisibility,

Rect outContentInsets) {

// 创建一个WindowState ,这个又是什么玩意儿呢?

win = new WindowState(session, client, token,

attachedWindow, attrs, viewVisibility);

win.attach();

return res;

}

WindowState 类中有一个和 Surface 相关的成员变量,叫 SurfaceSession 。它会在

attach 函数中被创建。 SurfaceSession 嘛,就和 SurfaceFlinger 有关系了。我们待会看。

好,我们知道 ViewRoot 创建及调用 add 后,我们客户端的 View 系统就和 WindowManagerService 建立了牢不可破的关系。

另外,我们知道 ViewRoot 是一个 handler ,而且刚才我们调用了 requestLayout ,所以接下来消息循环下一个将调用的就是 ViewRoot handleMessage

public void handleMessage(Message msg) {

switch (msg.what) {

case DO_TRAVERSAL:

performTraversals();

performTraversals 更加复杂无比,经过我仔细挑选,目标锁定为下面几个函数。当然,后面我们还会回到 performTraversals ,不过我们现在更感兴趣的是 Surface 是如何创建的。

private void performTraversals() {

// cache mView since it is used so much below...

final View host = mView;

boolean initialized = false;

boolean contentInsetsChanged = false;

boolean visibleInsetsChanged;

try {

//ViewRoot 也有一个Surface 成员变量,叫mSurface ,这个就是代表SurfaceFlinger 的客户端

//ViewRoot 在这个Surface 上作画,最后将由SurfaceFlinger 来合成显示。刚才说了mSurface 还没有什么内容。

relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

[---->ViewRoot:: relayoutWindow()]

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,

boolean insetsPending) throws RemoteException {

//relayOut 是跨进程调用,mSurface 做为参数传进去了,看来离真相越来越近了呀!

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); mSurface 做为参数传进去了。

}

我们赶紧转到 WindowManagerService 去看看吧。、

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 {

// 看到这里,我内心一阵狂喜,有戏,太有戏了!

// 其中win 是我们最初创建的WindowState

Surface surface = win.createSurfaceLocked();

if (surface != null) {

// 先创建一个本地surface ,然后把传入的参数outSurface copyFrom 一下

outSurface.copyFrom(surface);

win.mReportDestroySurface = false;

win.mSurfacePendingDestroy = false;

} else {

outSurface.release();

}

}

}

[--->WindowState::createSurfaceLocked]

Surface createSurfaceLocked() {

try {

mSurface = new Surface(

mSession.mSurfaceSession, mSession.mPid,

mAttrs.getTitle().toString(),

0, w, h, mAttrs.format, flags);

}

Surface.openTransaction();

这里使用了 Surface 的另外一个构造函数。

public Surface(SurfaceSession s,

int pid, String name, int display, int w, int h, int format, int flags)

throws OutOfResourcesException {

mCanvas = new CompatibleCanvas();

init(s,pid,name,display,w,h,format,flags); ----> 调用了nativeinit 函数。

mName = name;

}

到这里,不进入 JNI 是不可能说清楚了。不过我们要先回顾下之前的关键步骤。

l add 中, new 了一个 SurfaceSession

l 创建 new 了一个 Surface

l 调用 copyFrom ,把本地 Surface 信息传到 outSurface

JNI

上面两个类的 JNI 实现都在 framework/base/core/jni/android_view_Surface.cpp 中。

[---->SurfaceSession:: SurfaceSession()]

public class SurfaceSession {

/** Create a new connection with the surface flinger. */

public SurfaceSession() {

init();

}

它的 init 函数对应为:

[--->SurfaceSession_init]

static void SurfaceSession_init(JNIEnv* env, jobject clazz)

{

//SurfaceSession 对应为SurfaceComposerClient

sp<SurfaceComposerClient> client = new SurfaceComposerClient;

client->incStrong(clazz);

//Google 常用做法,在JAVA 对象中保存C++ 对象的指针。

env->SetIntField(clazz, sso.client, (int)client.get());

}

Surface init 对应为:

[--->Surface_init]

static void Surface_init(

JNIEnv* env, jobject clazz,

jobject session,

jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)

{

SurfaceComposerClient* client =

(SurfaceComposerClient*)env->GetIntField(session, sso.client);

sp<SurfaceControl> surface;

if (jname == NULL) {

//clientSurfaceComposerClient ,返回的surface 是一个SurfaceControl

// 真得很复杂!

surface = client->createSurface(pid, dpy, w, h, format, flags);

} else {

const jchar* str = env->GetStringCritical(jname, 0);

const String8 name(str, env->GetStringLength(jname));

env->ReleaseStringCritical(jname, str);

surface = client->createSurface(pid, name, dpy, w, h, format, flags);

}

//surfaceControl 信息设置到Surface 对象中

setSurfaceControl(env, clazz, surface);

}

static void setSurfaceControl(JNIEnv* env, jobject clazz,

const sp<SurfaceControl>& surface)

{

SurfaceControl* const p =

(SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);

if (surface.get()) {

surface->incStrong(clazz);

}

if (p) {

p->decStrong(clazz);

}

env->SetIntField(clazz, so.surfaceControl, (int)surface.get());

}

[--->Surface_copyFrom]

static void Surface_copyFrom(

JNIEnv* env, jobject clazz, jobject other)

{

const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);

const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);

if (!SurfaceControl::isSameSurface(surface, rhs)) {

setSurfaceControl(env, clazz, rhs);

// 把本地那个surfacesurfaceControl 对象转移到outSurface

}

}

这里仅仅是 surfaceControl 的转移,但是并没有看到 Surface 相关的信息。

那么 Surface 在哪里创建的呢?为了解释这个问题,我使用了终极武器, aidl

1 终极武器 AIDL

aidl 可以把 XXX.aidl 文件转换成对应的 java 文件。我们刚才调用的是 WindowSession

relayOut 函数。如下:

sWindowSession.relayout(

mWindow, params,

(int) (mView.mMeasuredWidth * appScale + 0.5f),

(int) (mView.mMeasuredHeight * appScale + 0.5f),

viewVisibility, insetsPending, mWinFrame,

mPendingContentInsets, mPendingVisibleInsets,

mPendingConfiguration, mSurface);

它的 aidl 文件在 framework/base/core/java/android/view/IWindowSession.aidl

interface IWindowSession {

int add(IWindow window, in WindowManager.LayoutParams attrs,

in int viewVisibility, out Rect outContentInsets);

void remove(IWindow window);

// 注意喔,这个outSurface 前面的是out ,表示输出参数,这个类似于C++ 的引用。

int relayout(IWindow window, in WindowManager.LayoutParams attrs,

int requestedWidth, int requestedHeight, int viewVisibility,

boolean insetsPending, out Rect outFrame, out Rect outContentInsets,

out Rect outVisibleInsets, out Configuration outConfig,

out Surface outSurface);

刚才说了, JNI 及其 JAVA 调用只是 copyFrom SurfaceControl 对象到 outSurface 中,但是没看到哪里创建 Surface 。这其中的奥秘就在 aidl 文件编译后生成的 java 文件中。

你在命令行下可以输入:

aidl-Id:/android-2.2-froyo-20100625-source/source/frameworks/base/core/java/-Id:/android-2.2-froyo-20100625-source/source/frameworks/base/Graphics/javad:/android-2.2-froyo-20100625-source/source/frameworks/base/core/java/android/view/IWindowSession.aidl test.java

以生成 test.java 文件。 -I 参数指定 include 目录,例如 aidl 有些参数是在别的 java 文件中指定的,那么这个 -I 就需要把这些目录包含进来。

先看看 ViewRoot 这个客户端生成的代码是什么。

public int relayout(

android.view.IWindow window,

android.view.WindowManager.LayoutParams attrs,

int requestedWidth, int requestedHeight,

int viewVisibility, boolean insetsPending,

android.graphics.Rect outFrame,

android.graphics.Rect outContentInsets,

android.graphics.Rect outVisibleInsets,

android.content.res.Configuration outConfig,

android.view.Surface outSurface) ---->outSurface 是第11 个参数

throws android.os.RemoteException

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

int _result;

try {

_data.writeInterfaceToken(DESCRIPTOR);

_data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));

if ((attrs!=null)) {

_data.writeInt(1);

attrs.writeToParcel(_data, 0);

}

else {

_data.writeInt(0);

}

_data.writeInt(requestedWidth);

_data.writeInt(requestedHeight);

_data.writeInt(viewVisibility);

_data.writeInt(((insetsPending)?(1):(0)));

// 奇怪,outSurface 的信息没有写到_data 中。那.....

mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);

_reply.readException();

_result = _reply.readInt();

if ((0!=_reply.readInt())) {

outFrame.readFromParcel(_reply);

}

....

if ((0!=_reply.readInt())) {

outSurface.readFromParcel(_reply); //Parcel 中读取信息来填充outSurface

}

}

finally {

_reply.recycle();

_data.recycle();

}

return _result;

}

真奇怪啊, Binder 客户端这头竟然没有把 outSurface 的信息发过去。我们赶紧看看服务端。

服务端这边处理是在 onTranscat 函数中。

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException

{

switch (code)

{

case TRANSACTION_relayout:

{

data.enforceInterface(DESCRIPTOR);

android.view.IWindow _arg0;

android.view.Surface _arg10;

// 刚才说了,Surface 信息并没有传过来,那么我们在relayOut 中看到的outSurface 是怎么

// 出来的呢?看下面这句,原来在服务端这边竟然new 了一个新的Surface !!!

_arg10 = new android.view.Surface();

int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);

reply.writeNoException();

reply.writeInt(_result);

//_arg10copyFrom 了,那怎么传到客户端呢?

if ((_arg10!=null)) {

reply.writeInt(1);// 调用SurfacewriteToParcel ,把信息加入reply

_arg10.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

}

return true;

}

太诡异了!竟然有这么多花花肠子。我相信如果没有 aidl 的帮助,我无论如何也不会知道这其中的奥妙。

那好,我们的流程明白了。

l 客户端虽然传了一个 surface ,但其实没传递给服务端

l 服务端调用 writeToParcel ,把信息写到 Parcel 中,然后数据传回客户端

l 客户端调用 Surface readFromParcel ,获得 surface 信息。

那就去看看 writeToParcel 吧。

[---->Surface_writeToParcel]

static void Surface_writeToParcel(

JNIEnv* env, jobject clazz, jobject argParcel, jint flags)

{

Parcel* parcel = (Parcel*)env->GetIntField(

argParcel, no.native_parcel);

const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));

// 还好,只是把数据序列化到Parcel

SurfaceControl::writeSurfaceToParcel(control, parcel);

if (flags & PARCELABLE_WRITE_RETURN_VALUE) {

setSurfaceControl(env, clazz, 0);

}

}

那看看客户端的 Surface_readFromParcel 吧。

[----->Surface_readFromParcel]

static void Surface_readFromParcel(

JNIEnv* env, jobject clazz, jobject argParcel)

{

Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);

// 客户端这边还没有surface

const sp<Surface>& control(getSurface(env, clazz));

// 不过我们看到希望了,根据服务端那边Parcel 信息来构造一个新的surface

sp<Surface> rhs = new Surface(*parcel);

if (!Surface::isSameSurface(control, rhs)) {

setSurface(env, clazz, rhs); // 把这个新surface 赋给客户端。终于我们有了surface

}

}

到此,我们终于七拐八绕的得到了 surface ,这其中经历太多曲折了。下一节,我们将精简这其中复杂的操作,统一归到 Native 层,以这样为切入点来了解 Surface 的工作流程和原理。

好,反正你知道 ViewRoot 调用了 relayout 后, Surface 就真正从 WindowManagerService 那得到了。继续回到 ViewRoot ,其中还有一个重要地方是我们知道却不了解的。

private void performTraversals() {

// cache mView since it is used so much below...

final View host = mView;

boolean initialized = false;

boolean contentInsetsChanged = false;

boolean visibleInsetsChanged;

try {

relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

// relayoutWindow 完后,我们得到了一个无比宝贵的Surface

// 那我们画界面的地方在哪里?就在这个函数中,离relayoutWindow 不远处。

....

boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();

if (!cancelDraw && !newSurface) {

mFullRedrawNeeded = false;

draw(fullRedrawNeeded); //draw?draw 什么呀?

}

[--->ViewRoot::draw()]

private void draw(boolean fullRedrawNeeded) {

Surface surface = mSurface; // 嘿嘿,不担心了,surface 资源都齐全了

if (surface == null || !surface.isValid()) {

return;

}

if (mAttachInfo.mViewScrollChanged) {

mAttachInfo.mViewScrollChanged = false;

mAttachInfo.mTreeObserver.dispatchOnScrollChanged();

}

int yoff;

final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();

if (scrolling) {

yoff = mScroller.getCurrY();

} else {

yoff = mScrollY;

}

if (mCurScrollY != yoff) {

mCurScrollY = yoff;

fullRedrawNeeded = true;

}

float appScale = mAttachInfo.mApplicationScale;

boolean scalingRequired = mAttachInfo.mScalingRequired;

Rect dirty = mDirty;

if (mUseGL) { // 我们不用OPENGL

...

}

Canvas canvas;

try {

int left = dirty.left;

int top = dirty.top;

int right = dirty.right;

int bottom = dirty.bottom;

//Surface 中锁定一块区域,这块区域是我们认为的需要重绘的区域

canvas = surface.lockCanvas(dirty);

// TODO: Do this in native

canvas.setDensity(mDensity);

}

try {

if (!dirty.isEmpty() || mIsAnimating) {

long startTime = 0L;

try {

canvas.translate(0, -yoff);

if (mTranslator != null) {

mTranslator.translateCanvas(canvas);

}

canvas.setScreenDensity(scalingRequired

? DisplayMetrics.DENSITY_DEVICE : 0);

//mView 就是之前的decoreView,

mView.draw(canvas);

}

} finally {

// 我们的图画完了,告诉surface 释放这块区域

surface.unlockCanvasAndPost(canvas);

}

if (scrolling) {

mFullRedrawNeeded = true;

scheduleTraversals();

}

}

看起来,这个 surface 的用法很简单嘛:

l lockSurface ,得到一个画布 Canvas

l 调用 View draw ,让他们在这个 Canvas 上尽情绘图才。另外,这个 View 会调用所有它的子 View 来画图,最终会进入到 View onDraw 函数中,在这里我们可以做定制化的界面美化工作。当然,如果你想定制化整个系统画图的话,完全可以把 performTranvsal 看懂,然后再修改。

l unlockCanvasAndPost ,告诉 Surface 释放这块画布

当然,这几个重要函数调用干了具体的活。这些重要函数,我们最终会精简到 Native 层的。

2 总结

到这里,你应该知道了一个 Activity 中,调用 setContentView 后它如何从系统中获取一块 Surface ,以及它是如何使用这个 Surface 的了。不得不说,关于 UI 这块, Android 绝对是够复杂的。难怪 2.3 UI 这块代码基本重写一遍,希望能够简单精炼点。

更多相关文章

  1. C语言函数以及函数的使用
  2. android客户端与服务端交互的三种方式
  3. Android 开源项目 eoe 社区 Android 客户端
  4. 转:深入解读Linux与Android的相互关系
  5. Android 中Activity,Window和View之间的关系

随机推荐

  1. Android之帧动画与补间动画的使用
  2. android中listview的一些样式设置(关键点
  3. Android中ListView实现子控件点击事件后L
  4. Android:实时改变配置
  5. 关于代码家(干货集中营)共享知识点汇总系
  6. Android(安卓)11:新特性前瞻
  7. [置顶] Android 开发之旅:view的几种布局
  8. 三星首款Android手机上市
  9. Android 在onCreate()方法中获取控件宽高
  10. Android Market 注册成功