1. 程式人生 > >第一章 Activity啟動和檢視

第一章 Activity啟動和檢視

簡介:這一章主要研究Activity—》Window—》DecoView--》ContentView的過程。即初始化Activity介面過程。核心功能是分析setContentView原理。

關鍵問題:

1)Activity與Window對應關係如何?

2)Window是怎麼生成的?有什麼作用?對應的區域是多少?

3)DecoView有什麼作用?對應的區域是多少?

4)ContentView(int resId)是怎麼生成和新增子檢視的?有哪些步驟?

5)LayoutInflater是怎麼生成的?依賴什麼條件嗎?

1.0 介面層級關係

Activity的UI結構。

  • 每個Activity中都包含一個Window物件,通常,Android中的Window是由PhoneWindow實現的。
  • PhoneWindow又將一個DecorView設定為整個視窗的根View(DecorView是一個ViewGroup)。
  • DecorView裡面又有兩個View,一個是用作title或者導航欄的,

另外一個是ID為content的FrameLayout用來裝我們加寫的Xml檔案佈局的View。即setContentView

1.1 Activity初始化

Activity的建立離不開ActivityThread。

ActiveThread是每一個應用程式所在程序的主執行緒。

//ActivityThread有一個ArrayList儲存啟動的 Activity列表。

final

ArrayList<ActivityClientRecord> mRelaunchingActivities         = new ArrayList<ActivityClientRecord>();

1.1.1 ActivityClientRecord的儲存結構。

ActivityClientRecord儲存啟動的Activity相關資訊。

ActivityClientRecord儲存什麼內容呢?有什麼作用?

儲存Activity相關的資訊,包括

static final class ActivityClientRecord {

        IBinder token;  

//Binder物件

        int ident;

        Intent intent;   //Intent物件(意圖)

        String referrer;

        IVoiceInteractor voiceInteractor;

        Bundle state;   //Bundle引數

        PersistableBundle persistentState;

        Activity activity;  //建立的Activity物件

        Window window; //Activity的視窗物件

        Activity parent;  //Activity的父Activity

        String embeddedID;

        Activity.NonConfigurationInstances lastNonConfigurationInstances;

              //Activity執行的狀態資訊

        boolean paused;

        boolean stopped;

        boolean hideForNow;

        Configuration newConfig;

        Configuration createdConfig;

        Configuration overrideConfig;

        // Used for consolidating configs before sending on to Activity.

        private Configuration tmpConfig = new Configuration();

        ActivityClientRecord nextIdle;

        ProfilerInfo profilerInfo;

        ActivityInfo activityInfo;

        CompatibilityInfo compatInfo;

        LoadedApk packageInfo;

        List<ResultInfo> pendingResults;

        List<ReferrerIntent> pendingIntents;

        boolean startsNotResumed;

        boolean isForward;

        int pendingConfigChanges;

        boolean onlyLocalRequest;

        Window mPendingRemoveWindow;

        WindowManager mPendingRemoveWindowManager;

        boolean mPreserveWindow;

        // Set for relaunch requests, indicates the order number of the relaunch operation, so it

        // can be compared with other lifecycle operations.

        int relaunchSeq = 0;

        // Can only be accessed from the UI thread. This represents the latest processed message

        // that is related to lifecycle events/

        int lastProcessedSeq = 0;

……

}

1.1.2 啟動Activity

ActivityThread.java中startActivityNow函式:

public final Activity startActivityNow(Activity parent, String id,

        Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,

        Activity.NonConfigurationInstances lastNonConfigurationInstances) {

        //儲存啟動資訊到ActivityClientRecord物件中

        ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;

            r.ident = 0;

            r.intent = intent;

            r.state = state;

            r.parent = parent;

            r.embeddedID = id;

            r.activityInfo = activityInfo;

            r.lastNonConfigurationInstances = lastNonConfigurationInstances;

        if (localLOGV) {

            ComponentName compname = intent.getComponent();

            String name;

            if (compname != null) {

                name = compname.toShortString();

            } else {

                name = "(Intent " + intent + ").getComponent() returned null";

            }

            Slog.v(TAG, "Performing launch: action=" + intent.getAction()

                    + ", comp=" + name

                    + ", token=" + token);

        }

        //呼叫performLaunchActivity執行啟動一個新的Activity

        return performLaunchActivity(r, null);

    }

