如何使用Dagger2和Kotlin在AndroidStudio的其他项目模块中实现接口存储库

时间:2017-11-11 17:30:55

标签: android android-studio kotlin dagger-2 dagger

我希望能够在AndroidStudio中的其他项目模块中获得用户位置的2个不同的反应实现。

所以具体来说,可以使用gms,或者只使用原生的android LocationManager。

这是我的存储库界面:

interface RxLocationRepository {

@SuppressLint("MissingPermission")
fun onLocationUpdate(): Observable<Location>

fun stopLocationUpdates()
}

所以现在,我在这里实现这个接口,在AndroidStudio中的同一项目模块中的类中:

class RxLocationRepositoryImpl(val reactiveLocationProvider: ReactiveLocationProvider,
                           val reactiveLocationRequest: LocationRequest,
                           val isUsingLocationNativeApi: Boolean,
                           val locationManager: LocationManager,
                           val geoEventsDistanceMeters: Int,
                           val geoEventsIntervalSeconds: Int
) : RxLocationRepository {


var locationToPopulate: Location = Location(LocationManager.GPS_PROVIDER)
lateinit var mLocationCallbackNativeApi: LocationListener
private val subject: BehaviorSubject<Location> = BehaviorSubject.createDefault(locationToPopulate)
var locationEmitter: Observable<Location> = subject.hide()

init {
    configureEmitter()
}

@SuppressLint("MissingPermission")
private fun configureEmitter(){
    if (!isUsingLocationNativeApi)
      locationEmitter = reactiveLocationProvider.getUpdatedLocation(reactiveLocationRequest)
    else{
      configureNativeLocationEmitter()
    }
}

@SuppressLint("MissingPermission")
private fun configureNativeLocationEmitter() {

    mLocationCallbackNativeApi = object : LocationListener {

        override fun onLocationChanged(location: Location) {
            subject.onNext(location)
        }

        override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}

        override fun onProviderEnabled(provider: String) {}

        override fun onProviderDisabled(provider: String) {}

    }

    try {
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                (geoEventsIntervalSeconds * 1000).toLong(),
                geoEventsDistanceMeters.toFloat(),
                mLocationCallbackNativeApi,
                Looper.getMainLooper())
    } catch (ignored: IllegalArgumentException) {
        ignored.printStackTrace()
    }

    try {
        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                (geoEventsIntervalSeconds * 1000).toLong(),
                geoEventsDistanceMeters.toFloat(),
                mLocationCallbackNativeApi,
                Looper.getMainLooper())
    } catch (ignored: IllegalArgumentException) {
        ignored.printStackTrace()
    }
}

@SuppressLint("MissingPermission")
override fun onLocationUpdate(): Observable<Location> {
    return locationEmitter
}

override fun stopLocationUpdates() {
    if(isUsingLocationNativeApi)
    locationManager.removeUpdates(mLocationCallbackNativeApi)
  }
}

所以一切都很好,但是现在我想在不同的项目模块中实现这个gms(所以我不需要在gradle中有gms依赖)以及在其他模块中的本机实现Android Studio。

AndroidStudio中项目的最终结构将类似于location_core,location_gms,location_native。

location_core一定不能知道location_gms和native,我想与他们相反 - 他们将依赖于location_core。

所以现在,我只有location_core项目模块和一个LocationClient类,它将提供上下文:

class LocationClient @Inject constructor(val nexoLocationManager: NexoLocationManager){
companion object {

    private var locationClient: LocationClient? = null

    fun obtainLocationClient(context: Context): LocationClient{
        val result = locationClient ?:
                DaggerLocationComponent
                        .builder()
                        .context(context)
                        .build()
                        .locationClient()

        locationClient = result
        return result
    }
  }
}

它正在使用一些Dagger组件,它为他提供了不同对象的所有实现。 所以我想移动这个LocationClient或者在location_core项目模块中将其抽象化,并在AndroidStudio中的每个location_gms和location_native项目模块中移动它的实现。

因此,每个中的LocationClient类将提供此RxRepository的不同实现。 现在,在每个location_gms和location_native项目模块中,我只有实现类,它以自己的方式实现了存储库rxLocationRepository(我从上面的实现中移出)。

问题是我不知道如何用Dagger管理所有这些。 现在我在我的location_core中使用这样的Dagger,这里是LocationComponent:

@Singleton
@Component(modules = arrayOf(LocationModule::class))
interface LocationComponent{
fun locationClient(): LocationClient

@Component.Builder
interface Builder {
    @BindsInstance
    fun context(context: Context): Builder
    fun build(): LocationComponent
 }

}

及其模块:

@Module
class LocationModule {

//some stuff

    @Provides
@Singleton
fun providesRxLocationRepository(
                                 reactiveLocationProvider: ReactiveLocationProvider,
                                 reactiveLocationRequest: LocationRequest,
                                 @Named("CONFIG_LOCATION_USE_NATIVE_API")isUsingLocationNativeApi: Boolean,
                                 locationManager: LocationManager,
                                 @Named("CONFIG_LOCATION_GEO_EVENTS_DISTANCE_METERS")geoEventsDistanceMeters: Int,
                                 @Named("CONFIG_LOCATION_GEO_EVENTS_INTERVAL_SECONDS")geoEventsIntervalSeconds: Int
): RxLocationRepository = RxLocationRepositoryImpl(
        reactiveLocationProvider,
        reactiveLocationRequest,
        isUsingLocationNativeApi,
        locationManager,
        geoEventsDistanceMeters,
        geoEventsIntervalSeconds)

//some other stuff
}

那么如何在每个模块中编写实际的LocationClient - location_gms和location_native?如何使用Dagger提供每个location_gms和location_native项目模块中的rxLocationRepository的实现,这样我就可以在我的location_core模块中使用接口RxLocationRepository并且不用担心它的实现,因为它会在每个项目模块中?

当然,我必须指出,这3个模块永远不会在一起,我想,我猜每个build_variant中将有2个模块。 所以我必须摆脱location_core项目模块build.gradle中的google服务依赖。

enter image description here

更新

我怎么能在我的location_core中没有Dagger组件,我在其中测试如下: val component = DaggerLocationTestInstrumentalComponent.builder().context(InstrumentationRegistry.getContext()).build() val database = component.testDatabase() val locationManager = component.locationClient().nexoLocationManager

1 个答案:

答案 0 :(得分:1)

  

当然,我必须指出,这3个模块永远不会在一起,我想,我猜每个build_variant将是2个模块。

这是要注意的要点。您的组件定义不应位于location_core内。相反,它应该在&#34;消费者&#34;侧。消费者,我指的是实际使用位置的模块。我们称之为consumer_module

我将如何处理这项任务。

  1. location_gmslocation_native都应包含LocationModule,其实现方式不同。在两个模块中对LocationModule使用相同的包名称。

  2. consumer_module包含LocationComponent。根据构建变体,它将从LocationModulelocation_gms解析为location_native

  3. LocationModule的另一种方法是相同的,但在consumer_module两个LocationComponent中创建。每个都有不同的构建变体。在这种方法中,您不必为LocationModule保留相同的包名称。