Android快速轉戰Kotlin教程

前言

kotlin是啥?這裏就不用多說了,想必看這篇文章的童鞋確定是有所瞭解的。html

那麼這篇文章你能夠收穫什麼?java

答:本文主要經過本人如何從java轉戰到kotlin並應用在實際項目中的我的經歷,給你們提供一些學習思路、學習方法以及一些學習資料和我的總結。android

前提:你的項目(包含我的項目)即將開始用到kotlin(沒有項目做爲依託你會缺乏十足的動力,並且缺乏應用場景乘熱打鐵那也是白學) 建議:建議沒有切換kotlin的小夥伴快來轉戰kotlin吧!最近一段時間搞了kotlin以後發現寫起來確實比java爽多了,語法很是精簡,並且據統計現已有30%安卓項目使用了kotlin,因此小夥伴們行動起來吧,這一定是大勢所趨,可千萬別被淘汰了啊git

入門

俗話說萬事開頭難,不過咱們先把Kotlin語法學習一遍,你就會發現so easy,並且語言思想都是相通的github

第一步:學習語法

固然是去官網學習嘍:kotlinlang.org/docs/refere…數據庫

以下圖:編程

不過英文吃力的小夥伴能夠去菜鳥教程網站學習json

地址:www.runoob.com/kotlin/kotl…設計模式

以下圖:瀏覽器

內容與官網一致。

不過不能光看,必定要寫,就算照着抄也要多寫,儘可能在學習時候多和java語法作對比,會印象深入。 以下圖,本人的練習代碼:

第二步:對比學習

你們能夠參考下面的連接進行學習:

from-java-to-kotlin : github.com/MindorksOpe…

from-java-to-kotlin中給出了咱們經常使用的語法對比

如圖:

第三步:Demo練習

經過上面的學習咱們此刻已經熟悉了kotlin的基本語法,能夠來嘗試寫一個萬年曆的Demo。

一、新建工程

咱們新建一個工程,點擊Include kotlin support 如圖:

咱們看一下Include kotlin support都幫咱們作了什麼事情

首先module中gradle文件

如圖:

比咱們以前的工程多了下面兩個引用和一個依賴:

// 使用Kotlin插件
apply plugin: 'kotlin-android'
// 使用Kotlin Android擴展插件
apply plugin: 'kotlin-android-extensions'

dependencies {
    //...
    //添加Kotlin 標準庫
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    //...
}
複製代碼

知識點: kotlin-android-extensions至關於DataBinding,一樣的目的爲了偷懶不用寫findViewByIdAndroid 開發必備。

咱們再看一下project中的gradle文件 如圖:

比咱們以前的工程多了Kotlin編譯插件:

// 添加了Kotlin編譯插件
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
複製代碼

二、Demo說明

該項目使用MVP模式,裏面用到了Retrofit2+RxJava2,而後使用了聚合的萬年曆接口,Demo很是簡單便於初學者快速掌握。

Demo使用展現:

工程目錄結構如圖:

三、Activity

看下佈局文件很是簡單,咱們能夠在activity裏面直接將控件的id當成變量來使用

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...."> <DatePicker android:id="@+id/dataPicker" .... /> <Button android:id="@+id/selectButton" .... /> <TextView android:id="@+id/titleTextView" .... /> <TextView android:id="@+id/contentTextView" .... /> </android.support.constraint.ConstraintLayout> 複製代碼
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    selectButton.setOnClickListener {
        titleTextView.visibility = View.GONE
        selectButton.visibility = View.GONE
        contentTextView.visibility = View.GONE
        dataPicker.visibility = View.VISIBLE
    }
    ....
    ....
}
複製代碼

注意:直接使用id做爲變量的時候,要在Module的gradle裏面加入擴展,才能使用,否則會報錯

apply plugin: 'kotlin-android-extensions'
複製代碼

這個上面已經說過,咱們建立工程的時候若是選中Include kotlin support怎會自動在gradle中生成。

四、Retrofit+RxJava

Retrofit結合RxJava能快捷的使用網絡請求。

建立Service接口,Kotlin的類型是寫在後面

interface RetrofitService {

