Viewにxmlに書いた要素やThemeが反映されるまでのコードを追ってみたメモ

ただのメモです。
全体的に追いきれていないので、間違っていたらご指摘ください、、

AppCompatActivityなどを継承していると結構違うコードとなりますが、基本的には同じようなことをしているはずです。


最初にまとめるとこんなかんじです。

LayoutInflaterでxmlから要素を取り出す

```java
AttributeSet attributeSet = Xml.asAttributeSet(getResources().getLayout(R.layout.activity_main));
```

Viewで**テーマをコンテキストから取得**して適応した要素一覧をTypedArrayで取得


```java
 final Resources.Theme theme = context.getTheme();
 TypedArray a = theme.obtainStyledAttributes(
                    attributeSet, textViewStylable, defStyleAttr, defStyleRes);)
```

それを反映する

```java
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
            case com.android.internal.R.styleable.TextView_text:
                text = a.getText(attr);
                break;
```



# 追ってみた

## xmlから要素を取得するまで
おなじみのおまじない

```java
            setContentView(R.layout.activity_main);
```

ActivityでWindowのクラスのsetContentViewを呼ぶ

```java
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
```

PhoneWindowでmLayoutInflater.inflateを呼ぶ

```java
    @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.
        if (mContentParent == null) {
            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 {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
```


以下のようにAttributeSetをxmlから取り出しています。
LayoutInflater内


```java
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
```

```java
    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;

            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {
                    // Empty
                }

                if (type != XmlPullParser.START_TAG) {
                    throw new InflateException(parser.getPositionDescription()
                            + ": No start tag found!");
                }

                final String name = parser.getName();
                
                if (DEBUG) {
                    System.out.println("**************************");
                    System.out.println("Creating root view: "
                            + name);
                    System.out.println("**************************");
                }

                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        // Create layout params that match root, if supplied
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // Set the layout params for temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }

                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }

                    // Inflate all children under temp against its context.
                    rInflateChildren(parser, temp, attrs, true);

                    if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }

                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
                InflateException ex = new InflateException(e.getMessage());
                ex.initCause(e);
                throw ex;
            } catch (Exception e) {
                InflateException ex = new InflateException(
                        parser.getPositionDescription()
                                + ": " + e.getMessage());
                ex.initCause(e);
                throw ex;
            } finally {
                // Don't retain static reference on context.
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;
            }

            Trace.traceEnd(Trace.TRACE_TAG_VIEW);

            return result;
        }
    }
```

それをcreateViewFromTagでViewのコンストラクタをリフレクションで呼び出したりなどがあるのですが、ここでは書きません。



とりあえずレイアウトの内容が取得できそうなattributeSetが取得出来ました。

## xmlから取得した要素をViewに反映するまで

### TextViewコンストラクタ内

このattributeSetがViewのコンストラクタで渡ってきます。

```java
    public TextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.textViewStyle);
    }
```



attributeSetを利用してこんな感じでTypedArrayを取得します(ここではTextViewを例に話します)

```java
 final Resources.Theme theme = context.getTheme();
 TypedArray a = theme.obtainStyledAttributes(
                    attributeSet, textViewStylable, defStyleAttr, defStyleRes);)
```
となっており、getTheme()でActivityはContextThemeWrapperを継承しているので、そのメンバ変数にセットされているResources.Themeを取得します **ここでManifestなどで設定したテーマの反映がなされるようです** 
ここまで読んでいる方はご存知かもしれませんが、Contextに設定されているテーマによってViewのテーマが変わります。



#### 引数について
* textViewStylableはdeclare-styleableで定義された要素(例:com.android.internal.R.styleable.TextView)  
layout_widthなどはdeclare-styleableで定義されたものレイアウトのようです。

```xml
   <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
```
参考: http://y-anz-m.blogspot.jp/2011/06/declare-styleable.html


* defStyleAttrはcom.android.internal.R.attr.textViewStyleなどのattrで テーマで利用する要素一覧のようです
* defStyleResはAPI21からのようで、デフォルトのスタイルのようです。

styleableやattrはここで定義されていたりします。
https://android.googlesource.com/platform/frameworks/base/+/HEAD/core/res/res/values/attrs.xml
TextViewStyleはこちら？
https://android.googlesource.com/platform/frameworks/base/+/HEAD/core/res/res/values/styles.xml


あとはa(TypedArray)から要素を取り出してViewの要素に反映させていくだけです。

```java
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
            case com.android.internal.R.styleable.TextView_text:
                text = a.getText(attr);
                break;
```

### Resources.Theme#obtainStyledAttributes()内

そうなると、Resources.Theme#obtainStyledAttributes()の中が気になります。


```java
        public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
            final int len = attrs.length;
            final TypedArray array = TypedArray.obtain(Resources.this, len);
            array.mTheme = this;
            AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices);
            return array;
        }
```

AssetManager.java

```java
native static final boolean applyStyle(long theme,
            int defStyleAttr, int defStyleRes, long xmlParser,
            int[] inAttrs, int[] outValues, int[] outIndices);
```

JNIによってC++のソースにに突入します。

https://android.googlesource.com/platform/frameworks/base.git/+/android-4.3_r2.1/core/jni/android_util_AssetManager.cpp#987
styleやthemeから取得してTypedArrayに代入していっているような気がします(自信ないです)


AssetManagerがよしなにやってくれているようですがそこがちょっと追いきれていません、、