Kotlin + Dagger 注入问题取决于设备的 Android 版本/SDK (?)

kka*_*aun 5 android kotlin dagger dagger-2 kapt

上周,在我当前的 Kotlin MVP 项目中实施 Dagger 时,由于主要手机的维护,我在带有KitKat 4.4.2 的旧手机上测试它(是的,它仍然支持所有主要的材料功能和东西 :))。所以那一周我遇到了典型的问题,而不是一些不寻常的问题,并通过调查提供的错误或多或少地修复了它们。最后,代码已编译,当前项目版本的构建没有问题,并且在 KitKat 与 UI 交互时没有重大错误。

但是当我从维修中心拿起带有Nougat 7.1.2 的主手机并在其上启动应用程序时,我遇到了与 DI 相关的奇怪问题。在那之后,我还在 mate 的Marshmallow 6.0上启动了应用程序,然后又抓到了一个,一模一样。问题简要描述如下:

  • 应用程序(成功)启动;

  • 我能够操作通过上下文和片段管理器注入提供的 ViewPager/DrawerLayout/etc ui 功能;

  • 所有服务也都可用并按预期通过注入运行;

  • 当我抽动 Activity 的演示者时,应用程序崩溃。

现在最有趣的部分让我发疯:所有不会带来任何问题的可访问类都是通过构造函数注入注入的。

但是,演示实例,这是使用注射字段注入,是没有初始化需要的时候。

当然,我尝试不使用 lateinit 修饰符,而是像使用 @JvmField 或不使用它的可空字段一样注入它:结果是相同的 - 它根本没有注入。

由于问题与 相关Activity,我有“自然约束”不使用主构造函数进行注入。

这是一个例外,除了第一个字符串外,它对我来说信息量不大:

kotlin.UninitializedPropertyAccessException: lateinit property presenter has not been initialized
    at .ui.common.view.BaseViewActivity.getPresenter(BaseViewActivity.kt:14)
    at .ui.main.view.MainActivity.onPlaceTypeClick(MainActivity.kt:143)
    at .ui.types.nearby.view.NearbyPlaceTypeItemViewHolder$bind$1.onClick(NearbyPlaceTypeItemViewHolder.kt:32)
    at android.view.View.performClick(View.java:5647)
    at android.view.View$PerformClick.run(View.java:22462)
    at android.os.Handler.handleCallback(Handler.java:754)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:163)
    at android.app.ActivityThread.main(ActivityThread.java:6205)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
Run Code Online (Sandbox Code Playgroud)

这是一些代码:

应用程序:

class MyApp : MultiDexApplication(), HasActivityInjector {

    @Inject
    @JvmField
    var activityInjector: DispatchingAndroidInjector<Activity>? = null

    override fun onCreate() {
        super.onCreate()
        DaggerMyAppComponent.builder().create(this).inject(this)
    }

    override fun activityInjector(): AndroidInjector<Activity>? {
        return activityInjector
    }
}
Run Code Online (Sandbox Code Playgroud)

应用组件:

@Singleton
@Component(modules = [
    MyAppModule::class,
    DataModule::class,
    PreferencesModule::class,
    ServiceModule::class,
    NavigationModule::class
])
interface MyAppComponent : AndroidInjector<MyApp> {

    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<MyApp>()
}
Run Code Online (Sandbox Code Playgroud)

应用模块:

@Module(includes = [AndroidSupportInjectionModule::class])
abstract class MyAppModule {

    @Binds
    @Singleton
    abstract fun application(myApp: MyApp): Application

    @PerActivity
    @ContributesAndroidInjector(modules = [(MainActivityModule::class)])
    abstract fun mainActivityInjector(): MainActivity

    //... other activity injectors
}
Run Code Online (Sandbox Code Playgroud)

基础活动模块:

@Module
abstract class BaseActivityModule {

    @Binds
    @PerActivity
    internal abstract fun activity(appCompatActivity: AppCompatActivity): Activity

    @Binds
    @PerActivity
    internal abstract fun activityContext(activity: Activity): Context

    @Module
    companion object {

        const val ACTIVITY_FRAGMENT_MANAGER = "BaseActivityModule.activityFragmentManager"

        @JvmStatic
        @Provides
        @Named(ACTIVITY_FRAGMENT_MANAGER)
        @PerActivity
        fun activityFragmentManager(activity: AppCompatActivity): FragmentManager {
            return activity.supportFragmentManager
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

目前我有一个广泛的 MVP 模型,所以每个活动/片段都有 1 个视图模块和 1 个展示器模块,并且所有常见的注入都通过基类进行交互。以下是活动 DI 部分的示例:

@Module(includes = [
    BaseActivityModule::class,
    MainPresenterModule::class
])
abstract class MainActivityModule {

    @Binds
    @PerActivity
    abstract fun mainView(mainActivity: MainActivity): MainView

    @Binds
    @PerActivity
    abstract fun appCompatActivity(mainActivity: MainActivity): AppCompatActivity

    @PerFragment
    @ContributesAndroidInjector(modules = [LocationFragmentModule::class])
    abstract fun locationFragmentInjector(): LocationFragment

    //... other related fragments injection methods
}


@Module
abstract class MainPresenterModule {

    @Binds
    @PerActivity
    abstract fun mainPresenter(mainPresenterImpl: MainPresenterImpl): MainPresenter
}
Run Code Online (Sandbox Code Playgroud)

基本活动:

abstract class BaseActivity : AppCompatActivity(), HasSupportFragmentInjector {

    @Inject
    @field:Named(BaseActivityModule.ACTIVITY_FRAGMENT_MANAGER)
    lateinit var fragmentManager: FragmentManager

    @Inject
    lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>

    override fun onCreate(@Nullable savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
        super.onCreate(savedInstanceState)
    }

    override fun supportFragmentInjector(): AndroidInjector<Fragment>? {
        return fragmentInjector
    }
}
Run Code Online (Sandbox Code Playgroud)

使用构造函数注入的示例 - MainPresenter(所有注入都有效 - 此处没有问题):

@PerActivity
class MainPresenterImpl @Inject constructor(
        val navigationManager: NavigationManager,
        val locationManager: LocationManagerImpl,
        val sharedPrefsRepo: SharedPreferencesRepository,
        view: MainActivity) : BaseViewPresenter<MainView>(view), MainPresenter {
}
Run Code Online (Sandbox Code Playgroud)

下面是所有“魔法”开始地方-var presenter不在任何情况下初始化。

abstract class BaseViewActivity<T : MVPresenter> (): BaseActivity(), MVPView {

    @Inject
    lateinit var presenter: T
}
Run Code Online (Sandbox Code Playgroud)

但是,如上所述,这不会发生在KitKat 上- 应用程序在它上面运行没有任何问题。所以,再一次:我认为当应用程序在Marshmallow+上运行时存在问题(不幸的是,还不知道 Lollipop)。

我真的想知道是否有人更有经验以前有过这样的问题。没有构建错误,根本没有匕首异常;只是未初始化的属性。

它可能以某种方式与许可有关吗?我拥有的唯一危险许可是设备的位置访问权限,并且它已被持有和测试。

毕竟,如果存在支持/版本控制问题的可能性,它可能与哪些其他方面有关?

提前致谢。

更新:

似乎 Presenter 实例绑定Activity它的 之后onCreate(),但是当触发了一些具有侦听器角色的 Activity 的回调操作时解除绑定。所以,看起来 Kotlin 再次查找了 Presenter,但发现它没有初始化。仍然不明白为什么这种情况只发生在较新的平台上。