通過呼叫performLaunchActivity函式啟動Activity.

現在來看看啟動Activity的操作。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

。。。。。。
//1)採用Instrumentation生成一個新的Activity例項  
    Activity activity = null;

    try {

        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();

        activity = mInstrumentation.newActivity(

                cl, component.getClassName(), r.intent);

        StrictMode.incrementExpectedActivityCount(activity.getClass());

        r.intent.setExtrasClassLoader(cl);

        r.intent.prepareToEnterProcess();

        if (r.state != null) {

            r.state.setClassLoader(cl);

        }

    } catch (Exception e) {

        if (!mInstrumentation.onException(activity, e)) {

            throw new RuntimeException(

                "Unable to instantiate activity " + component

                + ": " + e.toString(), e);

        }

    }



//2Activity執行上下文初始化
//Activity依賴的上下文監測:如果歸屬的Application未生成,則生成和初始化Application物件(這種跨程序呼叫需要)
    try {

        Application app = r.packageInfo.makeApplication(false, mInstrumentation);



        if (localLOGV) Slog.v(TAG, "Performing launch of " + r);

        if (localLOGV) Slog.v(

                TAG, r + ": app=" + app

                + ", appName=" + app.getPackageName()

                + ", pkg=" + r.packageInfo.getPackageName()

                + ", comp=" + r.intent.getComponent().toShortString()

                + ", dir=" + r.packageInfo.getAppDir());



//ActivityBaseContext監測:如果不存在進行建立 
        if (activity != null) {

            Context appContext = createBaseContextForActivity(r, activity);

            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());

            Configuration config = new Configuration(mCompatConfiguration);

            if (r.overrideConfig != null) {

                config.updateFrom(r.overrideConfig);

            }

            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "

                    + r.activityInfo.name + " with config " + config);

            Window window = null;

            if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {

                window = r.mPendingRemoveWindow;

                r.mPendingRemoveWindow = null;

                r.mPendingRemoveWindowManager = null;

            }
      //3)將Activity物件關聯BaseContext。建立PhoneWindow物件。

            activity.attach(appContext, this, getInstrumentation(), r.token,

                    r.ident, app, r.intent, r.activityInfo, title, r.parent,

                    r.embeddedID, r.lastNonConfigurationInstances, config,

                    r.referrer, r.voiceInteractor, window);



            if (customIntent != null) {

                activity.mIntent = customIntent;

            }

            r.lastNonConfigurationInstances = null;

            activity.mStartedActivity = false;

            int theme = r.activityInfo.getThemeResource();

            if (theme != 0) {

                activity.setTheme(theme);

            }

//4)回撥ActivityonCreate生命週期
activity.mCalled = false;

    if (r.isPersistable()) {

        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);

    } else {

        mInstrumentation.callActivityOnCreate(activity, r.state);

    }

    if (!activity.mCalled) {

        throw new SuperNotCalledException(

            "Activity " + r.intent.getComponent().toShortString() +

            " did not call through to super.onCreate()");

    }

    r.activity = activity;

    r.stopped = true;

    if (!r.activity.mFinished) {

        activity.performStart();

        r.stopped = false;

    }
//回撥onRestoreInstanceState處理,讀取快取的資料

    if (!r.activity.mFinished) {

        if (r.isPersistable()) {

            if (r.state != null || r.persistentState != null) {

                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,

                        r.persistentState);

            }

        } else if (r.state != null) {

            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);

        }

    }
    //5)呼叫Activity onCreate後續處理?到底是什麼處理呢?標題欄的處理,設定標題和顏色。

    if (!r.activity.mFinished) {

        activity.mCalled = false;

        if (r.isPersistable()) {

            mInstrumentation.callActivityOnPostCreate(activity, r.state,

                    r.persistentState);

        } else {

            mInstrumentation.callActivityOnPostCreate(activity, r.state);

        }

        if (!activity.mCalled) {

            throw new SuperNotCalledException(

                "Activity " + r.intent.getComponent().toShortString() +

                " did not call through to super.onPostCreate()");

        }

    }

}

r.paused = true;
       //6)儲存新建立的ActivityClientRecord

        mActivities.put(r.token, r);



    } catch (SuperNotCalledException e) {

        throw e;



    } catch (Exception e) {

        if (!mInstrumentation.onException(activity, e)) {

            throw new RuntimeException(

                "Unable to start activity " + component

                + ": " + e.toString(), e);

        }

    }

    return activity;

}

