graph LR
正面解决(正面解决)-->去IO,去解析-->|solution|frontSolution("X2C,Anko,Compose")
正面解决-->去反射-->|solution|frontSolution
侧面解决-->异步加载-->|solution|AsyncLayoutInflater
我们可以看到,在setContentView中主要有两个耗时操作:
1.解析xml,获取XmlResourceParser,这是IO过程。
2.通过createViewFromTag,创建View对象,用到了反射。
以上两点就是布局加载可能导致卡顿的原因,也是布局的性能瓶颈。
@Around("execution(* android.app.Activity.setContentView(..))")
public void getSetContentViewTime(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
String name = signature.toShortString();
long time = System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Log.i("aop inflate",name + " cost " + (System.currentTimeMillis() - time));
}
上面用的Aspectj,比较简单,上面的注解的意思是在setContentView方法执行内部去调用我们写好的getSetContentViewTime方法。 这样就可以获取相应的耗时。我们可以看下打印的日志:
I/aop inflate: AppCompatActivity.setContentView(..) cost 69
I/aop inflate: AppCompatActivity.setContentView(..) cost 25
这样就可以实现无侵入的监控每个页面布局加载的耗时。
有时为了更精确的知道到底是哪个控件加载耗时,比如我们新添加了自定义View,需要监控它的性能。 我们可以利用setFactory2来监听每个控件的加载耗时。首先我们来回顾下setContentView方法:
public final View tryCreateView(@Nullable View parent, @NonNull String name,
...
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
...
return view;
}
在真正进行反射实例化xml结点前,会调用mFactory2的onCreateView方法。 这样如果我们重写onCreateView方法,在其前后加上耗时统计,即可获取每个控件的加载耗时。
private fun initItemInflateListener(){
LayoutInflaterCompat.setFactory2(layoutInflater, object : Factory2 {
override fun onCreateView(
parent: View?,
name: String,
context: Context,
attrs: AttributeSet
): View? {
val time = System.currentTimeMillis()
val view = delegate.createView(parent, name, context, attrs)
Log.i("inflate Item",name + " cost " + (System.currentTimeMillis() - time))
return view
}
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
return null
}
})
}
如上所示:真正的创建View的方法,仍然是调用delegate.createView,我们只是其之前与之后做了埋点。
注意,initItemInflateListener需要在onCreate之前调用。这样就可以比较方便地实现监听每个控件的加载耗时。
//编译生成xml–>javaView
https://github.com/iReaderAndroid/X2C
//仅仅优化反射