一 目的
本篇文章的目的就是为了讲清楚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类型,ContentParent是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的关系。
- public 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); ---->调用了native的init函数。
- mName = name;
- }
到这里,不进入JNI是不可能说清楚了。不过我们要先回顾下之前的关键步骤。
◆ add中,new了一个SurfaceSession
◆创建new了一个Surface
◆调用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的帮助,我无论如何也不会知道这其中的奥妙。
那好,我们的流程明白了。
◆客户端虽然传了一个surface,但其实没传递给服务端
◆服务端调用writeToParcel,把信息写到Parcel中,然后数据传回客户端
◆客户端调用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这块代码基本重写一遍,希望能够简单精炼点。