Chinaunix首页 | 论坛 | 博客
  • 博客访问: 432255
  • 博文数量: 113
  • 博客积分: 2228
  • 博客等级: 大尉
  • 技术积分: 1341
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-24 15:07
文章分类

全部博文(113)

文章存档

2013年(52)

2012年(61)

我的朋友

分类: 嵌入式

2012-11-05 10:33:00

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 将创建 mDecor 和 mContentParent 。 mDecor 是 DecorView 类型,
mContentParent 是 ViewGroup 类型
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 OutOfResource***ception {
mCanvas = new CompatibleCanvas();
init(s,pid,name,display,w,h,format,flags); ----> 调用了 native 的 init 函数。
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 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 surface;
if (jname == NULL) {
//client 是 SurfaceComposerClient ,返回的 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& 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& surface = getSurfaceControl(env, clazz);
const sp& rhs = getSurfaceControl(env, other);
if (!SurfaceControl::isSameSurface(surface, rhs)) {
setSurfaceControl(env, clazz, rhs);
// 把本地那个 surface 的 surfaceControl 对象转移到 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/java d:/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);
//_arg10 是 copyFrom 了,那怎么传到客户端呢?
if ((_arg10!=null)) {
reply.writeInt(1);// 调用 Surface 的 writeToParcel ,把信息加入 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& 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& control(getSurface(env, clazz));
// 不过我们看到希望了,根据服务端那边 Parcel 信息来构造一个新的 surface
sp 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 这块代码基本重写一遍,希望能够简单精炼点。

原文:

阅读(1245) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~