前言
最近Kotlin的呼聲又是日益高漲,前幾天9012年Google IO正式將Kotlin從first _class提升爲kotlin_first。我也是接觸了一段時間的Kotlin,給我的感覺就是簡約,快速。無需繁瑣的findId,高階函數的應用,再加上Kotlin的null 安全,更是將代碼的崩潰率降到更低。
今天我們就來介紹一下今天的主角—Anko
1.Anko
Anko是JetBrains開發的一個強大的庫,說起JetBrains ,那就牛逼了,Kotlin語言是他們開發的,最流行的的開發工具intellij idea都是他們開發的,AS也是基於IDEA的。好了,言歸正傳,Anko是Kotlin官方開發的一個讓開發Android應用更快速更簡單的Kotlin庫,並且能讓我們書寫的代碼更簡單清楚更容易閱讀。它包括多個部分
- Anko Commons: a lightweight library full of helpers for intents, dialogs, logging and so on;
- Anko Layouts: a fast and type-safe way to write dynamic Android layouts;
- Anko SQLite: a query DSL and parser collection for Android SQLite;
- Anko Coroutines: utilities based on the kotlinx.coroutines library.
1.1 如何使用
添加依賴
dependencies {
implementation "org.jetbrains.anko:anko:$anko_version"
}
這裏麪包括上面四個部分,當然你也可以只依賴一個部分,如下:
dependencies {
// Anko Commons
implementation "org.jetbrains.anko:anko-commons:$anko_version"
// Anko Layouts
implementation "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also available
implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
// Coroutine listeners for Anko Layouts
implementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
implementation "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"
// Anko SQLite
implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
}
下面我們分別介紹這幾個功能。
2 AnkoCommons
AnkoCommons對Android開發者來說是一個工具集,包括但不限於下面這幾個
- Intents
- Dialogs and toasts
- Logging
- Resources and dimensions
2.1 Intents
前面已經提到,Commons 庫是一個工具集,那Intents主要是幫助簡化Activity之間的跳轉。
傳統的 Kotlin 啓動新的 Activity 的方式是創建一個 Intent,同時可能傳遞一些參數,最後將創建的 Intent 通過 Context 的 startActivity() 方法傳遞,就像這樣:
val intent = Intent(this, SomeOtherActivity::class.java)
intent.putExtra("id", 5)
intent.setFlag(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(intent)
然鵝你用Anko只需要這樣:
startActivity(intentFor(“id” to 5).singleTop())
如果想要傳遞多個參數,你也可以這樣
startActivity<SomeOtherActivity>(
"id" to 5,
"city" to "Denpasar"
)
當然還有一些關於Intent的其它操作,如:撥打電話等:如下
2.2 Dialogs and toasts
這個庫主要是用來快速搭建Dialog和toast,具體包含以下幾個
- Toast
- SnackBar
- Alert (Dialog)
- Selectors
- Progress dialogs
2.2.1 Toast
Anko爲我們提供了更加簡單的Toast使用,只需要一行代碼即可實現
toast("Hi there!")
toast(R.string.message)
longToast("Wow, such duration")
2.2.2 SnackBars
SnackBar是 Android Support Library 22.2.0 裏面新增提供的一個控件,我們可以簡單的把它理解成一個加強版的Toast,或者是一個輕量級的Dialog。
我們可以用下面代碼快速創建snackbar。
view.snackbar("Hi there!")
view.snackbar(R.string.message)
view.longSnackbar("Wow, such duration")
view.snackbar("Action, reaction", "Click me!") { doStuff() }
這裏需要傳入view對象,這個可以是佈局中的任意一個view對象。
2.2.3 Alerts
Anko Alerts主要包括以下幾個功能:
- Android 默認dialog
- Android Appcompat 中AlertDialog
- 自定義Dialog
1.Android 默認dialog
通過以下代碼就可以構建一個可以交互的Android 默認dialog。
alert("Hi, I'm Roy", "Have you tried turning it off and on again?") {
yesButton { toast("Oh…") }
noButton {}
}.show()
代碼比較簡單,就不做解釋。
2.Android Appcompat 中AlertDialog
另外Anko還提供了Appcompat的AlertDialog實現方式,如下:
alert(Appcompat, "Some text message").show()
3.自定義Dialog
什麼,不能自定義dialog嗎?怎麼會,自定義dialog也是非常的簡單
alert {
customView {
editText()
}
}.show()
2.2.4 Selectors (包含列表的Dialog)
我們平時創建列表Dialog是這樣的:
val listItems = arrayOf("Russia", "USA", "Japan", "Australia") //傳數組
val listDialog: AlertDialog.Builder = AlertDialog.Builder(this)
listDialog.setItems(listItems) { p0, p1 ->
toast(p1)
}
val dialog: AlertDialog = listDialog.create()
dialog.show()
val window: Window = dialog.window
val params: WindowManager.LayoutParams = window.attributes
params.y = 45 * ScreenUtils.getScreenDensity().toInt()
params.gravity = Gravity.TOP or Gravity.RIGHT
params.width = ScreenUtils.getScreenWidth() / 2
params.height = ViewGroup.LayoutParams.WRAP_CONTENT
window.attributes = params
但是我們用Anko是這樣的:
val countries = listOf("Russia", "USA", "Japan", "Australia") //傳list
selector("Where are you from?", countries, { dialogInterface, i ->
toast("So you're living in ${countries[i]}, right?")
})
看起來只是簡化了dialog的創建過程。
2.2.5 Progress dialogs
不顯示進度的 Loading Dialg
pressDialog("Please wait a minute.", "Downloading…")
indeterminateProgressDialog("Fetching the data…")
2.3 Logging
打印log輔助工具。
Android SDK 提供 android.util.Log 類來提供一些 logging 方法,,這些方法都很實用,但是我們每次必須傳遞一個 Tag 參數,同時這個 Tag 信息必須是 String 類型的,這就略顯麻煩。不過現在我們可以通過 AnkoLogger 類擺脫這些惱人的問題:
class SomeActivity : Activity(), AnkoLogger {
fun someMethod() {
info("Info message")
debug(42) // .toString() method will be called automatically
}
}
默認的 Tag 名是當前的類名( 本例中的是SomeActivity),但是通過重寫 AnkoLogger 的 loggerTag 屬性我們是可以來更改的,而且每個方法有兩個版本:plain and lazy (inlined)
1.Lazy:
info("String " + "concatenation")
info { "String " + "concatenation" }
2.plain:
class SomeActivity : Activity() {
private val log = AnkoLogger(this.javaClass)
private val logWithASpecificTag = AnkoLogger("my_tag")
private fun someMethod() {
log.warning("Big brother is watching you!")
}
}
上面兩種方法分別是不同Tag的實現方式。
AnkoLogger中loggerTag 屬性具體對照如下:
2.4 Resources and dimensions
你可以在你的項目中使用Anko Resources and dimensions來簡化你的代碼,例如Color、Dimen等,顏色透明度直接色值.opaque就可以,尺寸的話直接使用dip(dipValue)、sp(spValue)就可以。在這裏面還有一個就是applyRecursively()用來控制子View的操作,如:
verticalLayout {
textView{
text = "EditText01"
backgroundColor = 0xff000.opaque
textSize = 14f
}
textView {
text = "EditText02"
backgroundColor = 0x99.gray.opaque
textSize = 23f
}
}.applyRecursively {//如果是ViewGroup的話可以使用applyRecursively來爲每個Child View進行設置
view -> when(view){
is TextView -> view.textColor = Color.RED
}
}
3.Anko Layouts
通常我們使用xml文件寫我們的佈局,但是他有一些缺點如不是類型安全,不是空安全,解析xml文件消耗更多的CPU和電量等等。而Anko Layout可以使用DSL(Domain Specific Language)動態創建我們的UI,並且它比我們使用Java動態創建佈局方便很多主要是更簡潔,它和擁有xml創建佈局的層級關係,能讓我們更容易閱讀。(官方說的優點)
舉個栗子:
verticalLayout {
val name = editText()
button("Say Hello") {
onClick { toast("Hello, ${name.text}!") }
}
}
上面的代碼是不是很簡單易懂,當然,默認的控件並不能滿足我們的需求,例如我們會更改字體的顏色及大小,會設置寬度和高度,會設置margin,padding值,那麼該如何實行呢,當然也很簡單,因爲它的邏輯和xml書寫佈局是一個套路。例如以下實現
val textView=textView("我是一個TextView"){
textSize = sp(17).toFloat()
textColor=0xff000.opaque
}.lparams{
margin=dip(10)
height= dip(40)
width= matchParent
}
配合上前面Common庫是不是很簡單呢?
這裏我們不需要setContentView。直接寫在onCreate方法中就行。
在上面創建UI過程中,我們直接把創建UI的代碼寫在onCreate方法中了,當然,還有一種寫法。我們創建一個內部類實行AnkoComponent接口,並重寫createView方法,該方法返回一個View,也就是我們創建的佈局。修改如下
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
MyActivityUI().setContentView(this)
}
}
class MyActivityUI : AnkoComponent<MyActivity> {
override fun createView(ui: AnkoContext<MyActivity>) = with(ui) {
verticalLayout {
val name = editText()
button("Say Hello") {
onClick { ctx.toast("Hello, ${name.text}!") }
}
}
}
}
現在我們編譯運行,發現效果和佈局文件寫的界面是一樣的。但是它的性能是有優勢的,其實吧並沒有發覺性能優勢。不管怎樣,這種DSL確實便於閱讀,也很容易上手,在上面的代碼中,你可能注意到了dip(10),它表示將10dp轉換爲像素的意思,是Anko的擴展函數,說的擴展函數,如果閱讀過Anko的源碼我們發現裏面大量的使用擴展函數,這也是Kotlin語言的優勢之一。
這裏就簡單介紹下Layout的使用和優點。但是我想各位看官在實際開發中也不一定會用,因爲不可視化用起來實在難以接受。不過這種見仁見智吧。
4.Anko SQLite: a query DSL and parser collection for Android SQLite;
Anko SQLite是一個查詢解析SQLite的領域專用語言
SQLite 存在的不足:
- 過多需要實現的模板代碼;
- 通過字符串實現 SQL 命令,容易出錯,且編譯時無法檢查;
- 每次需要手動關閉數據庫;
- 線程和同步訪問的問題;
隨着你應用的數據庫越來越複雜,暴露出來的問題也會越多。所以也難怪很多人會選擇 ORM 或者 NoSQL,但這帶來的方便性是以增加應用大小和方法數爲代價的。
如果你打算使用 Kotlin 來開發 Android 應用,那麼現在通過 Anko SQLite 就可以很方便的進行 SQLite 操作了。比如可以告別麻煩的 Cursor 和 ContentValue、內置安全機制,保證數據庫在執行所有代碼後能夠關閉。
通過繼承 ManagedSQLiteOpenHelper 類來實現數據庫幫助類,推薦做法就是按照官方的實現:
class DatabaseHelper(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "LibraryDatabase", null, 1) {
companion object {
private var instance: DatabaseHelper? = null
@Synchronized
fun Instance(context: Context): DatabaseHelper {
if (instance == null) {
instance = DatabaseHelper(context.applicationContext)
}
return instance!!
}
}
override fun onCreate(database: SQLiteDatabase) {
createTable(Book.TABLE_NAME, true, Book.COLUMN_ID to INTEGER + PRIMARY_KEY, Book.COLUMN_TITLE to TEXT, Book.COLUMN_AUTHOR to TEXT)
}
override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
dropTable(Book.TABLE_NAME, true)
}
}
訪問數據庫的推薦做法是通過依賴注入,或者爲 Context 添加一個 extension:
val Context.database: DatabaseHelper
get() = DatabaseHelper.Instance(applicationContext)
下面這是一個簡單的 model 類:
data class Book(val id: Int, val title: String, val author: String) {
companion object {
val Book.COLUMN_ID = "id"
val TABLE_NAME = "books"
val COLUMN_TITLE = "title"
val COLUMN_AUTHOR = "author"
}
}
當數據庫準備好後,就可以通過 use 方法來進行操作了。比如:
database.use {
insert(Book.TABLE_NAME, Book.COLUMN_ID to 1, Book.COLUMN_TITLE to "2666", Book.COLUMN_AUTHOR to "Roberto Bolano")
}
最後,讓我們來比較一下常見庫的大小:
要注意這裏 Anko SQLite 的依賴大小其實是包含了 Kotlin Runtim (method count: 6298, DEX size: 1117 KB) 和 Anko Commons module (method count: 982, DEX size: 174 KB) 的。因此如果你是通過 Kotlin 來開發,Anko SQLite 增加的大小其實並沒有圖表顯示得那麼多。
因此,如果你在使用 Kotlin 做開發並且數據庫的複雜度不高,首推 Anko SQLite。但如果數據庫結構非常複雜,DAO 和 SQL 查詢可能會變得很痛苦,這時候 ORM 和 NoSQL 就是首選方案了。
5.Anko Coroutines: utilities based on the kotlinx.coroutines library.
Kotlin協程
協程本質上是一個輕量級的線程,支持我們用同步寫法寫異步請求,而不用Callback。
doAsync(UI) {
val data: Deferred<Data> = bg {
// Runs in background
getData()
}
// This code is executed on the UI thread
showData(data.await())
}
我們可以用bg()在新的線程去做耗時操作,等返回結果再在UI線程進行操作。
具體協程教程我們可以看一下官方文檔
6.總結
Anko是Kotlin官方開發的一個讓開發Android應用更快速更簡單的Kotlin庫,並且能讓我們書寫的代碼更簡單清楚更容易閱讀。
主要包含了以下幾個部分。
- Anko Commons: a lightweight library full of helpers for intents, dialogs, logging and so on;
- Anko Layouts: a fast and type-safe way to write dynamic Android layouts;
- Anko SQLite: a query DSL and parser collection for Android SQLite;
- Anko Coroutines: utilities based on the kotlinx.coroutines library.
Anko庫用起來非常的簡單方便,裏面設計到DSL和擴展函數。
關於DSL可以看下這篇文章
關於擴展函數可以看下這篇文章