簡述: 今天我們來講點Kotlin中比較時髦的東西,有的人可能會說:“不像你之前的風格啊,之前的文章不是一直在死扣語法以及語法糖背後祕密。當你還在死扣泛型語法的時候,別人的文章早就說了Kotlin/Native和Kotlin1.3的新特性”。瞬間感覺自己out了,今天我們就說說這些時髦的東西,也許你能看到一些和別人不一樣的東西哦。
前段時間你們的熊貓小哥哥(也就是我),由於對Kotlin過度熱愛,一天偶然看到2018 JetBrains開發者日-Kotlin專場活動,腦袋一熱,瞬間心動了,馬上就買了門票和火車票去北京(第一次一個人去北京)參加活動了。因為看到有Kotlin中文社群兩位大佬(這兩位大佬是我一年多以前開始寫Kotlin的時候就關注了他們)的演講日程以及JetBrains資深佈道師Hali的演講,沒有過多思考直接買票,不要慫就是幹。最後順便和Kotlin社群的大佬們面個基啥的,謝謝大佬們的熱情款待。此次北京之行收穫挺多的,有時候知道一些最新技術方向和動態會比你埋頭閉門造車好的很多。
因為在我的公眾號上(Kotlin開發者聯盟),有一些小夥伴希望我能從北京的開發者會上帶點東西回來,所以總結了一下結合自己實際的開發,給大家帶來以下幾篇文章。
- 1、Kotlin/Native1.0 Beta(嚐鮮篇)
- 2、Kotlin中1.3版本新特性都有哪些?
- 3、Kotlin中的Coroutine(協程)在Android上應用(協程學前班篇)
- 4、Ktor非同步框架初體驗(Ktor學前班篇)
那麼,今天就開始第一篇,看過一些大佬寫關於Kotlin/ Native的文章,基本上都是翻譯了Kotlin Blog的官網部落格, 具體如何實踐的還是比較少的。今天我不打算這麼講,既然今天的主題是時髦那就講點有意思的東西。一起來看下今天提綱:
一、重新認識Kotlin語言
在開始之前,我覺得有必要一起重新來認識一下Kotlin這門語言,很多人一直都認為它不就是門JVM語言和Java、Scala一樣都是跑在JVM虛擬機器上。其實Kotlin並不僅僅是一門JVM語言,它的野心是真的大,JVM語言已經無法滿足它的雄心壯志了。它是一門多平臺的靜態編譯型語言,它可以用於JVM上(只不過在JVM層面比較出名而已,導致很多人都認為它是門JVM語言),實則它可以編譯成JavaScipt執行在瀏覽器中也可以編譯成IOS的可執行檔案跑在LLVM上
二、Kotlin/Native的基本介紹
用官方的話來說Kotlin / Native是一種將Kotlin程式碼編譯為本機二進位制檔案的技術,可以在沒有虛擬機器的情況下執行。它是基於LLVM的後端,用於Kotlin編譯器和Kotlin標準庫的本機實現。
Kotlin/Native目前支援以下平臺:
- --- iOS (arm32, arm64, emulator x86_64)
- --- MacOS (x86_64)
- --- Android (arm32, arm64)
- --- Windows (mingw x86_64)
- --- Linux (x86_64, arm32, MIPS, MIPS little endian)
- --- WebAssembly (wasm32)
為了更好說明Kotlin/Native能力,下面給出張官方的Kotlin/Native能力圖:
對於Kotlin/Native之前一直沒有去玩過,只是經常聽到社群小夥伴們說編譯起來巨慢,感覺好時髦啊。抱著好奇心,並且也符合我們這篇文章時髦的主題,決定一步步帶大家玩一玩。
三、Kotlin/Native開發IOS HelloWorld
1、需要準備的開發工具
- AppCode 2018.1(建議下載最新版本,這裡不是最新版本不過也能玩哈,最新版本應該到了2018.3)
- Kotlin/Native Plugin 181.5087.34(注意: 外掛和AppCode IDE的版本匹配問題,建議把IDE安裝好,然後IDE搜尋下載會預設給最佳匹配的外掛版本的)
- Xcode 9.2(注意: 這裡Xcode版本需要AppCode版本匹配,否則會有問題的,不過不匹配的話IDE會有提示的,建議如果AppCode 2018.1(Xcode 9.2), AppCode 2018.3(Xcode 10.0))
2、建立一個Kotlin/Native專案
第一步: 選擇左側的Kotlin/Native, 並選擇右側的Sing View App with a Kotlin/Native Framework
第二步: 填寫專案名和包名,選擇語言Swift(這裡先以Swift為例)
第三步: 最後finish即可建立完畢Kotlin/Native專案,建立完畢後專案結構如下
4、執行Kotlin/Native專案
如果你比較幸運跑起來的話,效果應該是在模擬器裝一個APP並且起了一個空白頁,終端上輸出了"Hello from Kotlin!"的Log,類似這樣:
注意: 但是你是真題測試,而且Run頂部預設只有一個IOS Device選項的話,然後你又點了Run 說明而且會報如下錯誤
這個問題是因為預設IOS Device選項是表示用真機除錯哈,然後這邊就需要一個IOS開發者賬號。設定開發者賬號的話,建議使用Xcode去開啟該專案然後給該專案配置一個開發者賬號。
設定完畢Xcode後,AppCode會自動檢測到重新整理的。
四、Kotlin/Native開發IOS 執行原理分析
看到上面IOS HelloWorld專案執行起來,大家有沒有思考一個問題,Kotlin的程式碼的程式碼是怎麼在IOS裝置上跑起來呢?
實際上,在這背後使用了一些指令碼和工具在默默支撐著整個專案的執行,如前所述,Kotlin / Native平臺有自己的編譯器,但每次想要構建專案時手動執行它明顯不是高效的。 所以Kotlin團隊了選擇Gradle。Kotlin / Native使用Gradle構建工具在Xcode中自動完成Kotlin / Native的整個構建過程。在這裡使用Gradle意味著開發人員可以利用其內部增量構建架構,只需構建和下載所需內容,從而節省開發人員的寶貴時間。
如果,你還對上述有點疑問不妨一起來研究下Kotlin/Native專案中的構建引數指令碼:
- 開啟構建指令碼是需要在Xcode中開啟的,具體可以參考如下圖:
通過以上專案可以分析到在Xcode中編譯一個Kotlin/Native專案,實際上在執行一段shell指令碼,並在shell指令碼執行中gradlew命令來對Kotlin/Native編譯,該指令碼呼叫gradlew工具,該工具是Gradle Build System的一部分,並傳遞構建環境和除錯選項。 然後呼叫一個konan gradle外掛實現專案編譯並輸出xxx.kexe檔案,最後並把它複製到iOS專案構建目錄("$TARGET_BUILD_DIR/$EXECUTABLE_PATH"
)。
最後來看下Supporting Files中的build.gradle構建檔案,裡面就引入了konan外掛(Kotlin/Native編譯外掛), 有空的話建議可以深入研究下konan外掛,這裡其實也是比較淺顯分析了下整個編譯過程,如果深入研究konan外掛原始碼的話,更能透過現象看到Kotlin/Native本質,這點才是最重要的。
buildscript {
ext.kotlin_version = '1.2.0'
repositories {
mavenCentral()
maven {
url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies"
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:0.7"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
repositories {
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib"
}
apply plugin: 'konan'
konan.targets = [
'ios_arm64', 'ios_x64'
]
konanArtifacts {
program('KotlinNativeOC')
}
複製程式碼
五、Kotlin/Native專案結構分析
1、Kotlin/Native + Swift專案結構分析
我們知道main函式是很多應用程式的入口,ios也不例外,在AppDelegate.swift中有@UIApplicationMain的註解,這裡就是APP啟動的入口。
@UIApplicationMain //main函式註解入口,所以AppDelegate類相當於啟動入口類
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?//預設加了UIWindow
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// KNFKotlinNativeFramework class is located in the framework that is generated during build.
// If it is not resolved, try building for the device (not simulator) and reopening the project
NSLog("%@", KNFKotlinNativeFramework().helloFromKotlin())//注意: 這裡就是呼叫了Kotlin中的一個helloFromKotlin方法,並把返回值用Log列印出來,所以你會看到App啟動的時候是有一段Log被列印出來
return true
}
...
}
複製程式碼
KotlinNativeFramework類
class KotlinNativeFramework {
fun helloFromKotlin() = "Hello from Kotlin!" //返回一個Hello from Kotlin!字串
}
複製程式碼
但是呢,有追求的程式設計師絕對不能允許跑出來的是一個空白頁面,空白頁面那還怎麼裝逼呢? 哈哈。在ViewController.swift中的viewDidLoad函式中加入一個文字(UILabel)。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 21))
label.center = CGPoint(x: 160, y: 285)
label.textAlignment = .center
label.font = label.font.withSize(15)
label.text = "Hello IOS, I'm from Kotlin/Native"
view.addSubview(label)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
複製程式碼
最後重新run一遍,效果如下:
2、Kotlin/Native + Objective C專案結構分析
在IOS同事幫助下,進一步瞭解IOS APP啟動基本知識,這將有助於我們接下來改造我們專案結構,使得它更加簡單,完全可以刪除額外的Swift程式碼,包括APP啟動代理那塊都交由Kotlin來完成。
- 第一步: 先建立一個Kotlin/Native + OC 的專案,這裡就不重複建立過程,直接把OC目錄結構給出:
- 第二步: 可以看到OC與Swift專案結構差不多哈,可以看到其中有幾個重要的檔案,
main.m、AppDelegate.m、ViewController.m
main.m APP啟動入口,相當於main函式,先從main函式入手,然後一步步弄清整個啟動流程。
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));//這裡也呼叫了AppDelegate類
}
}
複製程式碼
- 第三步: 然後轉到AppDelegate.m,可以看到在didFinishLaunchingWithOptions函式中呼叫了KNFKotlinNativeFramework中的helloFromKotlin函式。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// KNFKotlinNativeFramework class is located in the framework that is generated during build.
// If it is not resolved, try building for the device (not simulator) and reopening the project
NSLog(@"%@", [[[KNFKotlinNativeFramework alloc] init] helloFromKotlin]);//注意這裡呼叫helloFromKotlin,並輸出日誌
return YES;
}
複製程式碼
3、Kotlin/Native + Kotlin專案結構分析
到這裡很多人就會問了,看你上面說了那麼並沒有看到你Kotlin在做什麼事,全是Swift和OC在做APP啟動。現在就是告訴你Kotlin如何去替代它們做APP啟動的事了。
- 先新建立一個專案,這次建立的不再是Sing View App with a Kotlin/Native Framework, 而是一個Application專案。
- 生成後的目錄檔案全是Kotlin檔案,具體如下:
- 生成的main.kt替代main.m,並設定對應啟動的AppDelegate
import kotlinx.cinterop.autoreleasepool
import kotlinx.cinterop.cstr
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toCValues
import platform.Foundation.NSStringFromClass
import platform.UIKit.UIApplicationMain
fun main(args: Array<String>) {
memScoped {
val argc = args.size + 1
val argv = (arrayOf("konan") + args).map { it.cstr.getPointer(memScope) }.toCValues()
autoreleasepool {
UIApplicationMain(argc, argv, null, NSStringFromClass(AppDelegate))//注意: 在這裡設定對應啟動的AppDelegate
}
}
}
複製程式碼
- 生成AppDelegate替代原來的AppDelegate.m,並且在內部設定好啟動的Window.
import kotlinx.cinterop.initBy
import platform.Foundation.NSLog
import platform.UIKit.*
class AppDelegate : UIResponder(), UIApplicationDelegateProtocol {
override fun init() = initBy(AppDelegate())
private var _window: UIWindow? = null
override fun window() = _window
override fun setWindow(window: UIWindow?) { _window = window }
override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map<Any?, *>?): Boolean {//監聽APP啟動完成,列印Log
NSLog("this is launch from kotlin appDelegate")
return true
}
companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta//注意:一定得有個companion object否則在main函式NSStringFromClass(AppDelegate)會報錯
}
複製程式碼
- 再生成一個ViewController,這個ViewController很類似Android中的Activity。
import kotlinx.cinterop.*
import platform.Foundation.*
import platform.UIKit.*
@ExportObjCClass
class ViewController : UIViewController {
constructor(aDecoder: NSCoder) : super(aDecoder)
override fun initWithCoder(aDecoder: NSCoder) = initBy(ViewController(aDecoder))
@ObjCOutlet
lateinit var label: UILabel
@ObjCOutlet
lateinit var textField: UITextField
@ObjCOutlet
lateinit var button: UIButton
@ObjCAction
fun buttonPressed() {
label.text = "Konan says: 'Hello, ${textField.text}!'"
}
}
複製程式碼
執行出來的效果如下:
六、Kotlin/Native開發一個地圖Demo
1、IOS專案ViewController與元件繫結過程分析
看到上面的執行Demo,大家有沒有在思考一個問題IOS專案中的ViewController是怎麼和UI元件繫結在一起的呢?我個人認為這個很重要,換句話說這就是IOS開發最基本的套路,如果這個都不弄明白的話,下面Demo開發就是雲裡霧裡了,掌握了這個基本套路的話,作為一個Android開發者,你基本上就可以在IOS專案開發中任意折騰了。
- 第一步: 在kotlin目錄下新建一個KNMapViewController類,並且它去繼承UIViewController以及實現MKMapViewDelegateProtocol介面,並重寫viewDidLoad()函式。並且在viewDidLoad函式實現map地圖基本配置。
//匯入Kotlin以與Objective-C和一些Cocoa Touch框架互操作。
import kotlinx.cinterop.*
import platform.CoreLocation.CLLocationCoordinate2DMake
import platform.Foundation.*
import platform.MapKit.MKCoordinateRegionMake
import platform.MapKit.MKCoordinateSpanMake
import platform.MapKit.MKMapView
import platform.MapKit.MKMapViewDelegateProtocol
import platform.UIKit.*
@ExportObjCClass//注意: @ExportObjCClass註解有助於Kotlin建立一個在執行時可查詢的類。
class KNMapViewController: UIViewController, MKMapViewDelegateProtocol {
@ObjCOutlet //注意: @ObjCOutlet註解很重要,主要是將mMapView屬性設定為outlet。這允許您將Main.storyboard中的MKMapview連結到此屬性。
lateinit var mMapView: MKMapView
constructor(aDecoder: NSCoder) : super(aDecoder)
override fun initWithCoder(aDecoder: NSCoder) = initBy(KNMapViewController(aDecoder))
override fun viewDidLoad() {
super.viewDidLoad()
val center = CLLocationCoordinate2DMake(32.07, 118.78)
val span = MKCoordinateSpanMake(0.7, 0.7)
val region = MKCoordinateRegionMake(center, span)
with(mMapView) {
delegate = this@KNMapViewController
setRegion(region, true)
}
}
}
複製程式碼
- 第二步: 用Xcode開啟專案中的Main.storyboard,刪除原來自動生成一些檢視元件(如果你處於AppCode中開發專案,實際上直接在AppCode中雙擊Main.storyboard就會自動使用Xcode開啟當前整個專案,並開啟這個專案)
- 第三步: 給當前空的檢視繫結對應ViewController,這裡是KNMapViewController
- 第四步: 在當前空的檢視中新增一個map view元件並且設定元件的約束條件。
-
第五步: 右擊元件MKMapView可以看到黑色對話方塊,裡面Referencing Outlets還空的,說明當前ViewController沒有和MKMapView元件繫結
-
第六步: 配置outlet,這裡說下AppCode很坑爹地方,需要手動去source code中手動配置outlet,選中main.storyboard右擊open as 然後選擇開啟source code
- 第七步: 在view和viewController結尾標籤之間配置connection 配置的code如下:
<connections>
<outlet property="mMapView" destination="dest id" id="generate id"/>
</connections>
<!--property屬性值就是KNMapViewController中的mMapView變數名;destination屬性值是一個map view標籤中id(可以在subviews標籤內的mapView標籤中找到id), id屬性則是自動生成的,可以按照格式自己之指定一個,只要不出現重複的id即可-->
複製程式碼
配置結果如下:
- 第八步: 檢驗是否繫結成功, 回到main.stroyboard檢視,右擊元件檢視黑色框是否出現如下繫結關係,出現了則說明配置成功。
2、接著上述配置步驟,就可以回到AppCode中執行專案了
以上的執行結果就說明了,我們Demo已經執行成功了。並且我已經把此次Kotlin/Native專案都放到了GitHub上,如果感興趣的小夥伴可以clone下來玩一玩這個時髦的鬼東西。如果覺得對你有幫助,還請大佬們給個star。七、Kotlin/Native開發體驗分析
Kotlin/Native目前還是處於1.0的beta,所以還是有很多的地方是不讓人滿意的。下面我總結這次Kotlin/Native開發體驗的優缺點:
優點:
通過上述的幾個例子,可以明顯表明Kotlin/Native語言層面跨平臺能力還是很強的,和OC,Swfit專案操作性也很強,該有的基本上都已經實現了,所以對於它後續發展還是非常值得關注的。據我瞭解到,Kotlin團隊目前首要重心就是在Kotlin/Native這一塊,希望他們能給我們帶來更多關於Kotlin/Native的驚喜。
缺點
缺點還是有很多的:
- 1、Kotlin/Native編譯速度有點慢的驚人,不過執行速度還是不錯的。
- 2、AppCode中用Kotlin開發IOS專案,沒有很強的程式碼提示,很多API導包都比較麻煩,對於初學者更是一臉懵逼。
- 3、AppCode中的Kotlin/Native外掛居然沒提供右鍵建立Kotlin類、介面之類,包括官方也給出這是個問題不過提供一個fix方案。
最後可以測試一下:
- 4、AppCode中使用Kotlin開發IOS專案,配置outlet還得切換到在Xcode手工操作,希望後續改進。
- 5、AppCode耗記憶體還大了,比AndroidStudio還厲害,多開了兩個專案,基本就是每操作IDE就在轉圈圈,希望後續改進。
八、聊點關於Kotlin/Native和Flutter框架那點事
1、簡述:
其實關於這個主題,我是不太想去說的,因為我不想引起程式語言界的口戰。但是總有人喜歡去把Kotlin/Native和Flutter放在一起去做對比,因為他們好像都有一個共同點就是跨平臺都能開發Android、IOS應用。並且在此次Jetbrains開發者日上就有參會嘉賓在會上問官方佈道師Hali,Kotlin/Native和Flutter有什麼不一樣,優勢在哪?
2、提出個人觀點:
針對這個問題我說下個人的觀點:
首先,我個人是非常看好Flutter這個移動端跨平臺框架,它能夠完全抹平Android,IOS API層面的差異性。換句話說就是一個小白,也許他不懂Java,OC或Swift,他只要熟悉dart並且熟悉Flutter框架API就能開發出Android和IOS兩個原生般體驗的應用。確實很爽啊,無論站在公司節約人力和維護成本還是開發者技術成本角度考慮都是不錯的選擇。可是從另一角度可以想象下一旦形成這樣的局面,很多新專案都是用dart開發,swift和oc語言對於新的開發者而言你會去用嗎? 蘋果爸爸貌似就會不開心了,這其中故事大家可以自己去想像了(當然也許未來不是我說的那樣發展,純屬個人猜想的哈)。
然後,我說下Kotlin/Native吧,其實Kotlin/Native和Flutter不是一個層面東西,不太很好做對比,Kotlin/Native更多的是語言編譯器層面,而Flutter是框架層面。兩者根本就不是一個世界的。Flutter是自己實現一套渲染機制和UI引擎,並且有著豐富Development API。而Kotlin/Native更多關注是編譯器如何將kt原始碼編譯成IOS LLVM可執行的二進位制碼,但是Kotlin/Native並沒有像Flutter一樣在API層面抹平平臺差異性,而是 在語言層面做了平臺差異性抹平。也就是說你要開發IOS應用不僅會Kotlin/Native還得會IOS 應用開發Development Api. 這貌似Kotlin/Native稍遜Flutter一籌,但是想想也有道理,API層面抹平不是在語言層面職責,還是需要框架層面來做到這一點,說不定哪天Kotlin團隊也造出一個類似Flutter的輪子呢。而且語言層面跨平臺帶來的還有一點好處就是共享程式碼,Android、IOS、前端都可以用Kotlin來實現,可以把它們擁有相同邏輯用Kotlin編寫的程式碼放入一個common包中統一管理,並且Kotlin對多平臺共享這塊做了很好的支援,然後來自於不同平臺可以共享這塊通用的邏輯實現,不用各自平臺去寫一套了。
3、給開發者抉擇建議:
通過上述觀點,我們總結到對於開發者而言,學習一門新的技術實際上一般會存在兩層隱形成本。一層是對新的開發語言的掌握,另一層則是對這門技術的Development Api的熟悉和掌握。那麼Kotlin/Native就是為了磨平第一層開發語言的障礙。語言層面能做的也只能這樣了,不像Flutter它是一個框架,它可以直接從API層面抹平。如果Flutter最終能推廣起來,在熱更新和熱修復這塊支援比現在的原生還好的話,並且你是一個初學者,那麼它絕對是一個不錯的選擇。
如果你是Kotlin開發者,你完全可以使用Kotlin/Native然後花一些時間熟悉下IOS的API也能把IOS應用玩起來。當然Kotlin/Native開發IOS應用只是其中一小部分,你完全用它去做更多有意義的事。
如果你是移動開發者,建議兩門技術都可以去嘗試一下畢竟技多不壓身,當然語言只是一門工具,最終靠還是計算機紮實的基礎、分析需求的能力以及架構專案的功能能力。有了這些能力再加上一個高效的程式語言那麼你就更加所向披靡了。
Kotlin系列文章,歡迎檢視:
原創系列:
- 教你如何攻克Kotlin中泛型型變的難點(實踐篇)
- 教你如何攻克Kotlin中泛型型變的難點(下篇)
- 教你如何攻克Kotlin中泛型型變的難點(上篇)
- Kotlin的獨門祕籍Reified實化型別引數(下篇)
- 有關Kotlin屬性代理你需要知道的一切
- 淺談Kotlin中的Sequences原始碼解析
- 淺談Kotlin中集合和函式式API完全解析-上篇
- 淺談Kotlin語法篇之lambda編譯成位元組碼過程完全解析
- 淺談Kotlin語法篇之Lambda表示式完全解析
- 淺談Kotlin語法篇之擴充套件函式
- 淺談Kotlin語法篇之頂層函式、中綴呼叫、解構宣告
- 淺談Kotlin語法篇之如何讓函式更好地呼叫
- 淺談Kotlin語法篇之變數和常量
- 淺談Kotlin語法篇之基礎語法
翻譯系列:
- [譯]Kotlin的獨門祕籍Reified實化型別引數(上篇)
- [譯]Kotlin泛型中何時該用型別形參約束?
- [譯] 一個簡單方式教你記住Kotlin的形參和實參
- [譯]Kotlin中是應該定義函式還是定義屬性?
- [譯]如何在你的Kotlin程式碼中移除所有的!!(非空斷言)
- [譯]掌握Kotlin中的標準庫函式: run、with、let、also和apply
- [譯]有關Kotlin型別別名(typealias)你需要知道的一切
- [譯]Kotlin中是應該使用序列(Sequences)還是集合(Lists)?
- [譯]Kotlin中的龜(List)兔(Sequence)賽跑
- [譯]Effective Kotlin系列之考慮使用靜態工廠方法替代構造器
- [譯]Effective Kotlin系列之遇到多個構造器引數要考慮使用構建器
實戰系列:
- 用Kotlin擼一個圖片壓縮外掛ImageSlimming-導學篇(一)
- 用Kotlin擼一個圖片壓縮外掛-外掛基礎篇(二)
- 用Kotlin擼一個圖片壓縮外掛-實戰篇(三)
- 淺談Kotlin實戰篇之自定義View圖片圓角簡單應用
歡迎關注Kotlin開發者聯盟,這裡有最新Kotlin技術文章,每週會不定期翻譯一篇Kotlin國外技術文章。如果你也喜歡Kotlin,歡迎加入我們~~~