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

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

你可能感兴趣的:(android,dagger,hilt,context)