AndroidUtilCodeKTX !是時候提升你的開發效率了 !(持續更新中...)

秉心說TM發表於2019-07-17

前言

第一次接觸 Kotlin 還是 2017 年,當時 Kotlin 還沒扶正,也不是 Android 的官方開發語言。至於我是怎麼被安利的,沒記錯的話,應該是 開源實驗室 的 Kotlin 教程。當時身邊幾乎沒有人在學 Kotlin,網上相關的資料也很少,我還翻譯了一部分官網文件,寫了一本 GitBook 。 當然現在有更好的 Kotlin 語言中文站 了,對於英文基礎不是很好的同學,這是一個不錯的入門資料。

兩年半時間過來了,Kotlin 搖身一變,穩坐 Android 官方開發語言。儘管可能是為了避免與 Oracle 的訴訟,但我相信這絕對不是主要原因。被定義為 Better Java 的 Kotlin,的確做到了更好,也逐漸為更多人所使用。

《全新 LeakCanary 2 ! 完全基於 Kotlin 重構升級 !》

Retrofit 2.6.0 ! 更快捷的協程體驗 !

越來越多的知名三方庫都已經開始提供對 Kotlin 的支援。Android 官方的新特性也將優先支援 Kotlin。除了 Android 領域,Spring 5 正式釋出時,也將 Kotlin 作為其主打的新特性之一。肉眼可見的,在 JVM 語言領域,Kotlin 必將有一番作為。

我在之前的一篇文章 真香!Kotlin+MVVM+LiveData+協程 打造 Wanandroid! 中開源了 Kotlin MVVM 版本的 Wanandroid 應用,到現在一共有 138 個 star 和 20 個 fork。資料並不是多亮眼,但算是我開源生涯中的一大步。這個專案我也一直在更新,歡迎大家持續關注。

目前的工作中,除非一些不可抗拒因素,我已經將 Kotlin 作為第一選擇。在進行多個專案的開發工作之後,我發現我經常在各個專案之間拷貝程式碼,base 類,擴充套件函式,工具類等等。甚至有的時候會翻回以前的 Java 程式碼,或者 Blankj 的 AndroidUtilCode,拿過來直接使用。但是很多時候直接生搬硬套 Java 工具類,並不是那麼的優雅,也沒有很好的運用 Kotlin 語言特性。我迫切需要一個通用的 Kotlin 工具類庫。

基於此,AndroidUtilCodeKTX 誕生了。如果你用過 Blankj 的 AndroidUtilCode,它們的性質是一樣的,但絕不是其簡單的 Kotlin 翻譯版本,而是更多的糅合了 Kotlin 特性,一切從 “簡” ,程式碼越少越好。Talk is easy,show me the code ! 話不多說,下面就程式碼展示 AndroidUtilCodeKTX 中一些工具類的使用。

AndroidUtilCodeKTX

許可權請求

request(Manifest.permission.READ_CALENDAR, Manifest.permission.RECORD_AUDIO) {
    onGranted { toast("onGranted") }
    onDenied { toast("onDenied") }
    onShowRationale { showRationale(it) }
    onNeverAskAgain { goToAppInfoPage() }
}
複製程式碼

藉助擴充套件函式和 DSL ,可以輕鬆的在 Activity 中優雅的請求許可權以及處理回撥。這不是一個全新的輪子,主要程式碼來自 PermissionsKt 。但是它有一個致命的缺點,需要開發者手動覆寫 onRequestPermissionsResult() 來處理許可權請求結果,這顯得並不是那麼簡潔和優雅。我這裡借鑑了 RxPermissions 的處理方式,通過在當前 Activity 依附一個 Fragment 進行許可權請求以及回撥處理,這樣對使用者來說是無感的,且避免了額外的程式碼。後續會單獨寫一篇文章,分析其中 Kotlin DSL 的應用。

SharedPreferences

putSpValue("int", 1)
putSpValue("float", 1f)
putSpValue("boolean", true)
putSpValue("string", "ktx")
putSpValue("serialize", Person("Man", 3))

getSpValue("int", 0)
getSpValue("float", 0f)
getSpValue(key = "boolean", default = false)
getSpValue("string", "null")
getSpValue("serialize", Person("default", 0))
複製程式碼

基本的儲存和讀取操作都只需要一個函式即可完成,依賴泛型無需主動宣告值的型別。預設儲存檔名稱為包名,如果你想自定義檔名稱,宣告 name 引數即可:

putSpValue("another","from another sp file",name = "another")
getSpValue("another","null",name = "another")
複製程式碼

Activity 相關

主要是優化了 startActivity 的使用,爭取做到任何情況下都可以一句程式碼啟動 Activity。

普通跳轉:

startKtxActivity<AnotherActivity>()
複製程式碼

帶 flag 跳轉:

startKtxActivity<AnotherActivity>(Intent.FLAG_ACTIVITY_NEW_TASK)
複製程式碼

startActivityForResult :

startKtxActivityForResult<AnotherActivity>(requestCode = 1024)
複製程式碼

帶值跳轉:

startKtxActivity<AnotherActivity>(value = "string" to "single value")
複製程式碼

帶多個值跳轉:

startKtxActivity<AnotherActivity>(
                values = arrayListOf(
                    "int" to 1,
                    "boolean" to true,
                    "string" to "multi value"
                )
            )
複製程式碼

帶 Bundle 跳轉:

startKtxActivity<AnotherActivity>(
                extra = Bundle().apply {
                    putInt("int", 2)
                    putBoolean("boolean", true)
                    putString("string", "from bundle")
                }
            )
複製程式碼

基本涵蓋了所有的 Activity 跳轉情況。

Aes 加密相關

ByteArray.aesEncrypt(key: ByteArray, iv: ByteArray, cipherAlgotirhm: String = AES_CFB_NOPADDING): ByteArray
ByteArray.aesDecrypt(key: ByteArray, iv: ByteArray, cipherAlgotirhm: String = AES_CFB_NOPADDING): ByteArray 
File.aesEncrypt(key: ByteArray, iv: ByteArray, destFilePath: String): File?
File.aesDecrypt(key: ByteArray, iv: ByteArray, destFilePath: String): File?
複製程式碼

封裝了 Aes 加密操作,提供了快捷的資料和檔案加解密方法,無需關注內部細節。預設使用 AES/CFB/NoPadding 模式,你可以通過 cipherAlgotirhm 引數修改模式。

