Android context总结

类图

Context创建

Activity继承自ContextThemeWrapper,ContextThemeWrapper继承自ContextWrapper,当activity创建的之前,会先创建contextimpl ActivityThread.performLaunchActivity

接着会调用contextimpl的setOuterContext,传入的是activity。

结论:contextImpl中的outerContext指向了activity。

Context传递

attach调用了attachBaseContext,并把contextimpl传进去了:

该方法是ContextWrapper中的方法,并指向了mBase变量。Activity中getBaseContext和getApplicationContext区别:

getBaseContext指向了刚刚attach方法传进来的contextimpl。

指向了mBase.getApplicationContext:

mPackageInfo是在创建contextimpl的时候传入的,它是loadedApk对象,它的getApplication方法是获取应用的Application对象:

结论:getBaseContext获取的是contextimpl对象,getApplicationContext获取的是Application对象。

主题设置

回到performLaunchActivity,给activity设置主题:

将theme的resid传进来,最终会把resid这个theme追加到mTheme上。这里牵扯到资源加载,后面再说。

LayoutInflater中的context

LayoutInflater.from(context):

此处的context一般是activity,看activity.getSystemService方法,如果传入的不是activity,比如是ContextThemeWrapper,Application会怎么样:

通过base调用getSystemService,而base其实也是activity。Application是继承自ContextWrapper:

base是contextimpl,最终调用的是contextimpl的getSystemService,activity亦是如此:

从SYSTEM_SERVICE_FETCHERS中获取:

最终是在此处添加了一个PhoneLayoutInflater对象,并把contextimpl.getOuterContext传进去了,此处传进去的是Activity对象。

layoutinflater.inflate:

此过程调用createViewFromTag来创建view:

此处看view有没有theme属性,有的话,则构造一个contextthemeWrapper出来,举个例子:

此处定义了一个theme属性,那么给该view的构造器传入的context就是一个contextThemeWrapper对象。

在from方法里面,传入一个contextthemewrapper对象,并携带一个style。根据上面分析from方法时,contextthemewrapper是通过base.getSystemService。此处的base又是activity,又因为activity.getSystemService,调用base.getSystemService,所以最终又回到了contextimpl.getSystemService。而在创建PhoneLayoutInflater的时候,又通过contextimpl.getOuterContext传入到PhoneLayoutInflater构造器中,但是在contextthemewrapper中最后又调了PhoneLayoutInflater的cloneInContext:

所以此种情况下,最终给view传的context也是一个contextthemewrapper的context。

Attr部分

从主题中获取属性:

最终这些属性是通过context的obtainStyledAttributes获取属性值。常见的方法是:

AttributeSet表示所有的属性集,它是在inflate过程中解析到view的属性集。 attrs表示的是要从哪个属性集中取到属性。 例如:

attrs文件中定义一个declare-styleable属性集,aapt工具会生成对应的R.class,但是此时是一个R.jar文件:

该文件在app/build/intermediates/compile_and_runtime_not_namespaced_r_class_jar/debug/R.jar,反编译该jar文件:

最终所有的资源类型都会生成一个R$**.class的类,而R.class其实是一个空壳:

看刚才定义的declare-styleable生成如下:

并且会在R$attr.class下面也会生成一个两个int值:

可以看出来生出来一个R.styleable.TestView的数组和两个int值,分别是R.styleable.TestView_attr1和R.styleable.TestView_attr2,它两分别代表TestView数组的索引,而对应的值是定义在resource.arsc文件中:

所以最终得出结论是:通过context.obtainStyledAttributes传入attrbuteset和attrs数组,得到了typearray,然后通过typearray的索引得到所有的属性:

defStyleAttr、defStyleRes

新增一个不在declare-styleable数组中的attr11,见R文件:

在R.styleable类中没有attr11的定义,它在R.attr类中定义了:

在上面属性中attr7和attr11是引用类型的,他们可以指向另外一个引用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class TestView1 extends View {
    public TestView1(Context context) {
        this(context, null);
    }

    public TestView1(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, R.attr.attr11);
    }

    public TestView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, R.style.DefStyleRes);
    }

    public TestView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.gui, defStyleAttr, defStyleRes);
        log("TypedArray length:" + ta.length());
        for (int i = 0; i < ta.length(); i++) {
            int attrIndex = ta.getIndex(i);
            switch (attrIndex) {
                case R.styleable.gui_attr1:
                    log(ta.getString(attrIndex));
                    break;
                case R.styleable.gui_attr2:
                    log(ta.getString(attrIndex));
                    break;
                case R.styleable.gui_attr3:
                    log(ta.getString(attrIndex));
                    break;
                case R.styleable.gui_attr4:
                    log(ta.getString(attrIndex));
                    break;
                case R.styleable.gui_attr5:
                    log(ta.getString(attrIndex));
                    break;
                case R.styleable.gui_attr6:
                    log(ta.getString(attrIndex));
                    break;
                case R.styleable.gui_attr7:
                    log(ta.getString(attrIndex));
                    break;
                default:
                    break;
            }
        }
        ta.recycle();
    }

    private void log(String msg) {
        Log.v(getClass().getSimpleName(), "" + msg);
    }
}

TestView1中defStyleAttr传入R.attr.attr11,defStyleRes传入R.style.DefStyleRes。 布局文件如下:

其中theme中引用了attr11的引用,而themestyle中引用了attr1-attr4,DefStyleRes中也引用了attr1-attr4。日志如下:

R.styleable.gui总共长度是7,attr1用的xml中定义的,attr2是布局中定义的style中的属性,attr3和attr4取的是theme中attr11定义的attr3和attr4,由于attr5没有在attr11中的style中定义,所以取的是theme中的attr5属性。 优先级:布局中的attr>布局中的style中的attr>defStyleAttr中的attr>theme中的attr 此时无论怎么在DefStyleRes中定义属性,都不会在该style里面的attr取值,因为此时定义了defStyleAttr 此时如果去掉defStyleAttr,则会在DefStyleRes中取值:

结果如下:

总结:defStyleAttr定义了后,defStyleRes中的attr就不起作用了。

参考:https://blog.csdn.net/GracefulGuigui/article/details/104069265

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy