https://github.com/ReactiveX/...html
RxKotlin: RxJava bindings for Kotlinreact
咱们如今已经基本知道 Kotlin 中 DSL 的样子了。可是这些 DSL 都是怎样实现的呢?本节咱们就经过实现一个极简的http DSL来学习建立 DSL 背后的基本原理。jquery
在这里咱们对 OkHttp 作一下简单的封装,实现一个相似 jquery 中的 Ajax 的 http 请求的DSL。git
OkHttp 是一个成熟且强大的网络库,在Android源码中已经使用OkHttp替代原先的HttpURLConnection。不少著名的框架例如Picasso、Retrofit也使用OkHttp做为底层框架。github
提示: 更多关于OkHttp 的使用可参考: http://square.github.io/okhttp/ajax
咱们首先使用 IDEA 建立 Kotlin Gradle 项目编程
而后,在 build.gradle 里面配置依赖json
compile 'com.github.ReactiveX:RxKotlin:2.1.0' compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.8.1' compile group: 'com.alibaba', name: 'fastjson', version: '1.2.35'
其中,RxKotlin是ReactiveX 框架对 Kotlin 语言的支持库。咱们这里主要用RxKotlin来进行请求回调的异步处理。网络
咱们使用的是 'com.github.ReactiveX:RxKotlin:2.1.0' , 这个库是在 https://jitpack.io 上,因此咱们在repositories配置里添加 jitpack 仓库架构
repositories { maven { url 'https://jitpack.io' } ... }
ReactiveX是Reactive Extensions的缩写,通常简写为Rx,最初是LINQ的一个扩展,由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源。
Rx扩展了观察者模式用于支持数据和事件序列。Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步I/O(非阻塞)数据流。
Rx库支持.NET、JavaScript和C++ 。Rx近几年愈来愈流行,如今已经支持几乎所有的流行编程语言了。一个语言列表以下所示:
Rx的大部分语言库由ReactiveX这个组织负责维护。Rx 比较流行的库有RxJava/RxJS/Rx.NET等,固然将来RxKotlin也必将更加流行。
提示: Rx 的社区网站是: http://reactivex.io/ 。 Github 地址:https://github.com/ReactiveX/
首先咱们设计Http请求对象封装类以下
class HttpRequestWrapper { var url: String? = null var method: String? = null var body: RequestBody? = null var timeout: Long = 10 internal var success: (String) -> Unit = {} internal var fail: (Throwable) -> Unit = {} fun success(onSuccess: (String) -> Unit) { success = onSuccess } fun error(onError: (Throwable) -> Unit) { fail = onError } }
HttpRequestWrapper的成员变量和函数说明以下表
成员 | 说明 |
---|---|
url | 请求 url |
method | 请求方法,例如 Get、Post 等,不区分大小写 |
body | 请求头,为了简单起见咱们直接使用 OkHttp的RequestBody类型 |
timeout | 超时时间ms,咱们设置了默认值是10s |
success | 请求成功的函数变量 |
fail | 请求失败的函数变量 |
fun success(onSuccess: (String) -> Unit) | 请求成功回调函数 |
fun error(onError: (Throwable) -> Unit) | 请求失败回调函数 |
咱们直接调用 OkHttp 的 Http 请求 API
private fun call(wrap: HttpRequestWrapper): Response { var req: Request? = null when (wrap.method?.toLowerCase()) { "get" -> req = Request.Builder().url(wrap.url).build() "post" -> req = Request.Builder().url(wrap.url).post(wrap.body).build() "put" -> req = Request.Builder().url(wrap.url).put(wrap.body).build() "delete" -> req = Request.Builder().url(wrap.url).delete(wrap.body).build() } val http = OkHttpClient.Builder().connectTimeout(wrap.timeout, TimeUnit.MILLISECONDS).build() val resp = http.newCall(req).execute() return resp }
它返回请求的响应对象Response。
咱们在OkHttpClient.Builder().connectTimeout(wrap.timeout, TimeUnit.MILLISECONDS).build()
中设置超时时间的单位是 TimeUnit.MILLISECONDS
。
咱们经过wrap.method?.toLowerCase()
处理请求方法的大小写的兼容。
咱们首先新建一个数据发射源:一个可观察对象(Observable),做为发射数据用
val sender = Observable.create<Response>({ e -> e.onNext(call(wrap)) })
其中,e 的类型是 io.reactivex.Emitter
(发射器),它的接口定义是
public interface Emitter<T> { void onNext(@NonNull T value); void onError(@NonNull Throwable error); void onComplete(); }
其方法功能简单说明以下:
方法 | 功能 |
---|---|
onNext | 发射一个正常值数据(value) |
onError | 发射一个Throwable异常 |
onComplete | 发射一个完成的信号 |
这里,咱们经过调用onNext方法,把 OkHttp 请求以后的响应对象Response 做为正常值发射出去。
而后咱们再建立一个数据接收源:一个观察者(Observer)
val receiver: Observer<Response> = object : Observer<Response> { override fun onNext(resp: Response) { wrap.success(resp.body()!!.string()) } override fun onError(e: Throwable) { wrap.fail(e) } override fun onSubscribe(d: Disposable) { } override fun onComplete() { } }
receiver 的 onNext 函数接收 sender 发射过来的数据 Response, 而后咱们在函数体内,调用这个响应对象,给 wrap.success 回调函数进行相关的赋值操做。一样的,onError 函数中也执行相应的赋值操做。
最后,经过 subscribe 订阅函数来绑定 sender 与 receiver 的关联:
sender.subscribe(receiver)
做为接收数据的 receiver (也就是 观察者 (Observer) ),对发送数据的 sender (也就是可被观察对象( Observable)) 所发射的数据或数据序列做出响应。
这种模式能够极大地简化并发操做,由于它建立了一个处于待命状态的观察者,在将来某个时刻响应 sender 的通知,而不须要阻塞等待 sender 发射数据。这个很像协程中的通道编程模型。
咱们的ajax DSL主函数设计以下:
fun ajax(init: HttpRequestWrapper.() -> Unit) { val wrap = HttpRequestWrapper() wrap.init() doCall(wrap) }
其中,参数init: HttpRequestWrapper.() -> Unit
是一个带接收者的函数字面量,它的类型是init = Function1<com.kotlin.easy.HttpRequestWrapper, kotlin.Unit>
。 HttpRequestWrapper是扩展函数 init()
的接收者,点号 .
是扩展函数修饰符。
咱们在函数体内直接调用了这个函数字面量 wrap.init()
。这样的写法可能比较难以理解,这个函数字面量 init 的调用其实是 init.invoke(wrap)
,就是把传入 ajax 的函数参数直接传递给 wrap 。为了更简单的理解这个 init 函数的工做原理,咱们经过把上面的 ajax 函数的代码反编译成对应的 Java 代码以下:
public static final void ajax(@NotNull Function1 init) { Intrinsics.checkParameterIsNotNull(init, "init"); HttpRequestWrapper wrap = new HttpRequestWrapper(); init.invoke(wrap); doCall(wrap); }
也就是说,ajax 函数的一个更容易理解的写法是
fun ajax(init: HttpRequestWrapper.() -> Unit) { val wrap = HttpRequestWrapper() init.invoke(wrap) doCall(wrap) }
咱们在实际应用的时候,能够直接把 init 写成Lambda 表达式的形式,由于接收者类型HttpRequestWrapper 能够从上下文推断出来。
咱们这样调用 ajax 函数:
ajax { url = testUrl method = "get" success { string -> println(string) Assert.assertTrue(string.contains("百度一下")) } error { e -> println(e.message) } }
下面是几个测试代码示例:
package com.kotlin.easy import com.alibaba.fastjson.JSONObject import okhttp3.MediaType import okhttp3.RequestBody import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 /** * Created by jack on 2017/7/23. */ @RunWith(JUnit4::class) class KAjaxTest { @Test fun testHttpOnSuccess() { val testUrl = "https://www.baidu.com" ajax { url = testUrl method = "get" success { string -> println(string) Assert.assertTrue(string.contains("百度一下")) } error { e -> println(e.message) } } } @Test fun testHttpOnError() { val testUrl = "https://www2.baidu.com" ajax { url = testUrl method = "get" success { string -> println(string) } error { e -> println(e.message) Assert.assertTrue("connect timed out" == e.message) } } } @Test fun testHttpPost() { var json = JSONObject() json.put("name", "Kotlin DSL Http") json.put("owner", "Kotlin") val postBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json.toString()) ajax { url = "saveArticle" method = "post" body = postBody success { string -> println(string) } error { e -> println(e.message) } } } @Test fun testLambda() { val testUrl = "https://www.baidu.com" val init: HttpRequestWrapper.() -> Unit = { this.url = testUrl this.method = "get" this.success { string -> println(string) Assert.assertTrue(string.contains("百度一下")) } this.error { e -> println(e.message) } } ajax(init) }
到这里,咱们已经完成了一个极简的 Kotlin Ajax DSL。
本节工程源码: https://github.com/EasyKotlin...
相比于Java,Kotlin对函数式编程的支持更加友好。Kotlin 的扩展函数和高阶函数(Lambda 表达式),为定义Kotlin DSL提供了核心的特性支持。
使用DSL的代码风格,可让咱们的程序更加直观易懂、简洁优雅。若是使用Kotlin来开发项目的话,咱们彻底能够去尝试一下。
点击这里 > 去京东商城购买阅读
点击这里 > 去天猫商城购买阅读