Android hilt替换dagger遇到的context导致crash问题

tech2022-10-17  116

     最近在尝试将项目中的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的话,再看怎么解决

最新回复(0)