类图

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,所以会调用ContextThemeWrapper的getSystemService:
如果name是LAYOUT_INFLATER_SERVICE,则通过LayoutInflater.from(getBaseContext()).cloneInContext(this)来创建,上面分析过activity中的getBaseContext()是指向了contextImpl对象,所以兜兜转转,最终调用了contextImpl中的getSystemService来获取LayoutInflater对象。
那如果from传进来的不是Activity,而是application会怎么样呢?application它是继承自ContextWrapper:

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


从SYSTEM_SERVICE_FETCHERS中获取:

最终是在此处添加了一个PhoneLayoutInflater对象,并把contextimpl.getOuterContext传进去了,此处传进去的是Activity或者是application对象。
如果from中传入的是activity,在contextThemeWrapper的getSystemService方法中获取到PhoneLayoutInflater后,又调用了cloneInContext,将当前的context传入其中:
此处为什么要通过cloneInContext构造一个新的PhoneLayoutInflater呢? 因为layoutinflater解析xml是强依赖于context,所以为了确保当前activity创建的layoutinflater使用的是当前context,所以将当前的context传入其中构造一个新的layoutinflater。比如在from的时候传入的是view的context,而view的context是有可能不是activity类型的context,比如是一个contextThemewrapper类型的,那么是有可能PhoneLayoutInflater的origin context是一个activity,而newContext传入的是contextThemeWrapper。所以此处用cloneInContext是为了保证当前的context是即将要展示的theme样式。关于分析可以看下面的layoutinflater.inflate。
layoutinflater.inflate:
此过程调用createViewFromTag来创建view:

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

此处定义了一个theme属性,那么给该view的构造器传入的context就是一个contextThemeWrapper对象。所以view的context不一定是activity的context,如果设置了theme属性,那么就是一个contextThemeWrapper的context。
再来看一个例子,创建创建layoutinflater的时候指定contextThemeWrapper对象:
在from方法里面,传入一个contextthemewrapper对象,并携带一个style。根据上面分析from方法时,contextthemewrapper是通过LayoutInflater.from(getBaseContext()).cloneInContext(this)。此处的getBaseContext()又是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是引用类型的,他们可以指向另外一个引用。
|
|
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