Android 中Activity,Window和View之間的關係

我想大多數人,對於這3個東西的概念能區分,但是具體區別在哪卻很難說出來。
我這裏根據我個人的理解來講講我個人對這3個概念的理解。當然這裏設計到通用的事件窗口模型等通用GUI設計,我這裏就不打算講了,純粹從概念上來進行區分。
Activity是Android應用程序的載體,允許用戶在其上創建一個用戶界面,並提供用戶處理事件的API,如onKeyEvent, onTouchEvent等。 並維護應用程序的生命週期(由於android應用程序的運行環境和其他操作系統不同,android的應用程序是運行在框架之內,所以他的應用程序不能噹噹從進程的級別去考慮,而更多是從概念上去考慮。android應用程序是由多個活動堆積而成,而各個活動又有其獨立的生命週期)。Activity本身是個龐大的載體,可以理解成是應用程序的載體,如果木有Activity,android應用將無法運行。也可以理解成android應用程序的入口。Acivity的實例對象由系統維護。系統服務ActivityManager負責維護Activity的實例對象,並根據運行狀態維護其狀態信息。
但在用戶級別,程序員可能根願意理解成爲一個界面的載體。但僅僅是個載體,它本身並不負責任何繪製。Activity的內部實現,實際上是聚了一個Window對象。Window是一個抽象類,它的具體是在android_src_home/framework/policies/base/phone/com/android/internal/policy/impl目錄下的PhoneWindow.java。
當我們調用Acitivity的 setContentView方法的時候實際上是調用的Window對象的setContentView方法,所以我們可以看出Activity中關於界面的繪製實際上全是交給Window對象來做的。繪製類圖的話,可以看出Activity聚合了一個Window對象。
下面是PhoneWindow中的setContentView方法的實現:

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mContentParent.addView(view, params);
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }
    }
Window內部首先判斷mContentParent是否爲空,然後調用installDecor方法(安裝裝飾器),我們看看這個方法如何實現的   
  private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setIsRootNamespace(true);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
            if (mTitleView != null) {
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                    View titleContainer = findViewById(com.android.internal.R.id.title_container);
                    if (titleContainer != null) {
                        titleContainer.setVisibility(View.GONE);
                    } else {
                        mTitleView.setVisibility(View.GONE);
                    }
                    if (mContentParent instanceof FrameLayout) {
                        ((FrameLayout)mContentParent).setForeground(null);
                    }
                } else {
                    mTitleView.setText(mTitle);
                }
            }
        }
    }
在該方法中,首先創建一個DecorView,DecorView是一個擴張FrameLayout的類,是所有窗口的根View。我們在Activity中調用的setConctentView就是放到DecorView中了。這是我們類圖的聚合關係如下:
Activity--->Window--->DecorView
這是我們得出這3個類之間最直接的一個關係。
我們詳細分析一下,類對象是如何被創建的。
先不考慮Activity的創建(因爲 Acitivity的實例由ActivityManager維護,是在另一個進程設計到IPC的通信,後面會講到),而考慮Window和View的創建。
Activity被創建後,系統會調用它的attach方法來將Activity添加到ActivityThread當中。我們找到Activity的attach方法如下:


    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            Object lastNonConfigurationInstance,
            HashMap<String,Object> lastNonConfigurationChildInstances,
            Configuration config) {
        attachBaseContext(context);
      
mWindow= PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        mUiThread = Thread.currentThread();
        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstance = lastNonConfigurationInstance;
        mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;
        mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }
我們看紅色的代碼部分,就是創建Window對象的代碼。感興趣的同學可以跟蹤去看看具體是如何創建的。其實很簡單,其內部實現調用了Policy對象的makeNewWindow方法,其方法直接new了一個PhoneWindow對象如下:
public PhoneWindow makeNewWindow(Context context) {
        return new PhoneWindow(context);
}
這時我們已經可以把流程串起來,Activity創建後系統會調用其attach方法,將其添加到ActivityThread當中,在attach方法中創建了一個window對象。
下面分析View的創建。我們知道Window聚合了DocerView,當用戶調用setContentView的時候會把一顆View樹仍給DocerView.View樹是已經創建好的實例對象了,所以我們研究的是DocerView是個什麼東西,它是如何被創建的。
我們回頭看看Window實現裏邊的setContentView方法,我們看上面代碼的紅色部分setContentView->
installDecor-> generateDecor.
generateDecor直接new了一個DecorView對象:  
protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
}
我們可以去看看DecorView的實現,它是PhoneWindow的一個內部類。實現很簡單,它默認會包含一個灰色的標題欄,然後在標題欄下邊會包含一個空白區域用來當用戶調用setContentView的時候放置用戶View,並傳遞事件,這裏不做詳細分析,感興趣同學可以自己研究研究。
當DecorView創建好之後再回到Window中的setContentView方法中來,見上面代碼藍色部分,調用
  mContentParent.addView(view, params); 
來將用戶的View樹添加到DecorView中。

到這時爲止,我想我們已經很清晰的認識到它們3者之間的關係,並知道其創建流程。
現在總結一下:
Activity在onCreate之前調用attach方法,在attach方法中會創建window對象。window對象創建時並木有創建Decor對象對象。用戶在Activity中調用setContentView,然後調用window的setContentView,這時會檢查DecorView是否存在,如果不存在則創建DecorView對象,然後把用戶自己的View 添加到DecorView中。

原文鏈接:tech.techweb.com.cn
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章