自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用Kotlin開發(fā)Android項(xiàng)目是一種什么樣的感受?

移動(dòng)開發(fā)
從初學(xué) Kotlin,到嘗試性的寫一點(diǎn)體驗(yàn)代碼,再到實(shí)驗(yàn)性的做一些封裝工作,到最后摸爬滾打著寫了一個(gè)項(xiàng)目。不得不說過程中還是遇上了不少的問題,盡管有不少坑是源于我自己的選擇,比如使用了 anko 布局放棄了 xml,但是總體來說,這門語言帶給我的驚喜是完全足以讓我忽略路上的坎坷。

前言

從初學(xué) Kotlin,到嘗試性的寫一點(diǎn)體驗(yàn)代碼,再到實(shí)驗(yàn)性的做一些封裝工作,到***摸爬滾打著寫了一個(gè)項(xiàng)目。不得不說過程中還是遇上了不少的問題,盡管有不少坑是源于我自己的選擇,比如使用了 anko 布局放棄了 xml,但是總體來說,這門語言帶給我的驚喜是完全足以讓我忽略路上的坎坷。

這篇文章僅僅是想整理一下這一路走過來的一些感想和驚喜,隨著我對(duì) Kotlin 的學(xué)習(xí)和使用,會(huì)長(zhǎng)期修改。

用Kotlin開發(fā)Android項(xiàng)目是一種什么樣的感受?

正文

1.有了空安全,再也不怕服務(wù)端返回空對(duì)象了

簡(jiǎn)單一點(diǎn)的例子,那就是 String 和 String?是兩種不同的類型。String 已經(jīng)確定是不會(huì)為空,一定有值;而 String?則是未知的,也許有值,也許是空。在使用對(duì)象的屬性和方法的時(shí)候,String 類型的對(duì)象可以毫無顧忌的直接使用,而 String?類型需要你先做非空判斷。

 

  1. fun demo() { 
  2.     val string1: String = "string1" 
  3.     val string2: String? = null 
  4.     val string3: String? = "string3" 
  5.      
  6.     println(string1.length) 
  7.     println(string2?.length) 
  8.     println(string3?.length) 

輸出結(jié)果為:

 

  1. null 

盡管 string2 是一個(gè)空對(duì)象,也并沒有因?yàn)槲艺{(diào)用了它的屬性/方法就報(bào)空指針。而你所需要做的,僅僅是加一個(gè)"?"。

如果說這樣還體現(xiàn)不出空安全的好處,那么看下面的例子:

 

  1. val a: A? = A() 
  2. println(a?.b?.c) 

試想一下當(dāng)每一級(jí)的屬性皆有可能為空的時(shí)候,JAVA 中我們需要怎么處理?

2.轉(zhuǎn)型與智能轉(zhuǎn)換,省力又省心

我寫過這樣子的 JAVA 代碼

 

  1. if(view instanceof TextView) { 
  2.     TextView textView = (TextView) view
  3.     textView.setText("text"); 

而在 Kotlin 中的寫法則有所不同

 

  1. if(view is TextView) { 
  2.     TextView textView = view as TextView 
  3.     textView.setText("text"

縮減代碼之后對(duì)比更加明顯

 

  1. JAVA 
  2.  
  3. if(view instanceof TextView) { 
  4.     ((TextView) view).setText("text"); 
  5.  
  6. Kotlin 
  7.  
  8. if(view is TextView) { 
  9.     (view as TextView).setText("text"

相比于 JAVA 在對(duì)象前加 (Class) 這樣子的寫法,Kotlin 是在對(duì)象之后添加 as Class 來實(shí)現(xiàn)轉(zhuǎn)型。至少我個(gè)人而言,在習(xí)慣了 as Class 順暢的寫法之后,是再難以忍受 JAVA 中前置的寫法,哪怕有 cast 快捷鍵的存在,仍然很容易打斷我寫代碼的順序和思路

事實(shí)上,Kotlin 此處可以更簡(jiǎn)單:

 

  1. if(view is TextView) { 
  2.     view.setText("text"

因?yàn)楫?dāng)前上下文已經(jīng)判明 view 就是 TextView,所以在當(dāng)前代碼塊中 view 不再是 View 類,而是 TextView 類。這就是 Kotlin 的智能轉(zhuǎn)換。

接著上面的空安全來舉個(gè)例子,常規(guī)思路下,既然 String 和 String? 是不同的類型,是不是我有可能會(huì)寫出這樣的代碼?

 

  1. val a: A? = A() 
  2. if (a != null) { 
  3.     println(a?.b) 

這樣子寫,Kotlin 反而會(huì)給你顯示一個(gè)高亮的警告,說這是一個(gè)不必要的 safe call。至于為什么,因?yàn)槟闱懊嬉呀?jīng)寫了 a != null 了啊,于是 a 在這個(gè)代碼塊里不再是 A? 類型, 而是 A 類型。

 

  1. val a: A? = A() 
  2. if (a != null) { 
  3.     println(a.b) 

智能轉(zhuǎn)換還有一個(gè)經(jīng)常出現(xiàn)的場(chǎng)景,那就是 switch case 語句中。在 Kotlin 中,則是 when 語法。

 

  1. fun testWhen(obj: Any) { 
  2.     when(obj) { 
  3.         is Int -> { 
  4.             println("obj is a int"
  5.             println(obj + 1) 
  6.         } 
  7.  
  8.         is String -> { 
  9.             println("obj is a string"
  10.             println(obj.length) 
  11.         } 
  12.  
  13.         else -> { 
  14.             println("obj is something i don't care"
  15.         } 
  16.     } 
  17.  
  18. fun main(args: Array<String>) { 
  19.     testWhen(98) 
  20.     testWhen("98"

輸出如下:

 

  1. obj is a int 
  2. 99 
  3. obj is a string 

可以看出在已經(jīng)判斷出是 String 的條件下,原本是一個(gè) Any 類的 obj 對(duì)象,我可以直接使用屬于 String 類的 .length 屬性。而在 JAVA 中,我們需要這樣做:

 

  1. System.out.println("obj is a string"
  2. String string = (String) obj; 
  3. System.out.println(string.length) 

或者

 

  1. System.out.println("obj is a string"
  2. System.out.println(((String) obj).length) 

前者打斷了編寫和閱讀的連貫性,后者嘛。。

Kotlin 的智能程度遠(yuǎn)不止如此,即便是現(xiàn)在,在編寫代碼的時(shí)候還會(huì)偶爾蹦一個(gè)高亮警告出來,這時(shí)候我才知道原來我的寫法是多余的,Kotlin 已經(jīng)幫我處理了好了。此處不再一一贅述。

3.比 switch 更強(qiáng)大的 when

通過上面智能轉(zhuǎn)化的例子,已經(jīng)展示了一部分 when 的功能。但相對(duì)于 JAVA 的 switch,Kotlin 的 when 帶給我的驚喜遠(yuǎn)遠(yuǎn)不止這么一點(diǎn)。

例如:

 

  1. fun testWhen(intInt) { 
  2.     when(int) { 
  3.         in 10 .. Int.MAX_VALUE -> println("${int} 太大了我懶得算"
  4.         2, 3, 5, 7 -> println("${int} 是質(zhì)數(shù)"
  5.         else -> println("${int} 不是質(zhì)數(shù)"
  6.     } 
  7.  
  8. fun main(args: Array<String>) { 
  9.     (0..10).forEach { testWhen(it) } 

輸出如下:

  1. 不是質(zhì)數(shù)
  2. 不是質(zhì)數(shù)
  3. 是質(zhì)數(shù)
  4. 是質(zhì)數(shù)
  5. 不是質(zhì)數(shù)
  6. 是質(zhì)數(shù)
  7. 不是質(zhì)數(shù)
  8. 是質(zhì)數(shù)
  9. 不是質(zhì)數(shù)
  10. 不是質(zhì)數(shù)
  11. 太大了我懶得算

和 JAVA 中死板的 switch-case 語句不同,在 when 中,我既可以用參數(shù)去匹配 10 到 Int.MAX_VALUE 的區(qū)間,也可以去匹配 2, 3, 5, 7 這一組值,當(dāng)然我這里沒有列舉所有特性。when 的靈活、簡(jiǎn)潔,使得我在使用它的時(shí)候變得相當(dāng)開心(和 JAVA 的 switch 對(duì)比的話)

4.容器的操作符

自從迷上 RxJava 之后,我實(shí)在很難再回到從前,這其中就有 RxJava 中許多方便的操作符。而 Kotlin 中,容器自身帶有一系列的操作符,可以非常簡(jiǎn)潔的去實(shí)現(xiàn)一些邏輯。

例如:

 

  1. (0 until container.childCount) 
  2.         .map { container.getChildAt(it) } 
  3.         .filter { it.visibility == View.GONE } 
  4.         .forEach { it.visibility = View.VISIBLE } 

上述代碼首先創(chuàng)建了一個(gè) 0 到 container.childCount - 1 的區(qū)間;再用 map 操作符配合取出 child 的代碼將這個(gè) Int 的集合轉(zhuǎn)化為了 childView 的集合;然后在用 filter 操作符對(duì)集合做篩選,選出 childView 中所有可見性為 GONE 的作為一個(gè)新的集合;最終 forEach 遍歷把所有的 childView 都設(shè)置為 VISIBLE。

這里再貼上 JAVA 的代碼作為對(duì)比。

 

  1. for(int i = 0; i < container.childCount - 1;  i++) { 
  2.     View childView = container.getChildAt(i); 
  3.     if(childView.getVisibility() == View.GONE) { 
  4.         childView.setVisibility(View.VISIBLE); 
  5.     } 

這里就不詳細(xì)的去描述這種鏈?zhǔn)降膶懛ㄓ惺裁磧?yōu)點(diǎn)了。

5.線程切換,so easy

既然上面提到了 RxJava,不得不想起 RxJava 的另一個(gè)優(yōu)點(diǎn)——線程調(diào)度。Kotlin 中有一個(gè)專為 Android 開發(fā)量身打造的庫(kù),名為 anko,其中包含了許多可以簡(jiǎn)化開發(fā)的代碼,其中就對(duì)線程進(jìn)行了簡(jiǎn)化。

 

  1. async { 
  2.     val response = URL("https://www.baidu.com").readText() 
  3.     uiThread { 
  4.         textView.text = response 
  5.     } 

上面的代碼很簡(jiǎn)單,通過 async 方法將代碼實(shí)現(xiàn)在一個(gè)異步的線程中,在讀取到 http 請(qǐng)求的響應(yīng)了之后,再通過 uiThread 方法切換回 ui 線程將 response 顯示在 textView 上。

拋開內(nèi)部的實(shí)現(xiàn),你再也不需要為了一個(gè)簡(jiǎn)簡(jiǎn)單單的異步任務(wù)去寫一大堆的無效代碼。按照慣例,這里似乎應(yīng)該貼上 JAVA 的代碼做對(duì)比,但請(qǐng)?jiān)徫也幌胨⑵?啊哈哈)

6.一個(gè)關(guān)鍵字實(shí)現(xiàn)單例

沒錯(cuò),就是一個(gè)關(guān)鍵字就可以實(shí)現(xiàn)單例:

 

  1. object Log { 
  2.     fun i(string: String) { 
  3.         println(string) 
  4.     } 
  5.  
  6. fun main(args: Array<String>) { 
  7.     Log.i("test"

再見,單例模式

7.自動(dòng) getter、setter 及 class 簡(jiǎn)潔聲明

JAVA 中有如下類

 

  1. class Person { 
  2.     private String name
  3.  
  4.     public Person(String name) { 
  5.         this.name = name
  6.     } 
  7.  
  8.     public void setName(String name) { 
  9.         this.name = name
  10.     } 
  11.  
  12.     public void getName() { 
  13.         return name
  14.     } 
  15.  
  16. Person person = new Person("張三"); 

Person person = new Person("張三");

可以看出,標(biāo)準(zhǔn)寫法下,一個(gè)屬性對(duì)應(yīng)了 get 和 set 兩個(gè)方法,需要手動(dòng)寫的代碼量相當(dāng)大。當(dāng)然有快捷鍵幫助我們生成這些代碼,但是考慮到各種復(fù)雜情形總歸不***。

而 Kotlin 中是這樣的:

 

  1. class Person(var name: String) 
  2. val person = Person("張三"); 

還可以添加默認(rèn)值:

 

  1. class Person(var name: String = "張三"
  2. val person = Person() 

再附上我項(xiàng)目中一個(gè)比較復(fù)雜的數(shù)據(jù)類:

 

  1. data class Column
  2.         var subId: String?, 
  3.         var subTitle: String?, 
  4.         var subImg: String?, 
  5.         var subCreatetime: String?, 
  6.         var subUpdatetime: String?, 
  7.         var subFocusnum: Int?, 
  8.         var lastId: String?, 
  9.         var lastMsg: String?, 
  10.         var lastType: String?, 
  11.         var lastMember: String?, 
  12.         var lastTIme: String?, 
  13.         var focus: String?, 
  14.         var subDesc: String?, 
  15.         var subLikenum: Int?, 
  16.         var subContentnum: Int?, 
  17.         var pushSet: String? 

一眼望去,沒有多余代碼。這是為什么我認(rèn)為 Kotlin 代碼比 JAVA 代碼要更容易寫得干凈的原因之一。

8. DSL 式編程

說起 dsl ,Android 開發(fā)者接觸的最多的或許就是 gradle 了

例如:

 

  1. android { 
  2.     compileSdkVersion 23 
  3.     buildToolsVersion "23.0.2" 
  4.  
  5.     defaultConfig { 
  6.         applicationId "com.zll.demo" 
  7.         minSdkVersion 15 
  8.         targetSdkVersion 23 
  9.         versionCode 1 
  10.         versionName "1.0" 
  11.     } 
  12.     buildTypes { 
  13.         release { 
  14.             minifyEnabled false 
  15.             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
  16.         } 
  17.     } 

這就是一段 Groovy 的 DSL,用來聲明編譯配置

那么在 Android 項(xiàng)目的代碼中使用 DSL 是一種什么樣的感覺呢?

 

  1. override fun onCreate(savedInstanceState: Bundle?) { 
  2.     super.onCreate(savedInstanceState) 
  3.  
  4.     val homeFragment = HomeFragment() 
  5.     val columnFragment = ColumnFragment() 
  6.     val mineFragment = MineFragment() 
  7.  
  8.     setContentView( 
  9.             tabPages { 
  10.                 backgroundColor = R.color.white 
  11.                 dividerColor = R.color.colorPrimary 
  12.                 behavior = ByeBurgerBottomBehavior(context, null
  13.  
  14.                 tabFragment { 
  15.                     icon = R.drawable.selector_tab_home 
  16.                     body = homeFragment 
  17.                     onSelect { toast("home selected") } 
  18.                 } 
  19.  
  20.                 tabFragment { 
  21.                     icon = R.drawable.selector_tab_search 
  22.                     body = columnFragment 
  23.                 } 
  24.  
  25.                 tabImage { 
  26.                     imageResource = R.drawable.selector_tab_photo 
  27.                     onClick { showSheet() } 
  28.                 } 
  29.  
  30.                 tabFragment { 
  31.                     icon = R.drawable.selector_tab_mine 
  32.                     body = mineFragment 
  33.                 } 
  34.             } 
  35.     ) 

 

[[231055]]

沒錯(cuò),上面的代碼就是用來構(gòu)建這個(gè)主界面的 viewPager + fragments + tabBar 的。以 tabPages 作為開始,設(shè)置背景色,分割線等屬性;再用 tabFrament 添加 fragment + tabButton,tabImage 方法則只添加 tabButton。所見的代碼都是在做配置,而具體的實(shí)現(xiàn)則被封裝了起來。

前面提到過 anko 這個(gè)庫(kù),其實(shí)也可以用來替代 xml 做布局用:

 

  1. override fun onCreate(savedInstanceState: Bundle?) { 
  2.     super.onCreate(savedInstanceState) 
  3.  
  4.     verticalLayout { 
  5.         textView { 
  6.             text = "這是標(biāo)題" 
  7.         }.lparams { 
  8.             width = matchParent 
  9.             height = dip(44) 
  10.         } 
  11.  
  12.         textView { 
  13.             text = "這是內(nèi)容" 
  14.             gravity = Gravity.CENTER 
  15.         }.lparams { 
  16.             width = matchParent 
  17.             height = matchParent 
  18.         } 
  19.     } 

相比于用 JAVA 代碼做布局,這種 DSL 的方式也是在做配置,把布局的實(shí)現(xiàn)代碼封裝在了背后,和 xml 布局很接近。

關(guān)于 DSL 和 anko 布局,以后會(huì)有專門的文章做介紹,這里就此打住。

9.委托/代理,SharedPreference 不再麻煩

通過 Kotlin 中的委托功能,我們能輕易的寫出一個(gè) SharedPreference 的代理類

 

  1. class Preference<T>(val context: Context, val name: String?, val default: T) : ReadWriteProperty<Any?, T> { 
  2.     val prefs by lazy { 
  3.         context.getSharedPreferences("xxxx", Context.MODE_PRIVATE) 
  4.     } 
  5.  
  6.     override fun getValue(thisRef: Any?, property: KProperty<*>): T = with(prefs) { 
  7.         val res: Any = when (default) { 
  8.             is Long -> { 
  9.                 getLong(name, 0) 
  10.             } 
  11.             is String -> { 
  12.                 getString(namedefault
  13.             } 
  14.             is Float -> { 
  15.                 getFloat(namedefault
  16.             } 
  17.             is Int -> { 
  18.                 getInt(namedefault
  19.             } 
  20.             is Boolean -> { 
  21.                 getBoolean(namedefault
  22.             } 
  23.             else -> { 
  24.                 throw IllegalArgumentException("This type can't be saved into Preferences"
  25.             } 
  26.         } 
  27.         res as T 
  28.     } 
  29.  
  30.     override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = with(prefs.edit()) { 
  31.         when (value) { 
  32.             is Long -> putLong(name, value) 
  33.             is String -> putString(name, value) 
  34.             is Float -> putFloat(name, value) 
  35.             is Int -> putInt(name, value) 
  36.             is Boolean -> putBoolean(name, value) 
  37.             else -> { 
  38.                 throw IllegalArgumentException("This type can't be saved into Preferences"
  39.             } 
  40.         }.apply() 
  41.     } 

暫且跳過原理,我們?nèi)タ丛趺词褂?/p>

 

  1. class EntranceActivity : BaseActivity() { 
  2.      
  3.     private var userId: String by Preference(this, "userId"""
  4.  
  5.     override fun onCreate(savedInstanceState: Bundle?) { 
  6.         testUserId() 
  7.     } 
  8.      
  9.     fun testUserId() { 
  10.         if (userId.isEmpty()) { 
  11.             println("userId is empty"
  12.             userId = "default userId" 
  13.         } else { 
  14.             println("userId is $userId"
  15.         } 
  16.     } 

重復(fù)啟動(dòng) app 輸出結(jié)果:

 

  1. userId is empty 
  2. userId is default userId 
  3. userId is default userId 
  4. ... 

***次啟動(dòng) app 的時(shí)候從 SharedPreference 中取出來的 userId 是空的,可是后面卻不為空。由此可見,userId = "default userId" 這句代碼成功的將 SharedPreference 中的值修改成功了。

也就是說,在這個(gè) Preference 代理的幫助下,SharedPreference 存取操作變得和普通的對(duì)象調(diào)用、賦值一樣的簡(jiǎn)單。

10.擴(kuò)展,和工具類說拜拜

很久很久以前,有人和我說過,工具類本身就是一種違反面向?qū)ο笏枷氲臇|西。可是當(dāng)時(shí)我就想了,你不讓我用工具類,那有些代碼我該怎么寫呢?直到我知道了擴(kuò)展這個(gè)概念,我才豁然開朗。

 

  1. fun ImageView.displayUrl(url: String?) { 
  2.     if (url == null || url.isEmpty() || url == "url") { 
  3.         imageResource = R.mipmap.ic_launcher 
  4.     } else { 
  5.         Glide.with(context) 
  6.                 .load(ColumnServer.SERVER_URL + url) 
  7.                 .into(this) 
  8.     } 
  9. ... 
  10. val imageView = findViewById(R.id.avatarIv) as ImageView 
  11. imageView.displayUrl(url) 

上述代碼可理解為:

  1. 我給 ImageView 這個(gè)類擴(kuò)展了一個(gè)名為 displayUrl 的方法,這個(gè)方法接收一個(gè)名為 url 的 String?類對(duì)象。如不出意外,會(huì)通過 Glide 加載這個(gè) url 的圖片,顯示在當(dāng)前的 imageView 上;
  2. 我在另一個(gè)地方通過 findViewById 拿到了一個(gè) ImageView 類的實(shí)例,然后調(diào)用這個(gè) imageView 的displayUrl 方法,試圖加載我傳入的 url

通過擴(kuò)展來為 ImageView 添加方法,相比于通過繼承 ImageView 來寫一個(gè) CustomImageView,再添加方法而言,侵入性更低,不需要在代碼中全寫 CustomImageView,也不需要在 xml 布局中將包名寫死,造成移植的麻煩。

這事用工具類當(dāng)然也可以做,比如做成 ImageUtil.displayUrl(imageView, url),但是工具類閱讀起來并沒有擴(kuò)展出來的方法讀起來更自然更流暢。

擴(kuò)展是 Kotlin 相比于 JAVA 的一大殺器

責(zé)任編輯:未麗燕 來源: 安卓巴士
相關(guān)推薦

2018-05-30 15:07:37

KotlinAndroid開發(fā)

2019-07-08 17:34:29

共享辦公ideaPod文印

2019-04-03 14:51:18

CPU性能工藝

2015-11-03 08:51:21

程序員怪物

2020-11-06 17:49:38

程序員技術(shù)開發(fā)

2015-09-09 09:41:28

十年代碼

2017-03-10 09:09:41

C語言體驗(yàn)

2015-12-03 09:23:25

程序員產(chǎn)品經(jīng)理

2015-04-08 10:40:09

2021-01-14 21:46:02

Vue.jsReact框架

2010-08-02 13:30:34

移動(dòng)開發(fā)移動(dòng)開發(fā)平臺(tái)

2017-04-06 15:00:38

編程語言

2017-08-17 13:14:01

2020-04-07 08:05:51

程序員互聯(lián)網(wǎng)職業(yè)

2015-02-04 10:55:14

2019-01-11 10:39:24

軟件架構(gòu)虛擬空間機(jī)器人

2025-01-06 08:00:54

2016-08-30 21:09:33

2014-02-25 09:55:07

敏捷開發(fā)

2015-01-21 15:35:58

開源
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)