plainText.toByteArray().aesEncrypt(key, iv, "AES/CBC/PKCS5Padding"
plainText.toByteArray().aesEncrypt(key, iv, "AES/ECB/PKCS5Padding"
plainText.toByteArray().aesEncrypt(key, iv, "AES/CTR/PKCS5Padding"
複製程式碼

Hash 相關

StringByteArray 提供了擴充套件方法,可以快捷的進行雜湊。

ByteArray.hash(algorithm: Hash): String
String.hash(algorithm: Hash, charset: Charset = Charset.forName("utf-8")): String
ByteArray.md5Bytes(): ByteArray
ByteArray.md5(): String
String.md5(charset: Charset = Charset.forName("utf-8")): String
ByteArray.sha1Bytes(): ByteArray
ByteArray.sha1(): String
String.sha1(charset: Charset = Charset.forName("utf-8")): String
ByteArray.sha224Bytes(): ByteArray
ByteArray.sha224(): String
String.sha224(charset: Charset = Charset.forName("utf-8")): String
ByteArray.sha256Bytes(): ByteArray
ByteArray.sha256(): String
String.sha256(charset: Charset = Charset.forName("utf-8")): String
ByteArray.sha384Bytes(): ByteArray
ByteArray.sha384(): String
String.sha384(charset: Charset = Charset.forName("utf-8")): String
ByteArray.sha512Bytes(): ByteArray
ByteArray.sha512(): String
String.sha512(charset: Charset = Charset.forName("utf-8")): String
File.hash(algorithm: Hash = Hash.SHA1): String
複製程式碼

支援 MD5, sha1, sha224, sha256, sha384, sha512MD5 已經不再安全,密碼學上來說已經不再推薦使用。

val origin = "hello"
val md5 = origin.hash(Hash.MD5)
val sha1 = origin.hash(Hash.SHA1)
複製程式碼

除了字串取雜湊,還提供了對檔案取雜湊值操作。

val file = File("xxx")
val md5 = file.hash(Hash.MD5)
val sha1 = file.hash(Hash.SHA1)
複製程式碼

Intent 相關

跳轉到應用資訊介面:

Context.goToAppInfoPage(packageName: String = this.packageName)
複製程式碼

跳轉到日期和時間頁面:

Context.goToDateAndTimePage()
複製程式碼

跳轉到語言設定頁面:

Context.goToLanguagePage()
複製程式碼

跳轉到無障礙服務設定頁面:

Context.goToAccessibilitySetting()
複製程式碼

瀏覽器開啟指定網頁:

Context.openBrowser(url: String)
複製程式碼

安裝 apk :

// need android.permission.REQUEST_INSTALL_PACKAGES after N
Context.installApk(apkFile: File)
複製程式碼

在應用商店中開啟應用:

Context.openInAppStore(packageName: String = this.packageName)
複製程式碼

啟動 App :

Context.openApp(packageName: String)
複製程式碼

解除安裝 App :(好像並沒有什麼用)

Context.uninstallApp(packageName: String)
複製程式碼

Log 相關

fun String.logv(tag: String = TAG) = log(LEVEL.V, tag, this)
fun String.logd(tag: String = TAG) = log(LEVEL.D, tag, this)
fun String.logi(tag: String = TAG) = log(LEVEL.I, tag, this)
fun String.logw(tag: String = TAG) = log(LEVEL.W, tag, this)
fun String.loge(tag: String = TAG) = log(LEVEL.E, tag, this)

private fun log(level: LEVEL, tag: String, message: String) {
    when (level) {
        LEVEL.V -> Log.v(tag, message)
        LEVEL.D -> Log.d(tag, message)
        LEVEL.I -> Log.i(tag, message)
        LEVEL.W -> Log.w(tag, message)
        LEVEL.E -> Log.e(tag, message)
    }
}
複製程式碼

tag 預設為 ktx,你也可以在引數中自己指定。

"abc".logv()
"def".loge(tag = "xxx")
複製程式碼

SystemService 相關

原來我們是這樣獲取系統服務的:

val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
複製程式碼

其實也挺簡潔的,但是我們忘記了 Context.WINDOW_SERVICE 咋辦?通過擴充套件屬性可以讓它更優雅。

val Context.powerManager get() = getSystemService<PowerManager>()

inline fun <reified T> Context.getSystemService(): T? =
    ContextCompat.getSystemService(this, T::class.java)
複製程式碼

對於使用者來說,直接使用 powerManager 即可,無需任何額外工作。

已作為擴充套件屬性的系統服務如下:

val Context.windowManager
val Context.clipboardManager
val Context.layoutInflater
val Context.activityManager
val Context.powerManager
val Context.alarmManager
val Context.notificationManager
val Context.keyguardManager
val Context.locationManager
val Context.searchManager
val Context.storageManager
val Context.vibrator
val Context.connectivityManager
val Context.wifiManager
val Context.audioManager
val Context.mediaRouter
val Context.telephonyManager
val Context.sensorManager
val Context.subscriptionManager
val Context.carrierConfigManager
val Context.inputMethodManager
val Context.uiModeManager
val Context.downloadManager
val Context.batteryManager
val Context.jobScheduler
複製程式碼

App 相關

Context.versionName: String
Context.versionCode: Long
Context.getAppInfo(apkPath: String): AppInfo
Context.getAppInfos(apkFolderPath: String): List<AppInfo>
Context.getAppSignature(packageName: String = this.packageName): ByteArray?
複製程式碼

這一塊內容暫時還比較少,主要是整理了我在專案中遇到的一些常見需求。獲取版本號,版本名稱,根據 apk 檔案獲取應用資訊,獲取應用簽名等等。App 相關的其實會有很多工具類,後續會慢慢補充。

View 相關

View.visible()
View.invisible()
View.gone()
var View.isVisible: Boolean
var View.isInvisible: Boolean
var View.isGone: Boolean
View.setPadding(@Px size: Int)
View.postDelayed(delayInMillis: Long, crossinline action: () -> Unit): Runnable
View.toBitmap(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap
複製程式碼

也都是一些比較常用的函式。

Common

Context.dp2px(dp: Float): Int
Context.px2dp(px: Float): Int
View.dp2px(dp: Float): Int
View.px2dp(px: Float): Int
Context.screenWidth
Context.screenHeight
Context.copyToClipboard(label: String, text: String)
複製程式碼

其他

RecyclerView.itemPadding(top: Int, bottom: Int, left: Int = 0, right: Int = 0)
ByteArray.toHexString(): String
...... 
複製程式碼

使用

implementation 'luyao.util.ktx:AndroidUtilKTX:0.0.5'
複製程式碼

總結

以上都是我在專案中提煉出來的,也正因如此,現在的 AndroidUtilCodeKTX 還只是個孩子,做不到那麼的盡善盡美,面面俱到。我會持續更新完善這個庫,也希望大家可以多多提 issuepr,提供您寶貴的意見!

在這裡也十分感謝 Jie Smart 昨天的郵件反饋,提供了一些修改意見,也讓我更有動力去繼續維護。另外,最新版本的 AndroidUtilCodeKTX 我都會第一時間用在我的開源專案 wanandroid 上,歡迎大家 star ,fork !

文章首發微信公眾號: 秉心說 , 專注 Java 、 Android 原創知識分享,LeetCode 題解。

更多相關知識,掃碼關注我吧!

AndroidUtilCodeKTX !是時候提升你的開發效率了 !(持續更新中...)

相關文章