    /**
     *  獲取當天詳細信息
     *  @param date 日期
     */
    @GET("calendar/day")
    fun calenderDay(
            @Query("date") date: String,
            @Query("key") key: String
    ): Observable<CalentarDayBean>

    /**
     *  獲取近期假期
     *  @param date 日期
     */
    @GET("calendar/month")
    fun calenderMonth(
            @Query("date") date: String
    ): Observable<CalentarMonthBean>

    /**
     *  獲取當年假期列表
     *  @param date 日期
     */
    @GET("calendar/year")
    fun calenderYear(
            @Query("date") date: String
    ): Observable<CalentarYearBean>
}

複製代碼

建立Retrofit,Kotlin的class並不支持static變量,因此須要使用companion object來聲明static變量,其實這個變量也不是真正的static變量,而是一個伴生對象

伴生對象能夠實現靜態調用,經過類名.屬性名或者類名.方法名進行調用

class RetrofitUtil {
    companion object {
        /**
         * 建立Retrofit
         */
        fun create(url: String): Retrofit {
            //日誌顯示級別
            val level: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.BODY
            //新建log攔截器
            val loggingInterceptor: HttpLoggingInterceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
                message -> Logger.e("OkHttp: " + message)
            })
            loggingInterceptor.level = level
            // okHttpClientBuilder
            val okHttpClientBuilder = OkHttpClient().newBuilder()

            okHttpClientBuilder.connectTimeout(60, TimeUnit.SECONDS)
            okHttpClientBuilder.readTimeout(10, TimeUnit.SECONDS)
            //OkHttp進行添加攔截器loggingInterceptor
            //okHttpClientBuilder.addInterceptor(loggingInterceptor)

            return Retrofit.Builder()
                    .baseUrl(url)
                    .client(okHttpClientBuilder.build())
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build()
        }

        val retrofitService: RetrofitService = RetrofitUtil.getService(Constants.REQUEST_BASE_URL, RetrofitService::class.java)

        /**
         * 獲取ServiceApi
         */
        fun <T> getService(url: String, service: Class<T>): T {
            return create(url).create(service)
        }
    }
}
複製代碼

經過伴生對象,結合Retrofit結合RxJava 咱們直接就能夠調用接口了

RetrofitUtil
    .retrofitService
    .calenderDay(date,"933dc930886c8c0717607f9f8bae0b48")
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ result ->
        view?.showDayCalentarData(result)
        Logger.e(result.toString())
    }, { error ->
        view?.showError(error.message.toString())
        Logger.e(error.message.toString())
    })
複製代碼

五、使用對象聲明

在寫項目的時候,通常會將常量統一寫到一個類裏面,而後設置靜態變量,因爲在Kotlin中不存在靜態變量,全部就有對象聲明的存在,對象聲明比較經常使用的地方就是在這裏,對象聲明用Objcet關鍵字表示。

object Constants {

    val REQUEST_BASE_URL = "http://v.juhe.cn/"

    val KEY = "1be865c0e67e3"
}
複製代碼

使用的時候直接類名加.加變量名,如Constants.REQUEST_BASE_URL

六、使用數據類

Kotlin有專門的數據類,就是用data修飾的類 首先咱們先看一下json數據:

{
	"reason":"Success",
	"result":{
		"data":{
			"date":"2018-4-4",
			"weekday":"星期三",
			"animalsYear":"狗",
			"suit":"訂盟.納采.冠笄.拆卸.修造.動土.安牀.入殮.除服.成服.移柩.安葬.破土.啓攢.造倉.",
			"avoid":"做竈.開光.嫁娶.開市.入宅.",
			"year-month":"2018-4",
			"lunar":"二月十九",
			"lunarYear":"戊戌年"
		}
	},
	"error_code":0
}
複製代碼

再來看一下個人數據類:

data class CalentarDayBean(
    val reason: String,
    val result: CalentarDayResult,
    val error_code: Int
)

data class CalentarDayResult(
        val data: CalentarDayData
)

data class CalentarDayData(
        val date: String,
        val weekday: String,
        val animalsYear: String,
        val suit: String,
        val avoid: String,
        val yearMonth: String,
        val holiday: String,
        val lunar: String,
        val lunarYear: String,
        val desc: String
)
複製代碼

就是如此方便

七、MVP

kotlin的MVP和java原理如出一轍我先定義了IBaseModelIBaseView

IBaseModel

interface IBaseModel<T> {

    fun onDestroy()

    fun attachView(view: T)
}
複製代碼

IBaseView

interface IBaseView {

    fun showLoading()


    fun hideLoading()

    fun showMessage(message: String)

    fun killMyself()
}
複製代碼

而後完成ICalentarContract,這個相似合同類的接口把P和V的全部方法所有寫在一塊兒,看起來代碼格外清楚

interface ICalentarContract {
    /**
     * 對於常用的關於UI的方法能夠定義到IBaseView中,如顯示隱藏進度條,和顯示文字消息
     */
    interface View : IBaseView {
        fun showDayCalentarData(calentarDayBean: CalentarDayBean)
        fun showError(errorMsg: String)
    }

    /**
     * Model層定義接口,外部只需關心Model返回的數據,無需關心內部細節,如是否使用緩存
     */
    interface Model : IBaseModel<ICalentarContract.View> {
        fun getDayCalentarData(date: String)
    }
}
複製代碼

而後activity去實現ICalentarContract.View,presenter去實現ICalentarContract.Model

class CalentarDatePresenter : ICalentarContract.Model {
....
}
複製代碼
class MainActivity : AppCompatActivity(), ICalentarContract.View {
...
}
複製代碼

so easy~~~ 到這裏咱們的Demo就完成了,能夠盡情玩樂。

項目地址:待上傳。。。。。。。。。。。。。

好了,到這裏咱們基本掌握了Kotlin在安卓中的應用,那麼接下來就須要去學習一下kotlin設計模式以及一些進階知識~

進階

1、Kotlin設計模式

本文只列出幾個經常使用的設計模式

一、觀察者模式( observer pattern )

Example

interface TextChangedListener {
    fun onTextChanged(newText: String)
}

class PrintingTextChangedListener : TextChangedListener {
    override fun onTextChanged(newText: String) = println("Text is changed to: $newText")
}

class TextView {

    var listener: TextChangedListener? = null

    var text: String by Delegates.observable("") { prop, old, new ->
        listener?.onTextChanged(new)
    }
}
複製代碼

Usage

val textView = TextView()
textView.listener = PrintingTextChangedListener()
textView.text = "Lorem ipsum"
textView.text = "dolor sit amet"
複製代碼

Output

Text is changed to: Lorem ipsum
Text is changed to: dolor sit amet
複製代碼

二、策略模式( strategy pattern )

Example

class Printer(val stringFormatterStrategy: (String) -> String) {
    fun printString(string: String) = println(stringFormatterStrategy.invoke(string))
}

val lowerCaseFormatter: (String) -> String = { it.toLowerCase() }

val upperCaseFormatter = { it: String -> it.toUpperCase() }
複製代碼

Usage

val lowerCasePrinter = Printer(lowerCaseFormatter)
lowerCasePrinter.printString("LOREM ipsum DOLOR sit amet")

val upperCasePrinter = Printer(upperCaseFormatter)
upperCasePrinter.printString("LOREM ipsum DOLOR sit amet")

val prefixPrinter = Printer({ "Prefix: " + it })
prefixPrinter.printString("LOREM ipsum DOLOR sit amet")
複製代碼

Output

lorem ipsum dolor sit amet
LOREM IPSUM DOLOR SIT AMET
Prefix: LOREM ipsum DOLOR sit amet
複製代碼

三、單例模式(singleton pattern)

Example

class Singletone private constructor() {
    init {
        println("Initializing with object: $this")
    }

    companion object {
        val getInstance =SingletonHolder.holder
    }

    private object SingletonHolder {
        val holder = Singletone()
    }

    fun print() = println("Printing with object: $this")
}

複製代碼

Usage

Singletone.getInstance.print()
Singletone.getInstance.print()
複製代碼

Output

Initializing with object: advance.Singletone@266474c2
Printing with object: advance.Singletone@266474c2
Printing with object: advance.Singletone@266474c2
複製代碼

四、工廠模式(Factory Method)

Example

interface Currency {
    val code: String
}