關鍵在於呼叫activity.attach處理。

1.2 PhoneWindow的初始化

Activity.java

我們來看看attach處理。

1.2.1 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,

        NonConfigurationInstances lastNonConfigurationInstances,

        Configuration config, String referrer, IVoiceInteractor voiceInteractor,

        Window window) {
     //1)關聯BaseContext
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);

  //2)新建PhoneWindow.PhoneWindowActivity相關,因此上下文為Activity.
只有主活動視窗使用Deor上下文,所有其他視窗依賴於賦予它們的任何上下文。

    mWindow = new PhoneWindow(this, window);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    //問題:PhoneWindowLayoutInflater物件怎麼生成的呢?
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }

    //3)初始化當前Activity的基本資訊
if (info.uiOptions != 0) {

        mWindow.setUiOptions(info.uiOptions);

    }

    mUiThread = Thread.currentThread();



    mMainThread = aThread;

    mInstrumentation = instr;

    mToken = token;

    mIdent = ident;

    mApplication = application;

    mIntent = intent;

    mReferrer = referrer;

    mComponent = intent.getComponent();

    mActivityInfo = info;

    mTitle = title;

    mParent = parent;

    mEmbeddedID = id;

    mLastNonConfigurationInstances = lastNonConfigurationInstances;

    if (voiceInteractor != null) {

        if (lastNonConfigurationInstances != null) {

            mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;

        } else {

            mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,

                    Looper.myLooper());

        }

    }



    mWindow.setWindowManager(

            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),

            mToken, mComponent.flattenToString(),

            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

    if (mParent != null) {

        mWindow.setContainer(mParent.getWindow());

    }

    mWindowManager = mWindow.getWindowManager();

    mCurrentConfig = config;

}

1.2.2 問題:PhoneWindow中有一個LayoutInflater,這個是什麼時候生成的呢?

這個實在PhoneWindow建構函式建立的時候生成的。上下文對應的是Activity。

PhoneWindow.java

           //有一個成員為DecorView

           private DecorView mDecor;//設定視窗內容存放的檢視,要麼是DecorView自身,或者是DecorView的字檢視--包含儲存存放內容的子檢視   // This is the view in which the window contents are placed. It is either // mDecor itself, or a child of mDecor where the contents go. ViewGroup mContentParent;

public PhoneWindow(Context context) {
    super(context);
    mLayoutInflater = LayoutInflater.from(context);
}

LayoutInflater在PhoneWindow初始化的時候建立的。

1.3 DecorView初始化

常見問題:

1)DecorView什麼時候初始化?

2)DecorView和ContentParent是同一個東西嗎?儲存內容有什麼不同?

3)FEATURE_CONTENT_TRANSITIONS對ContentView的初始化有什麼影響?

FEATURE_CONTENT_TRANSITIONS是Activity切換的過渡動畫。在這種情況下,

不但要進行setContentView初始化目標頁面,還需要對切換效果播放一個動畫。

4)setContentView是直接從XML將得到的內容檢視新增到DecorView嗎?

5)如果Activity初始化時不呼叫setContentView,頁面是否能夠顯示?

1.3.1 Activity. setContentView

這是視窗的頂檢視,包含視窗裝飾。

Activity.java 看看setContentView入口處理:

Step1

/**

* 將佈局資源新增到Activity內容區。資源(XML)將被展開inflated形成子檢視,新增到Activity頂層檢視中。

*//**  * Set the activity content from a layout resource.  The resource will be  * inflated, adding all top-level views to the activity.  *  * @param layoutResID Resource ID to be inflated.  *  * @see #setContentView(android.view.View)  * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)  */public void setContentView(@LayoutRes int layoutResID) {        //1)將資源檢視新增到PhoneWindow

    getWindow().setContentView(layoutResID);        //2)初始化DecorView(如果沒有初始化)ActionBar

    initWindowDecorActionBar(); }

1.3.2 PhoneView. setContentView

Step2

我們Activity物件繼承Activity類,在onCreate中,會呼叫setContentView方法設定頁面內容。

Note: FEATURE_CONTENT_TRANSITIONS

@Override

public void setContentView(int layoutResID) {

    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window

    // decor, when theme attributes and the like are crystalized. Do not check the feature

    // before this happens.
注意:可以在安裝視窗裝飾器的過程中設定FEATURE_CONTENT_TRANSITIONS。在發生這種情況之前不要設定這個特性。

if (mContentParent == null) {

        //1}內容區父檢視為空時,進行安裝操作即DecorView的初始化。然後展開佈局資源,新增到頂級檢視中
     installDecor();

    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {

        mContentParent.removeAllViews();

    }
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {

        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,

                getContext());

        transitionTo(newScene);

    } else {
   //3)layoutResId展開並且新增到內容區父檢視中

        mLayoutInflater.inflate(layoutResID, mContentParent);

    }

    mContentParent.requestApplyInsets();

    final Callback cb = getCallback();

    if (cb != null && !isDestroyed()) {

        cb.onContentChanged();

    }

    mContentParentExplicitlySet = true;

}

1.3.3 PhoneView. installDecor

Step3: 接著分析installDecor。安裝DecorView

private void installDecor() {

    mForceDecorInstall = false;

    //1.建立DecorView.如果沒有初始化則進行建立;如果已經存在,則將其關聯當前PhoneWindow
if (mDecor == null) {

      //Décor為空生成一個新的DecorView
    mDecor = generateDecor(-1);

        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

        mDecor.setIsRootNamespace(true);

        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {

            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);

        }

    } else {

        mDecor.setWindow(this);

    }



//建立內容區的父容器。如果父容器為空則進行初始化  
   if (mContentParent == null) {

        //父容器為空則生成一個新的父容器,    
        mContentParent = generateLayout(mDecor);



        // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
        final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                R.id.decor_content_parent);

        if (decorContentParent != null) {
            mDecorContentParent = decorContentParent;
            mDecorContentParent.setWindowCallback(getCallback());
            if (mDecorContentParent.getTitle() == null) {
                mDecorContentParent.setWindowTitle(mTitle);
            }

            final int localFeatures = getLocalFeatures();
            for (int i = 0; i < FEATURE_MAX; i++) {

                if ((localFeatures & (1 << i)) != 0) {

                    mDecorContentParent.initFeature(i);

                }

            }

。。。。。。

  

    }

}

在其中,會生成DecorView和Contentparent。那麼我們繼續分析

1.3.4 PhoneView. generateDecor

常見問題:

  1. DecorView的上下文從何獲取?
  2. DecorView
protected DecorView generateDecor(int featureId) {
     //系統程序沒有Application上下文,在這種情況下,我們需要直接使用我們擁有的上下文。否則,我們需要Application程式上下文,因此我們不依附於該Activity
    // System process doesn't have application context and in that case we need to directly use

    // the context we have. Otherwise we want the application context, so we don't cling to the

    // activity.

    Context context;

    if (mUseDecorContext) {

        Context applicationContext = getContext().getApplicationContext();

        if (applicationContext == null) {

            context = getContext();

        } else {

            context = new DecorContext(applicationContext, getContext().getResources());

            if (mTheme != -1) {

                context.setTheme(mTheme);

            }

        }

    } else {

        context = getContext();

    }

    return new DecorView(context, featureId, this, getAttributes());

}

新建立一個DecorView物件。

1.3.5 PhoneWindow. generateLayout

常見問題:

1.ContentParent與DecorView關係?

2.ContentParent是否需要依賴DecorView?

3. ContentParent層級結構如何?

4.使用者是否可以替代ContentParent?

5.DecorView和ContentParent是檢視嗎?

兩者都是容器,是FrameLayout

protected ViewGroup generateLayout(DecorView decor) {

    // Apply data from current theme.

    // Inflate the window decor.



    int layoutResource;

    int features = getLocalFeatures();

    // System.out.println("Features: 0x" + Integer.toHexString(features));

    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {

        layoutResource = R.layout.screen_swipe_dismiss;

    } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {

        if (mIsFloating) {

            TypedValue res = new TypedValue();

            getContext().getTheme().resolveAttribute(

                    R.attr.dialogTitleIconsDecorLayout, res, true);

            layoutResource = res.resourceId;

        } else {

            layoutResource = R.layout.screen_title_icons;

        }

        // XXX Remove this once action bar supports these features.

        removeFeature(FEATURE_ACTION_BAR);

        // System.out.println("Title Icons!");

    } else if ((features & ((1 << FEATURE_PROGRESS) | (