Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 深入淺出-詳解Android Surface系統

深入淺出-詳解Android Surface系統

編輯:關於Android編程

一 目的   本篇文章的目的就是為了講清楚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這塊代碼基本重寫一遍,希望能夠簡單精煉點。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved