Anko 是一個 DSL (Domain-Specific Language), 它是JetBrains出品的,用 Kotlin 開發的安卓框架。它主要的目的是用來替代之前XML的方式來使用代碼生成UI佈局。html
先來看一個直觀的例子java
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="match_parent" android:layout_width="match_parent">
<EditText android:id="@+id/todo_title" android:layout_width="match_parent" android:layout_heigh="wrap_content" android:hint="@string/title_hint" />
<!-- Cannot directly add an inline click listener as onClick delegates implementation to the activity -->
<Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/add_todo" />
</LinearLayout>複製代碼
使用Anko以後,能夠用代碼實現佈局,而且button還綁定了點擊事件。react
verticalLayout {
var title = editText {
id = R.id.todo_title
hintResource = R.string.title_hint
}
button {
textResource = R.string.add_todo
onClick { view -> {
// do something here
title.text = "Foo"
}
}
}
}複製代碼
能夠看到 DSL 的一個主要優勢在於,它須要不多的時間便可理解和傳達某個領域的詳細信息。jquery
OkHttp是一個成熟且強大的網絡庫,在Android源碼中已經使用OkHttp替代原先的HttpURLConnection。不少著名的框架例如Picasso、Retrofit也使用OkHttp做爲底層框架。在這裏我對OkHttp作一下簡單的封裝,其實封裝得有點粗暴只是爲了演示如何實現dsl。android
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.schedulers.Schedulers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
import java.util.concurrent.TimeUnit
/** * Created by Tony Shen on 2017/6/1. */
class RequestWrapper {
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 onSuccess(onSuccess: (String) -> Unit) {
_success = onSuccess
}
fun onFail(onError: (Throwable) -> Unit) {
_fail = onError
}
}
fun http(init: RequestWrapper.() -> Unit) {
val wrap = RequestWrapper()
wrap.init()
executeForResult(wrap)
}
private fun executeForResult(wrap:RequestWrapper) {
Flowable.create<Response>({
e -> e.onNext(onExecute(wrap))
}, BackpressureStrategy.BUFFER)
.subscribeOn(Schedulers.io())
.subscribe(
{ resp ->
wrap._success(resp.body()!!.string())
},
{ e -> wrap._fail(e) })
}
private fun onExecute(wrap:RequestWrapper): Response? {
var req:Request? = null
when(wrap.method) {
"get","Get","GET" -> req =Request.Builder().url(wrap.url).build()
"post","Post","POST" -> req = Request.Builder().url(wrap.url).post(wrap.body).build()
"put","Put","PUT" -> req = Request.Builder().url(wrap.url).put(wrap.body).build()
"delete","Delete","DELETE" -> req = Request.Builder().url(wrap.url).delete(wrap.body).build()
}
val http = OkHttpClient.Builder().connectTimeout(wrap.timeout, TimeUnit.SECONDS).build()
val resp = http.newCall(req).execute()
return resp
}複製代碼
封裝完OkHttp以後,看看如何來編寫get請求git
http {
url = "http://www.163.com/"
method = "get"
onSuccess {
string -> L.i(string)
}
onFail {
e -> L.i(e.message)
}
}複製代碼
是否是很像之前用jquery來寫ajax?github
post請求也是相似的,只不過多了bodyajax
var json = JSONObject()
json.put("xxx","yyyy")
....
val postBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),json.toString())
http {
url = "https://......"
method = "post"
body = postBody
onSuccess {
string -> L.json(string)
}
onFail {
e -> L.i(e.message)
}
}複製代碼
cv4j 是咱們開發的實時圖像處理框架。最先使用濾鏡的方式以下:express
CV4JImage cv4jImage = new CV4JImage(bitmap);
CommonFilter filter = new NatureFilter();
Bitmap newBitMap = filter.filter(cv4jImage.getProcessor()).getImage().toBitmap();
image.setImageBitmap(newBitMap);複製代碼
後來增長了RxJava封裝的版本apache
RxImageData.bitmap(bitmap).addFilter(new NatureFilter()).into(image);複製代碼
如今Kotlin項目除了可使用上述兩種方式以外,還多了一種方式。
cv4j {
bitmap = BitmapFactory.decodeResource(resources, R.drawable.test_io)
filter = NatureFilter()
imageView = image
}複製代碼
這個dsl是如何封裝的呢?實際上是對RxJava版本進一步封裝。
/** * Copyright (c) 2017-present, CV4J Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
package com.cv4j.rxjava
import android.app.Dialog
import android.graphics.Bitmap
import android.widget.ImageView
import com.cv4j.core.datamodel.CV4JImage
import com.cv4j.core.filters.CommonFilter
/** * only for Kotlin code,this class provides the DSL style for cv4j */
class Wrapper {
var bitmap:Bitmap? = null
var cv4jImage: CV4JImage? = null
var bytes:ByteArray? = null
var useCache:Boolean = true
var imageView: ImageView? = null
var filter: CommonFilter? = null
var dialog: Dialog? = null
}
fun cv4j(init: Wrapper.() -> Unit) {
val wrap = Wrapper()
wrap.init()
render(wrap)
}
private fun render(wrap: Wrapper) {
if (wrap.bitmap!=null) {
if (wrap.filter!=null) {
RxImageData.bitmap(wrap.bitmap).dialog(wrap.dialog).addFilter(wrap.filter).isUseCache(wrap.useCache).into(wrap.imageView)
} else {
RxImageData.bitmap(wrap.bitmap).dialog(wrap.dialog).isUseCache(wrap.useCache).into(wrap.imageView)
}
} else if (wrap.cv4jImage!=null) {
if (wrap.filter!=null) {
RxImageData.image(wrap.cv4jImage).dialog(wrap.dialog).addFilter(wrap.filter).isUseCache(wrap.useCache).into(wrap.imageView)
} else {
RxImageData.image(wrap.cv4jImage).dialog(wrap.dialog).isUseCache(wrap.useCache).into(wrap.imageView)
}
} else if (wrap.bytes!=null) {
if (wrap.filter!=null) {
RxImageData.bytes(wrap.bytes).dialog(wrap.dialog).addFilter(wrap.filter).isUseCache(wrap.useCache).into(wrap.imageView)
} else {
RxImageData.bytes(wrap.bytes).dialog(wrap.dialog).isUseCache(wrap.useCache).into(wrap.imageView)
}
}
}複製代碼
來看一下程序的最終效果圖
cv4j 目前已經支持了幾十種濾鏡,固然除了濾鏡還有其餘功能,感興趣的童鞋能夠看咱們的源碼:)。
使用dsl的代碼風格,可讓程序更加直觀和簡潔。若是使用Kotlin來開發項目的話,徹底能夠嘗試一下。
公司的sdk項目我也考慮引入Kotlin,我已經寫了一個module用於封裝原先的sdk,這個module只適用於Kotlin項目。用於簡化初始化sdk和實現deep link的註冊服務。
能夠感覺一下,使用dsl是否是比原先的代碼更加簡潔和直觀呢?
另外,衆所周知的Gradle也是基於DSL的Java構建工具。
參考資料: