类图
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是引用类型的,他们可以指向另外一个引用。
|
|
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