Activityレイアウトロードプロセスソース分析(I)


最近Androidソースを読むと、新大陸を発見したような感じがします.以前はAndroidの知識に触れていたが、ソースコードを読むうちに、明るくなってきた.先日、2編のブログActivity起動プロセスソース分析(応用中)とActivity起動プロセスソース分析(Launcher中)を書き終えましたが、今日はActivityレイアウトロードプロセスを書きたいと思っています.実は、熱いうちに鉄を打って、この知識を整理したいと思っています.
整理を開始する前に、次のような概念を理解する必要があります.
  • Window:ウィンドウを表す抽象クラスです.Androidシステムのインタフェースも、ウィンドウの形で存在します.
  • PhoneWindow:Windowクラスの具体的な実装クラスであり、Activityでのレイアウトロードロジックは主にこのような中で完了します.
  • WindowManager:Windowの管理クラスで、Windowの追加、更新、削除を管理しています.
  • WindowManagerService(WMS):システムウィンドウ管理サービスクラスであり、システムの様々なWindowを具体的に管理する.
  • DecorView:WindowのトップレベルのViewで、主に各種のViewを搭載しています.

  • 一、Activityレイアウトロード分析


    Activityレイアウトの設定は、主にActivityのonCreate()でsetContentView()メソッドを呼び出すことを知っています.次に、このメソッドを見てみましょう.
       
        public void setContentView(int layoutResID) {
            getWindow().setContentView(layoutResID);// 
            initActionBar();
        }
    

    ここでは主にgetWindow()を呼び出す.setContentView()メソッドでは、ActivityのgetWindow()を見てみましょう.
        public Window getWindow() {
            return mWindow;
        }
    

    このことからmWindowはActivityの属性変数であることがわかり、前述のActivity起動プロセスの紹介では、Activity起動前にattach()が呼び出されることを知っていましたが、このmWindowはattach初期化時に付与されたもので、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) {
            attachBaseContext(context);
            mFragments.attachActivity(this);
            
            mWindow = PolicyManager.makeNewWindow(this);// 
    
            ......
    
            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;
            mLastNonConfigurationInstances = lastNonConfigurationInstances;
    
            mWindow.setWindowManager(null, mToken, mComponent.flattenToString(),
                    (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
            if (mParent != null) {
                mWindow.setContainer(mParent.getWindow());
            }
            mWindowManager = mWindow.getWindowManager();
            mCurrentConfig = config;
        }
    

    ここではPolicyManagerに注目してみましょうmakeNewWindow(this)メソッド、Windowを作成するには、PolicyManagerクラスを見てみましょう
    public final class PolicyManager {  
         
       private static final String POLICY_IMPL_CLASS_NAME =  
            "com.android.internal.policy.impl.Policy";  
      
        private static final IPolicy sPolicy;  
      
        static {  
            try {  
                Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);  
                sPolicy = (IPolicy)policyClass.newInstance();// Policy
    
            } catch (ClassNotFoundException ex) {  
                throw new RuntimeException(  
                        POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);  
            } catch (InstantiationException ex) {  
                throw new RuntimeException(  
                        POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);  
            } catch (IllegalAccessException ex) {  
                throw new RuntimeException(  
                        POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);  
            }  
        }  
       
        public static Window makeNewWindow(Context context) {  
            return sPolicy.makeNewWindow(context); // 
        }  
        .......
    }
    

    以上から明らかなように,ここでは主に反射によりPolicyを初期化し,その後,PolicyのmakeNewWindow()メソッドを設計モードにおける氏置換原則を用いて呼び出し,Policyにおけるメソッドを引き続き見る.
    public class Policy implements IPolicy {  
       
        ........
    
        public PhoneWindow makeNewWindow(Context context) {  
            return new PhoneWindow(context);// 
        }  
        ......
    }  
    

    mWindowは実際にはPhoneWindowであり、ActivityでgetWindow()であることがわかります.setContentView()メソッドとは、PhoneWindowのsetContentViewメソッドを呼び出すことですので、PhoneWindowのsetContentView()メソッドを見てみましょう
        @Override
        public void setContentView(int layoutResID) {
            if (mContentParent == null){
                installDecor();//1. 
            } else {
                mContentParent.removeAllViews();
            }
            mLayoutInflater.inflate(layoutResID, mContentParent);//2. 
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }
    

    注釈2から、レイアウトパディングmLayoutInflaterがmContentParentにレイアウト内容を入力していることを知っていますが、mContentParentはView Groupで、どのように値を付けているのでしょうか.ここで注釈1を見てみましょう.mContentParentが空の場合、装飾器が取り付けられます.phoneWindowのinstallDecor()メソッドを見てみましょう.
      private void installDecor() {
            if (mDecor == null) {
                mDecor = generateDecor();//1. 
                mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
                mDecor.setIsRootNamespace(true);
            }
            if (mContentParent == null) {
                mContentParent = generateLayout(mDecor);//2. ViewGroup 
    
                mDecor.makeOptionalFitsSystemWindows();
    
                mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
                if (mTitleView != null) {
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                      .......
                    } else {
                        mTitleView.setText(mTitle);// Activity title
                    }
                } else {
                    mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
                    if (mActionBar != null) {
                       .......mActionBar 
                    }
                }
            }
        }
    

    まず、注釈1のデコレーションの生成方法generateDecor()を見てみましょう.
        protected DecorView generateDecor() {
            return new DecorView(getContext(), -1);
        }
    

    ここでは主に装飾器DecorViewの初期化です.DecorViewのソースコードを見てみましょう.
       private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
            ......
    
            public DecorView(Context context, int featureId) {
                super(context);
                mFeatureId = featureId;
            }
           .......
    

    DecorViewクラスには内容が含まれていますが、ここでは説明しません.DecorViewはPhoneWindowの内部クラスであり、DecorViewはFrameLayoutに継承され、RootView Surface Takerインタフェースを実現していることを知っておくだけです.次に、mContentParentの生成、すなわちgenerateLayout(mDecor)メソッドについて説明します.
     protected ViewGroup generateLayout(DecorView decor) {
            
            .......// window 
    
            // Inflate the window decor.
    
            int layoutResource;
            int features = getLocalFeatures();
    
            // Activity feature 
            if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else {
                    layoutResource = com.android.internal.R.layout.screen_title_icons;
                }
                
                removeFeature(FEATURE_ACTION_BAR);
               
            } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                    && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
                // 
                layoutResource = com.android.internal.R.layout.screen_progress;
                
            } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {      
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else {
                    layoutResource = com.android.internal.R.layout.screen_custom_title;
                }
               
                removeFeature(FEATURE_ACTION_BAR);
            } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
                // Titile 
                if (mIsFloating) {
                    TypedValue res = new TypedValue();
                    getContext().getTheme().resolveAttribute(
                            com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
                    layoutResource = res.resourceId;
                } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                    if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
                        layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
                    } else {
                        layoutResource = com.android.internal.R.layout.screen_action_bar;
                    }
                } else {
                    layoutResource = com.android.internal.R.layout.screen_title;
                }
                
            } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
                layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
            } else {
               
                layoutResource = com.android.internal.R.layout.screen_simple;//1. 
               
            }
    
            mDecor.startChanging();
    
            View in = mLayoutInflater.inflate(layoutResource, null);
            decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//2. View View
    
            ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//3. Activity ViewGroup
            ......
            }
    
            mDecor.finishChanging();
    
            return contentParent;
        }
    

    ここでは、システムのデフォルトのいくつかのActivityのヘッダレイアウトxmlファイルを見てみましょう.レイアウトファイルのソースコードの位置はandroid 4です.1.1_r 1frameworksbasecoreresreslayout、2つのファイルを選んでみましょう.
    最初のscreen_title.xml
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:fitsSystemWindows="true">
        
        <ViewStub android:id="@+id/action_mode_bar_stub"
                  android:inflatedId="@+id/action_mode_bar"
                  android:layout="@layout/action_mode_bar"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content" />
        <FrameLayout
            android:layout_width="match_parent" 
            android:layout_height="?android:attr/windowTitleSize"
            style="?android:attr/windowTitleBackgroundStyle">
            <TextView android:id="@android:id/title" 
                style="?android:attr/windowTitleStyle"
                android:background="@null"
                android:fadingEdge="horizontal"
                android:gravity="center_vertical"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        FrameLayout>
        <FrameLayout android:id="@android:id/content"
            android:layout_width="match_parent" 
            android:layout_height="0dip"
            android:layout_weight="1"
            android:foregroundGravity="fill_horizontal|top"
            android:foreground="?android:attr/windowContentOverlay" />
    LinearLayout>
    

    2つ目はscreen_simple.xml
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
        <ViewStub android:id="@+id/action_mode_bar_stub"
                  android:inflatedId="@+id/action_mode_bar"
                  android:layout="@layout/action_mode_bar"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content" />
        <FrameLayout
             android:id="@android:id/content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:foregroundInsidePadding="false"
             android:foregroundGravity="fill_horizontal|top"
             android:foreground="?android:attr/windowContentOverlay" />
    </LinearLayout>
    

    比較すると、この2つのレイアウトファイルには@android:id/contentという共通のidを持つFrameLayoutがありますが、これはActivityレイアウトの充填容器です.また,この2つのレイアウトの親レイアウトはいずれも線形レイアウトLinearLayoutであり,方向は垂直であることが分かった.これは,Activityコンテンツレイアウトが一般的にステータスバーの下のモードであることを検証した.次のコードを見てみましょう
      View in = mLayoutInflater.inflate(layoutResource, null);
      decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//2. View View
    
      ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//3. Activity ViewGroup
    

    ここでは、アクセラレータにシステムレイアウトViewを追加し、システムレイアウトViewからActivity充填コンテンツのコンテナView Groupを取得します.ここでID_ANDROID_CONTENTはcomです.android.internal.R.id.contentは、(View Group)findView ById(ID_ANDROID_CONTENT)によって、レイアウトファイルのFrameLayout、すなわちActivityコンテンツがレイアウトのView Groupを埋め込むことを取得する.これでPhoneWindowのsetContentViewメソッドに戻ります
        @Override
        public void setContentView(int layoutResID) {
            if (mContentParent == null) {
                installDecor();
            } else {
                mContentParent.removeAllViews();
            }
            mLayoutInflater.inflate(layoutResID, mContentParent);// 
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
        }
    

    mContentParentで値を割り当てた後、レイアウトフィラーmLayoutInflaterのinflate()メソッドを使用して、Activityのレイアウトファイルをデザイナに追加しました.しかし、現在ではアクセサリDecorViewにActivityレイアウトがあるようですが、いつWindowに追加されたのでしょうか.ここではActivityの起動プロセスを理解する必要があります.Activityの起動プロセスの最後のステップでActivity ThreadのhandleLaunchActivity()メソッドが実行されます.次に、このメソッドを分析し続けます.
    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
          
            .......
    
            Activity a = performLaunchActivity(r, customIntent);//1. Activity 
    
            if (a != null) {
                r.createdConfig = new Configuration(mConfiguration);
                Bundle oldState = r.state;
    
                handleResumeActivity(r.token, false, r.isForward);//2. Activity onResume 
    
                .......
            } else {
                .......
            }
        }
    

    注記1では、Activityのインスタンスが確立され、Activityライフサイクルのattach()およびonCreate()メソッドが実行される.setContentView()もonCreate()メソッドで呼び出されていることを知っていますので、このときActivityレイアウトファイルの内容はすでにデザイナDecorViewに読み込まれています.次にDecorViewとWindowを関連付けるので、handleResumeActivity()メソッドを引き続き見てみましょう.
     final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
            // If we are getting ready to gc after going to the background, well
            // we are back active so skip it.
            unscheduleGcIdler();
    
            ActivityClientRecord r = performResumeActivity(token, clearHide);//1. Activity onResume 
    
            if (r != null) {
                final Activity a = r.activity;
    
                ........
                boolean willBeVisible = !a.mStartedActivity;
                if (!willBeVisible) {
                    try {
                        willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(a.getActivityToken());//2.Activity 
                    } catch (RemoteException e) {
                    }
                }
                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;
                    l.softInputMode |= forwardBit;
                    if (a.mVisibleFromClient) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);//3. WindowManager DecorView Window, Window,Activity 。
                    }
                } else if (!willBeVisible) {
                    r.hideForNow = true;
                }
    
                cleanUpPendingRemoveWindows(r);
    
                if (!r.activity.mFinished && willBeVisible&& r.activity.mDecor != null && !r.hideForNow) {
                 
                    WindowManager.LayoutParams l = r.window.getAttributes();
                    if ((l.softInputMode
                            & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                            != forwardBit) {
                        l.softInputMode = (l.softInputMode
                                & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                                | forwardBit;
                        if (r.activity.mVisibleFromClient) {
                            ViewManager wm = a.getWindowManager();
                            View decor = r.window.getDecorView();
                            wm.updateViewLayout(decor, l);//4. DecorView, Activity 
                        }
                    }
                    r.activity.mVisibleFromServer = true;
                    mNumVisibleActivities++;
                    if (r.activity.mVisibleFromClient) {
                        r.activity.makeVisible();
                    }
                }
    
              .......
    
            } else {
              .......
            }
        }
    

    注記1でperformResumeActivity(token,clearHide)メソッドを呼び出すのは、実際にはactivityライフサイクルを呼び出すonResume()メソッドです.注記2では、Binderによるプロセス間通信により、Activity Management ServiceでwillActivity BeVisible()を呼び出してActivityを表示する制御スイッチを取得することで、注記3でWindowManagerによってデザイナDecorViewをWindowに追加し、その後、関連するViewの描画プロセスを呼び出すというレイアウト付きActivityがロードされる.
    ここまで、Activityレイアウトのロードプロセスは整理済みです.
    注:ソースコードはandroid-4.1.1_を採用r 1バージョンでは、ソースコードをダウンロードして自分でプロセスを行うことをお勧めします.これにより、より理解が深まります.

    二、参考文書


    Binder通信メカニズムの原理解析
    Activity起動フローソース分析(応用中)
    Activity起動フローソース分析(Launcher)