最近在尝试将项目中的dagger依赖注入替换成hilt, 碰到了crash的问题,排查了一下原因记录一下。
项目中使用liteav做视频播放器,在inflate视频播放组件的时候出的InflateException, 不过这不是根本的原因,因为是在渲染视图的过程中出的异常,根本原因被catch了封装成了InflateException。根据异常堆栈的信息,只看得出来是在渲染TCControllerFullScreen
组件通过反射调Constructor.newInstance
的时候出的error,至于具体TCControllerFullScreen内部哪个组件就不知道了。
化繁为简,将TCControllerFullScreen组件使用的layout文件 vod_controller_fullscreen.xml一点点的打开注释运行看会不会crash,发现造成crash的是TCVodMoreView
组件。跟踪源码,最开始的异常点在 TCVodMoreView 的 updateCurrentLight 方法,代码如下:
private void updateCurrentLight() {
// 这里是异常点,mContext强转Activity出错
Activity activity = (Activity)this.mContext;
Window window = activity.getWindow();
...
}
好了,异常点找到了,但是问题并没有结束。一般情况下,view的context是activity,但是这个地方转换为什么会出错?继续跟源码。这里的view传入的context是hilt包装的一个ContextWrapper: ViewComponentManager 内部类 FragmentContextWrapper
导致强转出错。
在LayoutInflater在inflate视图的时候,通过反射创建视图组件,传入的context参数就是LayoutInflater持有的context。LayoutInflater是通过performGetLayoutInflater创建的,而它持有的context就是在创建LayoutInflater的时候传入的context。
关键代码如下:
FragmentStateManager
:
void ensureInflatedView() {
if (mFragment.mFromLayout && mFragment.mInLayout && !mFragment.mPerformedCreateView) {
if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(TAG, "moveto CREATE_VIEW: " + mFragment);
}
// 这里是准备创建fragment视图,就是会调到fragment的onCreateView方法
// performGetLayoutInflater会创建一个LayoutInflater
mFragment.performCreateView(mFragment.performGetLayoutInflater(
mFragment.mSavedFragmentState), null, mFragment.mSavedFragmentState);
}
}
performGetLayoutInflater
会创建一个LayoutInflater。跟进去会调到onGetLayoutInflater
如下:
@NonNull
public LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState) {
// TODO: move the implementation in getLayoutInflater to here
return getLayoutInflater(savedInstanceState);
}
这里就是最关键的地方。正常情况下会这里调到getLayoutInflater
会通过mHost持有的Context也就是Activity创建Inflater,但是hilt生成的类重写了这个方法如下:
@Override
public LayoutInflater onGetLayoutInflater(Bundle savedInstanceState) {
LayoutInflater inflater = super.onGetLayoutInflater(savedInstanceState);
return LayoutInflater.from(FragmentComponentManager.createContextWrapper(inflater, this));
}
这里创建Inflater的时候传入的context就是通过FragmentComponentManager创建的,也就是FragmentContextWrapper对象。所以通过Hilt实现DI的fragment在创建视图时传入的context都是hilt封装的ContextWrapper类才会导致强转出错。
(Issue:)不明白为什么要这么做,而且hilt生成的fragment还有一个componentContext
的字段也是FragmentContextWrapper类(不过好像没看到什么地方会用到),为什么创建Inflater的时候不使用componentContext子字段而要新建一个对象,大写的❓
后面真的要把dagger替换成hilt的话,再看怎么解决