关于Kotlin
kotlin关键字:
object:定义静态类 lazy:懒属性(延迟加载) when:用于判断,相当于java中的switch()语句 try{…}catch(){…}:用于捕捉异常 let:默认当前这个对象作为闭包的it参数,返回值是函数里面最后一行,或者指定return apply:调用某对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象 with:一个单独的函数,并不是Kotlin中的extension,所以调用方式有点不一样,返回是最后一行, 然后可以直接调用对象的方法,感觉像是let和apply的结合 constructor:用于标识次级构造函数,解决重载 init:因为kotlin中的类定义同时也是构造函数,这个时候是不能进行操作的, 所以kotlin增加了一个新的关键字init用来处理类的初始化问题,init模块中的内容可以直接使用构造函数的参数 open:由于kotlin中所有类和方法默认都是final的,不能直接继承或重写,需要继承的类或类中要重写的方法都应当在定义时添加open关键字 abstract:描述一个抽象类,抽象类里的方法如果不提供实现则需要用abstract关键字来描述抽象方法.抽象的方法默认是open的 companion object: 伴随对象, 修饰静态方法 is:等同于Java中的instanceof
- kotlin静态代码块写法(lambda表达式)
companion object {
private val TAG = MainActivity.javaClass.name
init {
RxJavaPlugins.setErrorHandler { throwable ->
if (throwable is InterruptedIOException) {
Log.d(TAG, "Io interrupted")
}
}
}
}
关于RxJava
事件流机制
用两根水管代替观察者(产生事件的上游)和被观察者(接收事件的下游),通过一定方式建立连接
/**
* RxJava链式操作
*/
fun connect() {
//创建一个上游Observable(lambda表达式写法)
Observable.create(ObservableOnSubscribe<Int> { emitter ->
//ObservableEmitter:用来发出事件的,它可以分别发出next、complete和error三种类型的事件
/**
* 发送规则:
* <ol>
* <li> 上游可以发送无限个onNext, 下游也可以接收无限个onNext.
* <li> 当上游发送了一个onComplete后, 上游onComplete之后的事件将会继续发送, 而下游收到onComplete事件之后将不再继续接收事件.
* <li> 当上游发送了一个onError后, 上游onError之后的事件将继续发送, 而下游收到onError事件之后将不再继续接收事件.
* <li> 上游可以不发送onComplete或onError.
* <li> 最为关键的是onComplete和onError必须唯一并且互斥(需要代码自行控制)
* </ol>
*/
emitter.onNext(1)
emitter.onNext(2)
emitter.onNext(3)
emitter.onComplete()
//建立连接
/**
* subscribe()有多个重载的方法:
* <ol>
* <li> public final Disposable subscribe() {}
* <li> public final Disposable subscribe(Consumer<? super T> onNext) {}
* <li> public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {}
* <li> public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete) {}
* <li> public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe) {}
* <li> public final void subscribe(Observer<? super T> observer) {}
* <li> 不带任何参数的subscribe() 表示下游不关心任何事件,你上游尽管发你的数据去吧, 老子可不管你发什么.
* <li> 带有一个Consumer参数的方法表示下游只关心onNext事件, 其他的事件我假装没看见.
* </ol>
*/
}).subscribe(object : Observer<Int> {
//创建一个下游 Observer
//两根水管之间的一个开关
private var mDisposable: Disposable? = null
private var i: Int = 0
override fun onSubscribe(d: Disposable) {
Log.d(TAG, "subscribe")
mDisposable = d
}
override fun onNext(value: Int?) {
Log.d(TAG, "next value = $value")
i++
if (i == 2) {
Log.d(TAG, "dispose")
//调用dispose()并不会导致上游不再继续发送事件, 上游会继续发送剩余的事件
mDisposable!!.dispose()
Log.d(TAG, "isDisposed : " + mDisposable!!.isDisposed)
}
}
override fun onError(e: Throwable) {
Log.d(TAG, "error")
}
override fun onComplete() {
Log.d(TAG, "complete")
}
})
}
多线程实现事件异步操作
在RxJava中, 已经内置了很多线程选项供我们选择, 例如:
- Schedulers.io() 代表io操作的线程, 通常用于网络,读写文件等io密集型的操作
- Schedulers.computation() 代表CPU计算密集型的操作, 例如需要大量计算的操作
- Schedulers.newThread() 代表一个常规的新线程
- AndroidSchedulers.mainThread() 代表Android的主线程
fun switchThread() {
val observable = Observable.create(ObservableOnSubscribe<Int> { emitter ->
Log.d(TAG, "Observable thread is : " + Thread.currentThread().name)
Log.d(TAG, "emitter 1")
emitter.onNext(1)
})
val consumer = Consumer<Int> { integer ->
Log.d(TAG, "Observer thread is :" + Thread.currentThread().name)
Log.d(TAG, "onNext: " + integer!!)
}
/**
* 1. subscribeOn() 指定的是上游发送事件的线程
* 2. observeOn() 指定的是下游接收事件的线程
* 3. 多次指定上游的线程只有第一次指定的有效
* 4. 多次指定下游的线程是可以的, 每调用一次observeOn(), 下游的线程就会切换一次
*
*/
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(consumer)
}
Map操作符
Map是RxJava中最简单的一个变换操作符,它的作用就是对上游发送的每一个事件应用一个函数, 使得每一个事件都按照指定的函数去变化。
- 通过Map,可以将上游发来的事件转换为任意的类型,可以是一个Object,也可以是一个集合
- FlatMap将一个发送事件的上游Observable变换为多个发送事件的Observables,然后将它们发射的事件合并后放进一个单独的Observable里,但是不保证事件的顺序
- concatMap它和flatMap的作用几乎一模一样, 只是它的结果是严格按照上游发送的顺序来发送的
/**
* 嵌套的网络请求, 首先需要去请求注册, 待注册成功回调了再去请求登录的接口
* 登录和注册返回的都是一个上游Observable, flatMap操作符的作用就是把一个Observable转换为另一个Observable
*/
fun nestedNetWork(context: Context) {
val api = RetrofitProvider.get().create(Api::class.java)
api.register(RegisterRequestBean("zhangsan", 0)) //发起注册请求
.subscribeOn(Schedulers.io()) //在IO线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求注册结果
.doOnNext {
//先根据注册的响应结果去做一些操作
}
.observeOn(Schedulers.io()) //回到IO线程去发起登录请求
.flatMap { api.login(LoginRequestBean("zhangsan", 0)) }
.observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求登录的结果
.subscribe({
Toast.makeText(context, "登录成功", Toast.LENGTH_SHORT).show()
}, {
Toast.makeText(context, "登录失败", Toast.LENGTH_SHORT).show()
})
}
Zip操作符
Zip通过一个函数将多个Observable发送的事件结合到一起,然后发送这些组合到一起的事件。它按照严格的顺序应用这个函数。它只发射与发射数据项最少的那个Observable一样多的数据。
/**
* 一个界面需要展示用户的一些信息, 而这些信息分别要从两个服务器接口中获取, 而只有当两个都获取到了之后才能进行展示
*/
fun getUserInfo() {
val api = RetrofitProvider.get().create(Api::class.java)
val observable1 = api.getUserBaseInfo(UserBaseInfoRequest("zhangsan", 0)).subscribeOn(Schedulers.io())
val observable2 = api.getUserExtraInfo(UserExtraInfoRequest(24, "man")).subscribeOn(Schedulers.io())
Observable.zip(observable1, observable2,
BiFunction<UserBaseInfoResponse, UserExtraInfoResponse, UserInfo> { baseInfo, extraInfo ->
UserInfo(baseInfo, extraInfo)
}).observeOn(AndroidSchedulers.mainThread())
.subscribe {
//do something
}
}
Flowable操作符
解决上下游流速不均衡常规思路:
扫描二维码关注公众号,回复: 169459 查看本文章1)从数量上进行治理, 减少发送进水缸里的事件,但是过滤事件会导致事件丢失
2)从速度上进行治理, 减缓事件发送进水缸的速度,但是减速又可能导致性能损失
同步和异步的区别仅仅在于是否有缓存池
- 大数据流用Flowable,小数据流用Observable
- 响应式拉取
- request当成下游处理事件的能力, 下游能处理几个就告诉上游我要几个
- Flowable里默认有一个大小为128的水缸, 当上下游工作在不同的线程中时, 上游就会先把事件发送到这个水缸中
- BackpressureStrategy.BUFFER无大小限制
- BackpressureStrategy.ERROR会在出现上下游流速不均衡的时候直接抛出MissingBackpressureException异常
- BackpressureStrategy.DROP直接把存不下的事件丢弃
- BackpressureStrategy.LATEST只保留最新的事件
- 不是自己创建的Flowable:
1) onBackpressureBuffer()
2) onBackpressureDrop()
3) onBackpressureLatest() - 当上下游在同一个线程中的时候,在下游调用request(n)就会直接改变上游中的requested的值,多次调用便会叠加这个值,而上游每发送一个事件之后便会去减少这个值,当这个值减少至0的时候,继续发送事件便会抛异常了
- 当上下游工作在不同的线程里时,每一个线程里都有一个requested,而我们调用request()时,实际上改变的是下游主线程中的requested,而上游中的requested的值是由RxJava内部调用request(n)去设置的,这个调用会在合适的时候自动触发
object FlowableUse {
private val TAG = FlowableUse.javaClass.name
var mSubscription: Subscription? = null
/**
* 读取一个文本文件,需要一行一行读取,然后处理并输出,
* 如果文本文件很大的时候,比如几十M的时候,全部先读入内存肯定不是明智的做法,因此我们可以一边读取一边处理
*/
fun readFile() {
Flowable.create(FlowableOnSubscribe<String> { emitter ->
try {
val reader = FileReader("test.txt")
val br = BufferedReader(reader)
val str = br.readLine()
while (str != null && !emitter.isCancelled) {
while (emitter.requested() == 0L) {
if (emitter.isCancelled) {
break
}
}
emitter.onNext(str)
}
br.close()
reader.close()
emitter.onComplete()
} catch (e: Exception) {
emitter.onError(e)
}
}, BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.subscribe(object : Subscriber<String> {
override fun onSubscribe(s: Subscription) {
mSubscription = s
s.request(1)
}
override fun onNext(string: String) {
Log.d(TAG, string)
try {
Thread.sleep(2000)
mSubscription?.request(1)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
override fun onError(t: Throwable) {}
override fun onComplete() {}
})
}
}