class Euro(override val code: String = "EUR") : Currency
class UnitedStatesDollar(override val code: String = "USD") : Currency

enum class Country {
    UnitedStates, Spain, UK, Greece
}

class CurrencyFactory {
    fun currencyForCountry(country: Country): Currency? {
        when (country) {
            Country.Spain, Country.Greece -> return Euro()
            Country.UnitedStates          -> return UnitedStatesDollar()
            else                          -> return null
        }
    }
}
複製代碼

Usage

val noCurrencyCode = "No Currency Code Available"

val greeceCode = CurrencyFactory().currencyForCountry(Country.Greece)?.code() ?: noCurrencyCode
println("Greece currency: $greeceCode")

val usCode = CurrencyFactory().currencyForCountry(Country.UnitedStates)?.code() ?: noCurrencyCode
println("US currency: $usCode")

val ukCode = CurrencyFactory().currencyForCountry(Country.UK)?.code() ?: noCurrencyCode
println("UK currency: $ukCode")
複製代碼

Output

Greece currency: EUR
US currency: USD
UK currency: No Currency Code Available
複製代碼

五、代理模式(Protection Proxy)

Example

interface File {
    fun read(name: String)
}

class NormalFile : File {
    override fun read(name: String) = println("Reading file: $name")
}

//Proxy:
class SecuredFile : File {
    val normalFile = NormalFile()
    var password: String = ""

    override fun read(name: String) {
        if (password == "secret") {
            println("Password is correct: $password")
            normalFile.read(name)
        } else {
            println("Incorrect password. Access denied!")
        }
    }
}
複製代碼

Usage

val securedFile = SecuredFile()
securedFile.read("readme.md")

securedFile.password = "secret"
securedFile.read("readme.md")
複製代碼

Output

Incorrect password. Access denied!
Password is correct: secret
Reading file: readme.md
複製代碼

六、建造者模式(builder pattern)

Example

// Let's assume that Dialog class is provided by external library. // We have only access to Dialog public interface which cannot be changed. class Dialog() { fun showTitle() = println("showing title") fun setTitle(text: String) = println("setting title text $text") fun setTitleColor(color: String) = println("setting title color $color") fun showMessage() = println("showing message") fun setMessage(text: String) = println("setting message $text") fun setMessageColor(color: String) = println("setting message color $color") fun showImage(bitmapBytes: ByteArray) = println("showing image with size ${bitmapBytes.size}") fun show() = println("showing dialog $this") } //Builder: class DialogBuilder() { constructor(init: DialogBuilder.() -> Unit) : this() { init() } private var titleHolder: TextView? = null private var messageHolder: TextView? = null private var imageHolder: File? = null fun title(init: TextView.() -> Unit) { titleHolder = TextView().apply { init() } } fun message(init: TextView.() -> Unit) { messageHolder = TextView().apply { init() } } fun image(init: () -> File) { imageHolder = init() } fun build(): Dialog { val dialog = Dialog() titleHolder?.apply { dialog.setTitle(text) dialog.setTitleColor(color) dialog.showTitle() } messageHolder?.apply { dialog.setMessage(text) dialog.setMessageColor(color) dialog.showMessage() } imageHolder?.apply { dialog.showImage(readBytes()) } return dialog } class TextView { var text: String = "" var color: String = "#00000" } } 複製代碼

Usage

//Function that creates dialog builder and builds Dialog
fun dialog(init: DialogBuilder.() -> Unit): Dialog {
    return DialogBuilder(init).build()
}

val dialog: Dialog = dialog {
	title {
    	text = "Dialog Title"
    }
    message {
        text = "Dialog Message"
        color = "#333333"
    }
    image {
        File.createTempFile("image", "jpg")
    }
}

dialog.show()
複製代碼

Output

setting title text Dialog Title
setting title color #00000
showing title
setting message Dialog Message
setting message color #333333
showing message
showing image with size 0
showing dialog Dialog@5f184fc6
複製代碼

二、相關書籍

我的認爲仍是須要找一本書籍好好地閱讀一遍,一下提供了相關書籍能夠選擇適合本身的。

NO.1

《Kotlin for Android Developers》

Kotlin是編寫Android應用程序的新官方語言,多虧了這本書,你很快就能寫出代碼。直奔主題,實用和完整的例子,它將在開發Android應用程序的同時展現你的語言。學習Kotlin並開始使用這個強大而現代的語言再次享受Android開發。

NO.2

《Kotlin開發快速入門與實戰》

學習本書以前不須要具有任何的計算機專業背景,任何有志於APP開發的讀者都能利用本書從頭學起。

資深軟件開發工程師根據Kotlin最新版本撰寫,系統講解Kotlin開發技巧和項目實戰。全書共分爲7章,內容層次清晰,難度按部就班。但願經過閱讀本書,可以讓你成爲一個全棧工程師。

NO.3

《瘋狂Kotlin講義》

本書尤爲適合從Java轉Kotlin的讀者,對於沒有Java功底的讀者,可忽略「對比」部分,直接學習本書也可掌握Kotlin編程。

本書對Kotlin的解讀十分系統、全面,超過Kotlin官方文檔自己覆蓋的內容。本書不少地方都會結合Java字節碼進行深刻解讀,好比對Kotlin擴展的解讀,對Kotlin主、次構造器的解讀,這種解讀目的不止於教會讀者簡單地掌握Kotlin的用法,而是力求讓讀者深刻理解Kotlin,且更好地理解Java。

NO.4

《Kotlin實戰》

本書主要面向有必定Java 經驗的開發者。

本書將從語言的基本特性開始,逐漸覆蓋其更多的高級特性,尤爲注重講解如何將 Koltin 集成到已有 Java 工程實踐及其背後的原理。本書分爲兩個部分。第一部分講解如何開始使用 Kotlin 現有的庫和API,包括基本語法、擴展函數和擴展屬性、數據類和伴生對象、lambda 表達式,以及數據類型系統(着重講解了可空性和集合的概念)。第二部分教你如何使用 Kotlin 構建本身的 API,以及一些深層次特性——約定和委託屬性、高階函數、泛型、註解和反射,以及領域特定語言的構建。

本書適合廣大移動開發者及入門學習者,尤爲是緊跟主流趨勢的前沿探索者。

NO.5

《揭祕Kotlin編程原理》

本書深刻介紹Kotlin面向對象設計的語法特性及其背後的實現方式。

在本書中,讀者不只能清晰地瞭解Kotlin的語法、高級特性,還能真正地掌握Kotlin背後的實現機制和設計哲學,造成對Kotlin語言既直觀、又深入的認識——在此基礎上,讀者能準確、快速地上手實踐,大大提高本身的移動開發能力。

Kotlin的這些特性和實現機制,能夠幫助開發者掃清開發道路上的一些障礙,讓開發變得更加簡單!本書是一本值得擁有,能切實幫助讀者加薪提職的好書!

項目

學習一門語言最快的方式就是看其如何在實際項目中運用,有了上面的基礎和進階,下面咱們看一些開源項目:

1.Kotlin-for-Android-Developers(★1676)

介紹:這個項目實際上是Kotlin-for-Android-Developers這本書的配套代碼,若是你是kotlin的初學者,那麼這絕對是你學習kotlin的不二之選。項目經過一個天氣的例子很好的展現了kotlin帶來的強大功能,好比網絡數據的請求,數據的緩存設計,數據庫的操做,各類擴展函數的妙用等等。

地址:github.com/antoniolg/K…

2.Bandhook-Kotlin (★1494)

介紹:Kotlin版本的音樂播放器,數據來源於LastFm。

地址:github.com/antoniolg/B…

3.GankClient-Kotlin (★1216)

介紹:gank.io kotlin實現的乾貨集中營Android客戶端,風格採用了Material Design。

地址:github.com/githubwing/…

4.PoiShuhui-Kotlin(★897)

介紹:一個用Kotlin寫的簡單漫畫APP。

地址:github.com/wuapnjie/Po…

5.Eyepetizer-in-Kotlin(★1167)

介紹:Kotlin版本的Eyepetizer客戶端

地址:github.com/LRH1993/Eye…

6.Tucao(★792)

介紹:Kotlin版本的吐槽客戶端

地址:github.com/blackbbc/Tu…

資源

1、重要資源

Kotlin 官網

