自从成为Android开发受官方支持的语言以来,Kotlin在Android开发人员中Swift普及,Google称使用Kotlin创建的应用程序增长了6倍 。
如果您以前使用过RxJava或RxAndroid,并且想切换到Kotlin,或者想开始使用Kotlin进行反应式编程,那么本教程适合您。 我们将介绍在Kotlin中创建RxJava 2.0 Observers
, Observables
和数据流的要点,然后研究如何通过将RxJava与Kotlin扩展功能相结合来削减项目中的大量样板代码。
将RxJava与Kotlin结合使用可以帮助您以更少的代码创建具有高度响应性的应用程序,但是没有哪种编程语言是完美的,因此,我还将分享许多开发人员在刚开始将Kotlin与RxJava 2.0一起使用时遇到的SAM转换问题的解决方法。
总结一下,我们将创建一个应用程序,演示如何使用RxJava解决现实生活中的Android项目中遇到的一些问题。
如果这是您对RxJava的初次体验,那么在此过程中,我还将提供理解RxJava核心概念所需的所有背景信息。 即使您以前从未尝试过RxJava,到本文结尾,您也将对如何在项目中使用此库有深入的了解, 并且您将使用RxJava,RxKotlin,RxAndroid创建一些可运行的应用程序和RxBinding。
无论如何,什么是RxJava?
RxJava是ReactiveX库的开源实现,可帮助您以反应式编程风格创建应用程序。 尽管RxJava旨在处理同步和异步数据流,但它并不限于“传统”数据类型。 RxJava对“数据”的定义非常广泛,包括诸如缓存,变量,属性,甚至是用户输入事件(如单击和滑动)之类的东西。 仅仅因为您的应用程序不处理大量数据或执行复杂的数据转换,并不意味着它不能从RxJava中受益!
那么RxJava如何工作?
RxJava扩展了基于Observers和Observables概念的Observer软件设计模式。 要创建基本的RxJava数据管道,您需要:
- 创建一个可观察的。
- 给Observable发出一些数据。
- 创建一个观察者。
- 将观察者订阅到可观察者。
一旦可观察对象至少具有一个观察者,它将开始发出数据。 每次Observable发出一条数据时,它将通过调用onNext()
方法通知其分配的Observer,然后Observer通常将响应此数据发出执行某些操作。 一旦Observable完成发送数据,它将通过调用onComplete()
通知Observer。 然后,Observable将终止,并且数据流将结束。
如果发生异常,则将调用onError()
,并且Observable将立即终止,而不会发出更多数据或调用onComplete()
。
但是RxJava 不仅仅是将数据从Observable传递给Observer! RxJava有大量的运算符集合,可用于过滤,合并和转换这些数据。 例如,假设您的应用程序具有检测“ onClick
事件的“ 立即付款”按钮,并且您担心不耐烦的用户可能多次点击该按钮,从而导致您的应用程序处理多次付款。
RxJava使您可以将这些onClick
事件转换为数据流,然后可以使用RxJava的各种运算符对其进行操作。 在此特定示例中,您可以使用debounce()
运算符过滤快速连续发生的数据发射,因此,即使用户不喜欢“ 立即付款”按钮,您的应用程序也只会注册一次付款。
使用RxJava有什么好处?
我们已经看到RxJava如何在特定的应用程序中帮助您解决特定的问题,但是,它通常提供Android项目什么?
RxJava通过提供一种编写所需实现内容的方法,而不是编写应用程序必须执行的指令列表的方式,可以帮助简化代码。 例如,如果您想忽略在同一500毫秒内发生的所有数据发射,那么您应该编写:
.debounce(500, TimeUnit.MILLISECONDS)
另外,由于RxJava 几乎将所有内容都视为数据,因此它提供了一个模板,可以将其应用于各种事件:创建一个Observable,创建一个Observer,将Observer订阅到Observable,冲洗并重复。 这种公式化的方法可以使代码更加直观易懂。
Android开发人员的另一个主要好处是RxJava可以减轻Android多线程带来的痛苦。 当今的移动用户希望他们的应用程序能够执行多任务,即使这就像在后台下载数据同时又对用户输入保持响应一样简单。
Android具有用于创建和管理多个线程的多个内置解决方案,但是这些解决方案都不是特别容易实现的,它们可以快速生成复杂,冗长的代码,这些代码难以阅读且容易出错。
在RxJava中,您可以结合使用运算符和调度程序来创建和管理其他线程。 您可以使用subscribeOn
运算符和调度程序轻松更改执行工作的线程。 例如,在这里我们正在安排要在新线程上执行的工作:
.subscribeOn(Schedulers.newThread())
您可以使用observeOn
运算符指定将工作结果发布到observeOn
。 在这里,我们使用AndroidSchedulers.mainThread
调度程序将结果发布到Android的所有重要主UI线程中,该调度程序可作为RxAndroid库的一部分使用:
.observeOn(AndroidSchedulers.mainThread())
相比于Android的内置多线程解决方案,RxJava的做法是更简洁,更容易理解。
同样,您可以在我的Android RxJava 2入门文章中了解有关RxJava的工作原理以及将该库添加到项目中的好处的更多信息。
我应该使用RxJava还是RxKotlin?
由于Kotlin与Java可以100%互操作,因此您可以毫无困难地在Kotlin项目中使用大多数Java库-RxJava库也不例外。
有一个专门的RxKotlin库 ,这大约是普通RxJava库Kotlin包装。 该包装器提供了扩展,可以针对Kotlin环境优化RxJava,并可以进一步减少您需要编写的样板代码。
由于您可以在Kotlin中使用RxJava而不需要RxKotlin,因此,除非另有说明,否则我们将在本文中始终使用RxJava。
在Kotlin中创建简单的观察者和可观察对象
观察者和可观察者是RxJava的构建块,所以让我们开始创建:
- 一个简单的Observable,它响应按钮单击事件而发出短数据流。
- 一个Observable,通过向Android Studio的Logcat打印不同的消息来对此数据做出反应。
使用您选择的设置创建一个新项目,但是请确保在出现提示时选中包括Kotlin支持复选框。 接下来,打开项目的build.gradle文件,并将RxJava库添加为项目依赖项:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.0-alpha1'
implementation 'androidx.constraintlayout:constraintlayout:1.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
}
然后,打开项目的activity_main.xml文件,并添加将启动数据流的按钮:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start RxJava stream" />
</LinearLayout>
创建Observable的方法有多种,但是最简单的方法之一是使用just()
运算符将对象或对象列表转换为Observable。
在下面的代码中,我们将创建一个Observable( myObservable
)并为其指定要发射的项目1、2、3、4和5。 我们还创建了一个Observer( myObserver
),将其订阅到myObservable
,然后让它在每次收到新的发射时向Logcat打印一条消息。
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private var TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Start the stream when the button is clicked//
button.setOnClickListener { startRStream() }
}
private fun startRStream() {
//Create an Observable//
val myObservable = getObservable()
//Create an Observer//
val myObserver = getObserver()
//Subscribe myObserver to myObservable//
myObservable
.subscribe(myObserver)
}
private fun getObserver(): Observer<String> {
return object : Observer<String> {
override fun onSubscribe(d: Disposable) {
}
//Every time onNext is called, print the value to Android Studio’s Logcat//
override fun onNext(s: String) {
Log.d(TAG, "onNext: $s")
}
//Called if an exception is thrown//
override fun onError(e: Throwable) {
Log.e(TAG, "onError: " + e.message)
}
//When onComplete is called, print the following to Logcat//
override fun onComplete() {
Log.d(TAG, "onComplete")
}
}
}
//Give myObservable some data to emit//
private fun getObservable(): Observable<String> {
return Observable.just("1", "2", "3", "4", "5")
}
}
您现在可以将该应用程序进行测试:
- 在物理Android智能手机或平板电脑或Android虚拟设备(AVD)上安装项目。
- 单击“ 启动RxJava流”按钮。
- 打开Android Studio的Logcat Monitor,方法是选择“ Android Monitor”选项卡(光标位于以下屏幕截图中),然后选择“ Logcat”选项卡。
此时,Observable将开始发出其数据,Observer将其消息打印到Logcat。 您的Logcat输出应如下所示:
如果您想自己尝试,可以从GitHub下载此项目 。
用于RxJava的Kotlin扩展
现在,我们已经了解了如何在Kotlin中建立简单的RxJava管道,让我们看看如何使用RxKotlin的扩展功能以更少的代码实现这一目标。
要使用RxKotlin库,您需要将其添加为项目依赖项:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.0-alpha1'
implementation 'androidx.constraintlayout:constraintlayout:1.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
//Add the following//
implementation 'io.reactivex.rxjava2:rxkotlin:2.2.0'
}
在下面的示例中,我们使用RxKotlin的toObservable()
扩展函数将List
转换为Observable。 我们还使用了subscribeBy()
扩展功能,因为它允许我们使用命名参数构造一个Observer,这将产生更清晰的代码。
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import io.reactivex.rxkotlin.subscribeBy
import io.reactivex.rxkotlin.toObservable
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Start the stream when the button is clicked//
button.setOnClickListener { startRStream() }
}
private fun startRStream() {
val list = listOf("1", "2", "3", "4", "5")
//Apply the toObservable() extension function//
list.toObservable()
//Construct your Observer using the subscribeBy() extension function//
.subscribeBy(
onNext = { println(it) },
onError = { it.printStackTrace() },
onComplete = { println("onComplete!") }
)
}
}
这是您应该看到的输出:
解决RxJava的SAM歧义问题
RxKotlin还为SAM转换问题提供了重要的解决方法,当给定的Java方法上有多个SAM参数重载时,可能会发生此问题 。 这种SAM歧义性使Kotlin编译器感到困惑,因为它无法确定应该转换的接口,因此您的项目将无法编译。
当将RxJava 2.0与Kotlin一起使用时,这种SAM模糊性是一个特殊的问题,因为许多RxJava运算符都采用多种SAM兼容类型。
让我们看看实际的SAM转换问题。 在下面的代码中,我们使用zip()
运算符组合两个Observable的输出:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import io.reactivex.Observable
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Start the stream when the button is clicked//
button.setOnClickListener { startRStream() }
}
private fun startRStream() {
val numbers = Observable.range(1, 6)
val strings = Observable.just("One", "Two", "Three",
"Four", "Five", "Six" )
val zipped = Observable.zip(strings, numbers) { s, n -> "$s $n" }
zipped.subscribe(::println)
}
}
这将导致Kotlin编译器抛出类型推断错误。 但是,RxKotlin为受影响的运算符提供了辅助方法和扩展功能,包括我们在以下代码中使用的Observables.zip()
:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import io.reactivex.Observable
import io.reactivex.rxkotlin.Observables
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Start the stream when the button is clicked//
button.setOnClickListener { startRStream() }
}
private fun startRStream() {
val numbers = Observable.range(1, 6)
val strings = Observable.just("One", "Two", "Three",
"Four", "Five", "Six" )
val zipped = Observables.zip(strings, numbers) { s, n -> "$s $n" }
zipped.subscribe(::println)
}
}
这是此代码的输出:
结论
在本教程中,我向您展示了如何在Kotlin项目中开始使用RxJava库,包括使用许多其他支持库,例如RxKotlin和RxBinding。 我们研究了如何使用扩展功能在Kotlin中创建简单的Observers和Observables,一直到针对Kotlin平台优化RxJava。
到目前为止,我们已经使用RxJava创建了简单的Observable来发射数据,并且使用Observer将这些数据打印到Android Studio的Logcat中-但这不是在现实世界中使用RxJava的方式!
在下一篇文章中,我们将研究RxJava如何帮助解决开发Android应用程序时遇到的现实问题。 我们将RxJava与Kotlin结合使用来创建经典的“ 注册”屏幕。
翻译自: https://code.tutsplus.com/tutorials/kotlin-reactive-programming-with-rxjava-and-rxkotlin--cms-31577