天天看點

Android Application與SurfaceFlinger連接配接過程分析

     看了羅老師的的部落格很久了,特别羨慕,寫的非常認真而且詳細,是以呢,也經常向大家推薦,各位卓友如果有時間,請多看一下,對自己從整體上掌握Android Framework架構非常有益。自己的學習在同時,也跟着源碼分析,得出自己的結論,隻是單純的看羅老師的部落格,看完還是一頭霧水,隻有自己完整完整走一遍,才會有一定的掌握。

     Android當中的GUI繪圖應該算是最重要的一塊了,因為所有的應用都最直接和使用者接觸,當然這塊也是特别複雜的。自己看了好久,才有一點脈絡,記下來,怕以後忘掉了。

     從羅老師的部落格當中,可以了解到,Application都會與SurfaceFlinger連接配接,并得到一個匿名的binder對象,以便能夠繪制自己的UI,并在繪制完成後,請求SurfaceFlinger對自己的UI資料進行渲染。一個Application在SurfaceFlinger端對應有一個Client的對象,對應的代碼如下:

sp
    
      SurfaceFlinger::createConnection()
{
    sp
     
       bclient;
    sp
      
        client(new Client(this));
    status_t err = client->initCheck();
    if (err == NO_ERROR) {
        bclient = client;
    }
    return bclient;
}
      
     
    
           

     到這裡,Application就取得與SurfaceFlinger的正常連接配接了,好了,那我們的目标現在就非常清晰了,Application是怎麼執行到這裡的呢?我們還是從ViewRootImpl類來看起,Application中的每個界面對應一個Activity,而每個Activity都對應一個Window、一個ViewRootImpl、一個WindowState、一個Surface,這些如果有疑問的,可以去看羅老師的部落格。ViewRootImpl的建立是在WindowManagerGlobal當中的,代碼如下:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }
           

     這裡建立了ViewRootImpl對象,在ViewRootImpl的構造方法當中,對類成員變量進行初始化,代碼如下:

public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        mBasePackageName = context.getBasePackageName();

        mDisplayAdjustments = display.getDisplayAdjustments();

        mThread = Thread.currentThread();
        mLocation = new WindowLeaked(null);
        mLocation.fillInStackTrace();
        mWidth = -1;
        mHeight = -1;
        mDirty = new Rect();
        mTempRect = new Rect();
        mVisRect = new Rect();
        mWinFrame = new Rect();
        mWindow = new W(this);
        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
        mViewVisibility = View.GONE;
        mTransparentRegion = new Region();
        mPreviousTransparentRegion = new Region();
        // [+LEUI-9331]
        mPreBlurParams = new BlurParams();
        // [-LEUI-9331]
        mFirst = true; // true for the first time the view is added
        mAdded = false;
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
        mAccessibilityManager = AccessibilityManager.getInstance(context);
        mAccessibilityInteractionConnectionManager =
            new AccessibilityInteractionConnectionManager();
        mAccessibilityManager.addAccessibilityStateChangeListener(
                mAccessibilityInteractionConnectionManager);
        mHighContrastTextManager = new HighContrastTextManager();
        mAccessibilityManager.addHighTextContrastStateChangeListener(
                mHighContrastTextManager);
        mViewConfiguration = ViewConfiguration.get(context);
        mDensity = context.getResources().getDisplayMetrics().densityDpi;
        mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
        mFallbackEventHandler = new PhoneFallbackEventHandler(context);
        mChoreographer = Choreographer.getInstance();
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        loadSystemProperties();

        /**
         * M: increase instance count and check log property to determine
         * whether to enable/disable log system. @{
         */
        mIdent = sIdent++;
        checkViewRootImplLogProperty();
        if (LOCAL_LOGV) {
            enableLog(true, "a");
        }

        if (DEBUG_LIFECYCLE) {
            Log.v(TAG, "ViewRootImpl construct: context = " + context + ", mThread = " + mThread
                    + ", mChoreographer = " + mChoreographer + ", mTraversalRunnable = "
                    + mTraversalRunnable + ", this = " + this);
        }
    }
           

     其中的第二行mWindowSession非常重要,它是通過WindowManagerGlobal.getWindowSession()指派的,這裡是一個binder調用,最終的實作是在WindowManagerService中的openSession()方法當中,這裡新建立了一個Session對象,并通過binder程序間調用傳回給了Application。