kotlinlang.org/docs/refere…

Kotlin 官方網站是學習 Kotlin 好去處。在參考部分,你能夠找到該語言的全部概念和功能的深刻解析文檔。在教程部分有關於設置工做環境並使用編譯器的實用分步指南。

這裏還有個 Kotlin 編譯器,是一個瀏覽器 APP,你能夠在上面嘗試使用這門語言。它能加載許多示例,包括 Koans 課程 — 這是目前熟悉 Kotlin 語法的最好方式。

Kotlin 官博

blog.jetbrains.com/kotlin/

Kotlin 的官方博客由 JetBrains 的一位做者負責。你能夠在這裏找到全部與 Kotlin 相關的新聞、更新、教程、使用技巧等的內容。

在 Android 上開始使用 Kotlin

developer.android.com/kotlin/get-…

一篇很牛叉的文章,向咱們展現瞭如何使用 Kotlin 編寫和運行 Android 應用程序的測試

從 Java 到 Kotlin

github.com/MindorksOpe…

實用的快速提醒列表工具包含了一些簡短的代碼塊,藉由這個來幫助你快速找到通用 Java 操做符、功能以及聲明的 Kotlin 替代方案。

Kotlin 教學插件

blog.jetbrains.com/kotlin/2016…

用於 IntelliJ IDEa 的插件,可以讓你在本地離線環境下使用 Koans 課程。

Kotlin on GitHub

github.com/jetbrains/k…

Kotlin 於 2012 年開源,你能夠對該語言進行貢獻。

Kotlin Android 模板

github.com/nekocode/Ko…

Android 項目模板,使其很是容易設置穩定的 Kotlin 工做區,並快速引導你開發應用程序。

不可錯過的 Kotlin 資源列表

github.com/KotlinBy/aw…

這是一個比較完整的 Kotlin 資源列表,包括各類實用連接、書籍、庫、框架和視頻等。該列表的組織結構很是好,kotlin.link 也提供了一個風格化的版本。

kotlin設計模式

github.com/dbacinski/D…

DariuszBaciński 建立了一個 GitHub repo,其中有在 Kotlin 中實現的常見設計模式,也有用其餘語言編寫的相似項目,包括 Java,Swift,Java 和 PHP,若是你是其中一項語言的使用者,能夠用它們做爲參考點。

2、視頻資源

Kotlin 介紹

www.youtube.com/watch?v=X1R…

來自 Google I / O 2017 的演講,大會首次向人們介紹 Kotlin,並提出了改進工做流程的想法。它涵蓋了許多基礎知識,並展現了一些很酷的 Kotlin 技巧。

明日勝於今,我用 Kotlin

www.youtube.com/watch?v=fPz…

Google I / O 2017 大會關於 Kotlin 的第二個演講。這個演講涵蓋了更多高級話題,如設計模式,最佳實踐和其餘常見規則。 演講也揭示了在生產中使用 Kotlin 的意義,以及在工做中採用新興語言將面臨的挑戰。

Peter Sommerhoff 教你學 Kotlin

www.youtube.com/playlist?li…

這是一個免費的 Kotlin 課程,適合初學者,前面介紹了從變量到條件循環和函數的全部基礎知識,後面會深刻到更高級的主題,如 Kotlin 中的面向對象以及像 lambda 表達式的功能編程。

使用 Kotlin&Gradle 更好地開發 Android

www.youtube.com/watch?v=_Da…

這個講座從 2016 年開始,它介紹了現實世界中的編程語言功能,你將瞭解到 Kotlin 是如何適應 Android 工做流程中存在的工具。

使用 Kotlin&Gradle 更好地開發 Android

www.youtube.com/watch?v=ZlQ…

一個 8 分鐘的濃縮教程,讓你快速瞭解 Kotlin 的主要功能,如變量聲明、Lambdas、擴展功能等等。

Jake Wharton:用 Kotlin 進行 Android 開發

www.youtube.com/watch?v=A2L…

關於 Kotlin 的介紹,演講向咱們解釋了新語言是如何改進 Android 生態系統的,並展現了許多炫酷的方式,咱們可使用智能的 Kotlin 語法來得到優點。

相關文章
相關標籤/搜索