首页 安卓& Kotlin Books 用kotlin的反应性编程

20
rxpermissions. 由Alex Sullivan撰写

从Android Marshmallow开始,Android开发人员需要在运行时询问某些权限,以允许用户有机会拒绝这些权限而不拒绝整个应用程序。在大多数情况下,它是Android生态系统的巨大变化。然而,它也具有非琐碎的显影剂疼痛。

大多数Android开发人员密切熟悉Android流程,以便请求权限。它要求您请求权限,然后在活动生命周期中的另一个回调中处理该权限请求的结果。如果您已经获得了许可以及您所诊断的情况,那么您就可以在其中学习的位置之间的这种差异是很多头痛的原因。

那里’s a helpful library called rxpermissions. 您将在本章中使用,以帮助实现这些疼痛点中的一些,并在权限请求时为您提供反应流。你还能想要什么?

入门

通过为本章打开Starter项目开始。你会努力 Wundercast. 您在本书中早些时候开始的应用程序。回顾 Wundercast. 允许你搜索一个城市,看到了温度,湿度等气象信息。

除了您要爱的位置和API键按钮外,屏幕底部还有两个新的按钮在本章中。一旦您完成章节,将保存当前显示的天气,左侧将保存图标。然后,其右侧的时钟图标将重新加载上次保存的天气并在应用程序中显示它。方便,对吗?

Wundercast. API使用OpenWeatherMap,所以在继续之前,请确保您有一个有效的OpenWeatherMap API密钥 http://openweathermap.org。如果您还没有密钥,则可以在其中注册一个 //home.openweathermap.org/users/sign_up.

一旦你完成注册过程,请访问API密钥的专用页面,在 //home.openweathermap.org/api_keys 并生成一个新的。

然后,在初学项目中,打开 Weatherapi.kt. 在这里,把你上面生成的密钥,并在文件的顶部替换占位符:

val apiKey =
  BehaviorSubject.createDefault("INSERT-API-KEY-HERE")

一旦这样做了,运行应用程序,并确保你能敏锐的天气为你最喜欢的城市或城镇。

请求位置许可

当你第一次开始工作时 Wundercast.,该应用程序将立即在应用程序启动后立即请求位置权限。众所周知,这不是非常伟大的用户体验。它迫使用户快速决定在您有机会看到您实际需要它之前的机会之前,可以进行应用程序的位置许可。除此之外,请在没有适当的上下文的情况下请求权限可以使用户更有可能拒绝您的许可。相反,如果只有在用户单击左下方的位置按钮后,只有在左下方的位置按钮时,它会更好。

textObservable
  .subscribeOn(Schedulers.io())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(this::showNetworkResult)
  .addTo(disposables)
location.setOnClickListener {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    requestPermissions(
      arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
      locationRequestCode)
  }
}
private val locationRequestCode = 101
override fun onRequestPermissionsResult(requestCode: Int,
  permissions: Array<out String>, grantResults: IntArray) {
  super.onRequestPermissionsResult(requestCode, permissions,
    grantResults)
  if (requestCode == locationRequestCode) {
    val result = grantResults[0]
    if (result == PackageManager.PERMISSION_GRANTED) {
      TODO("Fetch the location!")
    }
  }
}
fun updateWeatherFromLocation() {
  cityLiveData.postValue("Current Location")
  lastKnownLocation
    .flatMapSingle {
      WeatherApi.getWeather(it).subscribeOn(Schedulers.io())
    }
    .onErrorResumeWith(Maybe.just(
      WeatherApi.NetworkResult.Success(Weather.empty)
    ))
    .subscribe(this::showNetworkResult)
    .addTo(disposables)
}
private lateinit var model: WeatherViewModel
val model = ...
model = ...
model.updateWeatherFromLocation()

使用rxpermissions.

You’ve got a working solution that incorporates permissions, but it took a lot of code, and you had to disrupt the existing reactive setup you had. It required storing more state, i.e., the view model, in your WeatherActivity class as well.

