LayoutInflater Factory使用
背景
通常我们hook布局解析有几种方式。
- 通过asm插桩的方式。代码中一般会写
LayoutInflater.from(Context).inflate(@LayoutRes int resource, @Nullable ViewGroup root)
。通过asm hook该处,调用我们预定义的代码,然后就可以拿到view做一些想做的事情; - 利用LayoutInflater中两个成员变量mFactory和mFactory2实例。其实AppCompatActivity就是这么做的。AppCompatActivity是为了实现TextView替换为AppCompatTextView实现一些TextView没有的效果,那么我们也可以利用LayoutInflater的factory实现我们想要的一些功能。
本文我们着重分析下LayoutInflater的两个Factory,以及AppCompatActivity的具体实现逻辑。
LayoutInflater inflate方法实现逻辑
在说Factory之前有一个细节我们需要注意下。
LayoutInflater.from(context) 这里获取是根据context获取的,不同的context是不同的实例,因此在setFactory的时候,需要针对一个基类的activity来处理,不可以设置全局。
LayoutInflater inflate核心代码如下
1 | public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { |
inflate过程大概经历4件事情,
@1 createViewFromTag 创建view
@2 设置LayoutParams
@3 rInflateChildren解析子view
@4 addView添加到布局中
createViewFromTag中创建view大致逻辑如下
1 | View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, |
1 | public final View tryCreateView(@Nullable View parent, @NonNull String name, |
1 | public final View createView(@NonNull Context viewContext, @NonNull String name, |
createViewFromTag逻辑如下:
- 尝试tryCreateView,依次尝试factory2和factory创建view;
- 如果tryCreateView为空,则调用onCreateView或者createView,两者区别为onCreateView是没有.,即系统View如TextView,onCreateView最终会对name之前拼接
android.view.
,最终还会调到createView中; - createView逻辑为调用class.forName获取类,然后获取类的构造方法,创建view实例。
AppCompatActivity和LayoutInflater的两个Factory
两个Factory的区别
mFactory2和mFactory的区别是调用方法的区别,mFactory2多了一个parent参数,即为当前创建的view的父view。
installViewFactory设置Factory
AppCompatActivity中onCreate时会执行installViewFactory方法,内部先判断factory是否为空,如果为空则调用LayoutInflaterCompat.setFactory2
方法,否则检查factory2是否是AppCompatDelegateImpl,如果不是则打印日志。这里没有什么约束条件。
1 | @Override |
1 | @Override |
1 | LayoutInflaterCompat.java |
LayoutInflaterCompat提供了对SDK小于21的支持,因此一般通过该方法进行设置factory,我们再看下inflater.setFactory2的具体逻辑。
1 | public void setFactory2(Factory2 factory) { |
这里会有强制限制的逻辑,也就是setFactory只能设置一次,那么继承AppCompatActivity的activity中只能通过反射的方式去设置factory了。
如果mFactory为空,则直接将两者都设置为需要替换的factory,否则会创建FactoryMerger,包裹之前的mFactory和mFactory2。使用优先级是先调用需要替换的factory,如果创建view为空,则调用origin的factory。具体逻辑参照下面代码。
1 | private static class FactoryMerger implements Factory2 { |
AppCompatActivity替换的Factory执行逻辑
设置完factory后,我们看下factory的具体做了什么事情。
1 | AppCompatDelegateImpl.java |
AppCompatDelegateImpl最终交给了AppCompatViewInflater来处理。
1 | AppCompatViewInflater.java |
上面代码是AppCompatViewInflater的核心逻辑了,即根据name来创建对应的CompatView。
demo
了解了LayoutInflater的两个Factory以及AppCompatActivity的实现逻辑,我们可以简单写个demo。如果activity继承了AppCompatActivity,因为AppCompatActivity已经设置过了,我们只能通过反射的方式来实现了。
1 | public class LayoutInflaterActivity extends AppCompatActivity { |