一 目的 本節(jié)的目的就是為了講清楚Android中的Surface系統(tǒng),大家耳熟能詳?shù)腟urfaceFlinger到底是個(gè)什么東西,它的工作流程又是怎樣的。當(dāng)然,鑒于SurfaceFlinger的復(fù)雜性,我們依然將采用情景分析的辦法,找到合適的切入點(diǎn)。 一個(gè)Activity是怎么在屏幕上顯示出來的呢?我將首先把這個(gè)說清楚。 接著我們把其中的關(guān)鍵調(diào)用抽象在Native層,以這些函數(shù)調(diào)用為切入點(diǎn)來研究SurfaceFlinger。好了,開始我們的征途吧。 二 Activity是如何顯示的 最初的想法就是,Activity獲得一塊顯存,然后在上面繪圖,最后交給設(shè)備去顯示。這個(gè)道理是沒錯(cuò),但是Android的 SurfaceFlinger是在System Server進(jìn)程中創(chuàng)建的,Activity一般另有線程,這之間是如何...如何掛上關(guān)系的呢?我可以先提前告訴大家,這個(gè)過程還比較復(fù)雜。呵呵。 好吧,我們從Activity最初的啟動(dòng)開始。代碼在 framework/base/core/java/android/app/ActivityThread.java中,這里有個(gè)函數(shù)叫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 ldState = r.state; handleResumeActivity(r.token, false, r.isForward); ---->調(diào)用handleResumeActivity } handleLaunchActivity中會(huì)調(diào)用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); //這個(gè)很關(guān)鍵。 } 上面addView那幾行非常關(guān)鍵,它關(guān)系到咱們?cè)贏ctivity中setContentView后,整個(gè)Window到底都包含了些什么。我先 告訴大家。所有你創(chuàng)建的View之上,還有一個(gè)DecorView,這是一個(gè)FrameLayout,另外還有一個(gè)PhoneWindow。上面這些東西 的代碼在 framework/Policies/Base/Phone/com/android/Internal/policy/impl。這些隱藏的View的創(chuàng)建都是由你在Acitivty的onCreate中調(diào)用setContentView導(dǎo)致的。 [---->PhoneWindow:: addContentView] public void addContentView(View view, ViewGroup.LayoutParams params) { if (mContentParent == null) { //剛創(chuàng)建的時(shí)候mContentParent為空 installDecor(); } mContentParent.addView(view, params); final Callback cb = getCallback(); if (cb != null) { cb.onContentChanged(); } } installDecor將創(chuàng)建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創(chuàng)建的時(shí)候會(huì)調(diào)用它的setWindowManager。而這個(gè)函數(shù)由Window類實(shí)現(xiàn)。 代碼在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代碼這個(gè)東西真的很復(fù)雜。mWindowManager的實(shí)現(xiàn)是LocalWindowManager,但由通過Bridge模式把功能交給WindowManagerImpl去實(shí)現(xiàn)了。 真的很復(fù)雜! 好了,羅里羅嗦的,我們回到wm.addView(decor, l)。最終會(huì)由WindowManagerImpl來完成 addView操作,我們直接看它的實(shí)現(xiàn)好了。 代碼在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是整個(gè)顯示系統(tǒng)中最為關(guān)鍵的東西,看起來這個(gè)東西好像和View有那么點(diǎn)關(guān)系,其實(shí)它根本和View等UI關(guān)系不大,它不過是一個(gè)Handler罷了,唯一有關(guān)系的就是它其中有一個(gè)變量為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不過定義了一些接口函數(shù)罷了。 看到Surface直覺上感到它和SurfaceFlinger有點(diǎn)關(guān)系。要不先去看看? Surface代碼在framework/base/core/java/android/view/Surface.java中,我們調(diào)用的是無參構(gòu)造函數(shù)。 public Surface() { mCanvas = new CompatibleCanvas(); //就是創(chuàng)建一個(gè)Canvas! } 如果你有興趣的話,看看Surface其他構(gòu)造函數(shù),最終都會(huì)調(diào)用native的實(shí)現(xiàn),而這些native的實(shí)現(xiàn)將和SurfaceFlinger 建立關(guān)系,但我們這里ViewRoot中的mSurface顯然還沒有到這一步。那它到底是怎么和SurfaceFlinger搞上的呢?這一切待會(huì)就會(huì) 水落石出的。 另外,為什么ViewRoot是主人公呢?因?yàn)閂iewRoot建立了客戶端和SystemServer的關(guān)系。我們看看它的構(gòu)造函數(shù)。 public ViewRoot(Context context) { super(); .... getWindowSession(context.getMainLooper()); } getWindowsession將建立和WindowManagerService的關(guān)系。 ublic static IWindowSession getWindowSession(Looper mainLooper) { synchronized (mStaticInit) { if (!mInitialized) { try { //sWindowSession是通過Binder機(jī)制創(chuàng)建的。終于讓我們看到點(diǎn)希望了 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的進(jìn)程調(diào)用另一端是WindowManagerService,代碼在 framework/base/services/java/com/android/server/WindowManagerService.java中。我們先不說這個(gè)。 回過頭來看看ViewRoot接下來的調(diào)用。 [-->ViewRoot::setView()],這個(gè)函數(shù)很復(fù)雜,我們看其中關(guān)鍵幾句。 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { requestLayout(); try { res = sWindowSession.add(mWindow, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets); } } requestLayout實(shí)現(xiàn)很簡單,就是往handler中發(fā)送了一個(gè)消息。 public void requestLayout() { checkThread(); mLayoutRequested = true; scheduleTraversals(); //發(fā)送DO_TRAVERSAL消息 } public void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; sendEmptyMessage(DO_TRAVERSAL); } } 我們看看跨進(jìn)程的那個(gè)調(diào)用。sWindowSession.add。它的最終實(shí)現(xiàn)在WindowManagerService中。 [--->WindowSession::add()] public int add(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets) { return addWindow(this, window, attrs, viewVisibility, outContentInsets); } WindowSession是個(gè)內(nèi)部類,會(huì)調(diào)用外部類的addWindow 這個(gè)函數(shù)巨復(fù)雜無比,但是我們的核心目標(biāo)是找到創(chuàng)建顯示相關(guān)的部分。所以,最后精簡的話就簡單了。 [--->WindowManagerService:: addWindow] public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, Rect outContentInsets) { //創(chuàng)建一個(gè)WindowState,這個(gè)又是什么玩意兒呢? win = new WindowState(session, client, token, attachedWindow, attrs, viewVisibility); win.attach(); return res; } WindowState類中有一個(gè)和Surface相關(guān)的成員變量,叫SurfaceSession。它會(huì)在 attach函數(shù)中被創(chuàng)建。SurfaceSession嘛,就和SurfaceFlinger有關(guān)系了。我們待會(huì)看。 好,我們知道ViewRoot創(chuàng)建及調(diào)用add后,我們客戶端的View系統(tǒng)就和WindowManagerService建立了牢不可破的關(guān)系。 另外,我們知道ViewRoot是一個(gè)handler,而且剛才我們調(diào)用了requestLayout,所以接下來消息循環(huán)下一個(gè)將調(diào)用的就是ViewRoot的handleMessage。 public void handleMessage(Message msg) { switch (msg.what) { case DO_TRAVERSAL: performTraversals(); performTraversals更加復(fù)雜無比,經(jīng)過我仔細(xì)挑選,目標(biāo)鎖定為下面幾個(gè)函數(shù)。當(dāng)然,后面我們還會(huì)回到performTraversals,不過我們現(xiàn)在更感興趣的是Surface是如何創(chuàng)建的。 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也有一個(gè)Surface成員變量,叫mSurface,這個(gè)就是代表SurfaceFlinger的客戶端 //ViewRoot在這個(gè)Surface上作畫,最后將由SurfaceFlinger來合成顯示。剛才說了mSurface還沒有什么內(nèi)容。 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); [---->ViewRoot:: relayoutWindow()] private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException {
//relayOut是跨進(jìn)程調(diào)用,mSurface做為參數(shù)傳進(jìn)去了,看來離真相越來越近了呀! 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做為參數(shù)傳進(jìn)去了。 } 我們趕緊轉(zhuǎn)到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 { //看到這里,我內(nèi)心一陣狂喜,有戲,太有戲了! //其中win是我們最初創(chuàng)建的WindowState! Surface surface = win.createSurfaceLocked(); if (surface != null) { //先創(chuàng)建一個(gè)本地surface,然后把傳入的參數(shù)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的另外一個(gè)構(gòu)造函數(shù)。 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); ---->調(diào)用了native的init函數(shù)。 mName = name; } 到這里,不進(jìn)入JNI是不可能說清楚了。不過我們要先回顧下之前的關(guān)鍵步驟。 l add中,new了一個(gè)SurfaceSession l 創(chuàng)建new了一個(gè)Surface l 調(diào)用copyFrom,把本地Surface信息傳到outSurface中 JNI層 上面兩個(gè)類的JNI實(shí)現(xiàn)都在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函數(shù)對(duì)應(yīng)為: [--->SurfaceSession_init] static void SurfaceSession_init(JNIEnv* env, jobject clazz) { //SurfaceSession對(duì)應(yīng)為SurfaceComposerClient sp<SurfaceComposerClient> client = new SurfaceComposerClient; client->incStrong(clazz); //Google常用做法,在JAVA對(duì)象中保存C++對(duì)象的指針。 env->SetIntField(clazz, sso.client, (int)client.get()); } Surface的init對(duì)應(yīng)為: [--->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) { //client是SurfaceComposerClient,返回的surface是一個(gè)SurfaceControl //真得很復(fù)雜! 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信息設(shè)置到Surface對(duì)象中 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); //把本地那個(gè)surface的surfaceControl對(duì)象轉(zhuǎn)移到outSurface上 } } 這里僅僅是surfaceControl的轉(zhuǎn)移,但是并沒有看到Surface相關(guān)的信息。 那么Surface在哪里創(chuàng)建的呢?為了解釋這個(gè)問題,我使用了終極武器,aidl。 1 終極武器AIDL aidl可以把XXX.aidl文件轉(zhuǎn)換成對(duì)應(yīng)的java文件。我們剛才調(diào)用的是WindowSession的 relayOut函數(shù)。如下: 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); //注意喔,這個(gè)outSurface前面的是out,表示輸出參數(shù),這個(gè)類似于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調(diào)用只是copyFrom了SurfaceControl對(duì)象到outSurface中,但是沒看到哪里創(chuàng)建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參數(shù)指定include目錄,例如aidl有些參數(shù)是在別的java文件中指定的,那么這個(gè)-I就需要把這些目錄包含進(jìn)來。 先看看ViewRoot這個(gè)客戶端生成的代碼是什么。 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個(gè)參數(shù) 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的信息發(fā)過去。我們趕緊看看服務(wù)端。 服務(wù)端這邊處理是在onTranscat函數(shù)中。 @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信息并沒有傳過來,那么我們?cè)趓elayOut中看到的outSurface是怎么 //出來的呢?看下面這句,原來在服務(wù)端這邊竟然new了一個(gè)新的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);//調(diào)用Surface的writeToParcel,把信息加入reply _arg10.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } return true; } 太詭異了!竟然有這么多花花腸子。我相信如果沒有aidl的幫助,我無論如何也不會(huì)知道這其中的奧妙。 那好,我們的流程明白了。 l 客戶端雖然傳了一個(gè)surface,但其實(shí)沒傳遞給服務(wù)端 l 服務(wù)端調(diào)用writeToParcel,把信息寫到Parcel中,然后數(shù)據(jù)傳回客戶端 l 客戶端調(diào)用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)); //還好,只是把數(shù)據(jù)序列化到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)); //不過我們看到希望了,根據(jù)服務(wù)端那邊Parcel信息來構(gòu)造一個(gè)新的surface sp<Surface> rhs = new Surface(*parcel); if (!Surface::isSameSurface(control, rhs)) { setSurface(env, clazz, rhs); //把這個(gè)新surface賦給客戶端。終于我們有了surface! } } 到此,我們終于七拐八繞的得到了surface,這其中經(jīng)歷太多曲折了。下一節(jié),我們將精簡這其中復(fù)雜的操作,統(tǒng)一歸到Native層,以這樣為切入點(diǎn)來了解Surface的工作流程和原理。 好,反正你知道ViewRoot調(diào)用了relayout后,Surface就真正從WindowManagerService那得到了。繼續(xù)回到ViewRoot,其中還有一個(gè)重要地方是我們知道卻不了解的。 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完后,我們得到了一個(gè)無比寶貴的Surface //那我們畫界面的地方在哪里?就在這個(gè)函數(shù)中,離relayoutWindow不遠(yuǎn)處。 .... boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
if (!cancelDraw && !newSurface) { mFullRedrawNeeded = false; draw(fullRedrawNeeded); //draw?draw什么呀? } [--->ViewRoot::draw()] private void draw(boolean fullRedrawNeeded) { Surface surface = mSurface; //嘿嘿,不擔(dān)心了,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中鎖定一塊區(qū)域,這塊區(qū)域是我們認(rèn)為的需要重繪的區(qū)域 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釋放這塊區(qū)域 surface.unlockCanvasAndPost(canvas); } if (scrolling) { mFullRedrawNeeded = true; scheduleTraversals(); } } 看起來,這個(gè)surface的用法很簡單嘛: l lockSurface,得到一個(gè)畫布Canvas l 調(diào)用View的draw,讓他們?cè)谶@個(gè)Canvas上盡情繪圖才。另外,這個(gè)View會(huì)調(diào)用所有它的子View來畫圖,最終會(huì)進(jìn)入到View的 onDraw函數(shù)中,在這里我們可以做定制化的界面美化工作。當(dāng)然,如果你想定制化整個(gè)系統(tǒng)畫圖的話,完全可以把performTranvsal看懂,然 后再修改。 l unlockCanvasAndPost,告訴Surface釋放這塊畫布 當(dāng)然,這幾個(gè)重要函數(shù)調(diào)用干了具體的活。這些重要函數(shù),我們最終會(huì)精簡到Native層的。 2 總結(jié) 到這里,你應(yīng)該知道了一個(gè)Activity中,調(diào)用setContentView后它如何從系統(tǒng)中獲取一塊Surface,以及它是如何使用這個(gè) Surface的了。不得不說,關(guān)于UI這塊,Android絕對(duì)是夠復(fù)雜的。難怪2.3把UI這塊代碼基本重寫一遍,希望能夠簡單精煉點(diǎn)。 |
|