implementation 'com.github.tbruyelle:rxpermissions:0.10.2'
val permissions = RxPermissions(this)
// 1
val locationObservable = location.clicks()
  // 2
  .flatMap {
    permissions
      .request(Manifest.permission.ACCESS_FINE_LOCATION)
  }
  // 3
  .filter { it }
  // 4
  .map { Unit }
RxJavaBridge.toV3Observable(permissions
  .request(Manifest.permission.ACCESS_FINE_LOCATION))
locationObservable.subscribe { 
    model.locationClicked() 
}.addTo(disposables)

请求其他许可

您已经获得了请求权限的基础知识 rxpermissions. 下来,好样的!它的时间来实现保存和恢复功能本章前面提到的。

val saveObservable = save.clicks()
val readObservable = load.clicks()
.flatMap {
  RxJavaBridge.toV3Observable(
    permissions.request(Manifest
      .permission.WRITE_EXTERNAL_STORAGE)
  )
}
.filter { it }
.map { Unit }
saveObservable.subscribe { model.saveClicked() }
  .addTo(disposables)
readObservable.subscribe { model.readSaveClicked() }
  .addTo(disposables)

从外部存储读取

Now that you’re calling both the saveClicked and readSaveClicked methods, it’s time to update the WeatherViewModel to execute the read and save logic.

val readObservable = readSavedClicks
  .subscribeOn(AndroidSchedulers.mainThread())
  .flatMapMaybe { readLastWeather(filesDir) }
  .doOnNext { cityLiveData.postValue(it.cityName) }
  .map { WeatherApi.NetworkResult.Success(it) }
Observable
  .merge(locationObservable, textObservable, readObservable)

将天气写入外部存储

Saving the weather will be just as easy as reading the weather out of external storage. Add the following block to the bottom of the init method:

saveClicks
  // 1
  .filter { weatherLiveData.value != null }
  .map { weatherLiveData.value!! }
  // 2
  .flatMapCompletable {
    // 3
    it.save(filesDir)
      .doOnComplete {
        snackbarLiveData.postValue(
          "${weatherLiveData.value!!.cityName} weather saved"
        )
      }
  }
  .subscribe()
  .addTo(disposables)

对方向变化反应

rxpermissions. 是一个伟大的图书馆,但有一个大痛点留意。

.compose(permissions.ensure(
  Manifest.permission.ACCESS_FINE_LOCATION))
fun <T> Observable<T>.ensure(permission: String, rxPermissions: RxPermissions): Observable<Boolean> {
  return RxJavaBridge.toV2Observable(this)
    .compose(rxPermissions.ensure(permission))
    .`as`(RxJavaBridge.toV3Observable())
}
ensure(Manifest.permission.ACCESS_FINE_LOCATION, permissions)
.ensure(Manifest.permission.WRITE_EXTERNAL_STORAGE, permissions)

关键点

  • rxpermissions. 库提供了一种易于请求权限的机制。
  • 你可以链观测量你与其他观测量就像正常的图书馆里。
  • Keep in mind that the code requesting permissions has to be made in an initialization method (like onCreate or onStart).
  • Use ensure if you’re triggering the permission request off some other event.
  • Use request if you’re triggering the permission request as soon as the page loads.

然后去哪儿?

rxpermissions是Android社区拥有RX范例的另一个优秀示例。在下一章中,您将潜入另一个同样地拥抱RX的图书馆,但是,图书馆有趣是谷歌开发了库的事实。谷歌通过Jetpack组件支持Rxjava的决定应该是一个引人注目的论据,支持图书馆。当你准备好了,继续前进!

有一个技术问题?想报告一个错误吗? 你可以问在这本书的作者的问题和bug报告在我们的官方图书论坛 这里.

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解开这本书,我们的书籍和视频整个目录,具有raywenderlich.com专业订阅。

现在解锁

为了突出或做笔记,你需要自己的这本书在订阅或购买本身。