public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    Log.e(TAG, "Failed to open window session", e);
                }
            }
            return sWindowSession;
        }
    }
           

     繼續往下看,當ViewRootImpl對象建立完成,Activity需要繪制自己的UI資料時,通過各種調用,最終都會調用到ViewRootImpl類的performTraversals()方法當中,這個也就是我們通常說的View繪制的标準流程(Measure、Layout、Draw)三步,這個方法代碼特别長,這裡就不貼出來了,我們主要看一下if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null) 這個分支,就是在繪制時,如果是第一次、視窗大小發生變化、狀态欄變化、可見性變化、繪圖參數不為空這幾種情況時,會調用relayoutWindow(params, viewVisibility, insetsPending)方法,在relayoutWindow方法中,會調用最開始建立好的mWindowSession.relayout()方法,mWindowSession我們在前面已經說過了,是在WindowManagerService的openSession()方法當中建立的對象,它對應的類是Session,它的relayoutWindow就是轉調到mService.relayoutWindow()方法當中了,而mService當然就是WindowManagerService了。

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {

        float appScale = mAttachInfo.mApplicationScale;
        boolean restore = false;
        if (params != null && mTranslator != null) {
            restore = true;
            params.backup();
            mTranslator.translateWindowLayout(params);
        }
        if (params != null) {
            if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
        }
        mPendingConfiguration.seq = 0;
        if (DEBUG_LAYOUT) {
            Log.d(TAG, ">>>>>> CALLING relayoutW+ " + mWindow + ", params = " + params
                    + ",viewVisibility = " + viewVisibility + ", insetsPending = " + insetsPending
                    + ", appScale = " + appScale + ", mWinFrame = " + mWinFrame + ", mSeq = "
                    + mSeq + ", mPendingOverscanInsets = " + mPendingOverscanInsets
                    + ", mPendingContentInsets = " + mPendingContentInsets
                    + ", mPendingVisibleInsets = " + mPendingVisibleInsets
                    + ", mPendingStableInsets = " + mPendingStableInsets
                    + ", mPendingOutsets = " + mPendingOutsets
                    + ", mPendingConfiguration = " + mPendingConfiguration + ", mSurface = "
                    + mSurface + ",valid = " + mSurface.isValid() + ", mOrigWindowType = "
                    + mOrigWindowType + ",this = " + this);
        }
        if (params != null && mOrigWindowType != params.type) {
            // For compatibility with old apps, don't crash here.
            if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                Slog.w(TAG, "Window type can not be changed after "
                        + "the window is added; ignoring change of " + mView);
                params.type = mOrigWindowType;
            }
        }
        int relayoutResult = mWindowSession.relayout(
                mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingConfiguration, mSurface);
        if (DEBUG_LAYOUT) {
            Log.d(TAG, "<<<<<< BACK FROM relayoutW- : res = " + relayoutResult + ", mWinFrame = "
                    + mWinFrame + ", mPendingOverscanInsets = " + mPendingOverscanInsets
                    + ", mPendingContentInsets = " + mPendingContentInsets
                    + ", mPendingVisibleInsets = " + mPendingVisibleInsets
                    + ", mPendingStableInsets = " + mPendingStableInsets
                    + ", mPendingOutsets = " + mPendingOutsets
                    + ", mPendingConfiguration = " + mPendingConfiguration + ", mSurface = "
                    + mSurface + ",valid = " + mSurface.isValid() + ",params = " + params
                    + ", this = " + this);
        }
        if (restore) {
            params.restore();
        }

        if (mTranslator != null) {
            mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
            mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
            mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
            mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
            mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
        }
        return relayoutResult;
    }
           

     好,我們進入WindowManagerService.relayoutWindow()方法當中,這裡請一定注意,relayoutWindow方法沒有帶重寫的标志,說明不是從其他類繼續的,而是直接調用的。因代碼較長,這裡也就不貼出來了,其中最關鍵的一句就是SurfaceControl surfaceControl = winAnimator.createSurfaceLocked(),這裡建立好一個SurfaceControl對象,然後就将其拷貝到outSurface當中了。這裡就有點疑問了,如果執行到這步,那Surface對象就已經生成好了,那麼與SurfaceFlinger的連接配接到底是什麼建立的呢?不對了,肯定是中間露掉什麼了,回頭看一下。      從添加視窗重新來,ViewRootImpl調用setView方法,間接調用了mWindowSession.addToDisplay()方法,轉而調用WindowManagerService.addWindow()方法,每個Activity對應的WindowState對象也是在這裡建構的,建立完成後,調用win.attach(),代碼如下:

void attach() {
        if (WindowManagerService.localLOGV) Slog.v(
            TAG, "Attaching " + this + " token=" + mToken
            + ", list=" + mToken.windows);
        mSession.windowAddedLocked();
    }
           

     其中的mSession對象就是WindowManagerService為每個ViewRootImpl建構的Session,在Session類的windowAddedLocked方法當中又建構了一個SurfaceSession,好像模糊的看到目标了,跟Surface有關系了,我們繼續往下看,SurfaceSession的構造方法直接調用jni的nativeCreate,實作在android_view_SurfaceSession.cpp類中:

static jlong nativeCreate(JNIEnv* env, jclass clazz) {
    SurfaceComposerClient* client = new SurfaceComposerClient();
    client->incStrong((void*)nativeCreate);
    return reinterpret_cast
     
      (client);
}
     
           

     看到這裡就很明顯了,直接在native層建立了一個SurfaceComposerClient指針,而SurfaceComposerClient建立後,在首次調用時,會執行onFirstRef的方法,就會與SurfaceFlinger取得連接配接,然後調用到最開始我們看的new Client當中,這樣Application就與SurfaceFlinger取得連接配接了。      中間寫的思路開始不清晰,走錯了,後面回頭再看一次,更加深了印象,寫的不好的地方,請大家見諒,謝謝!