前言
第一次接触 Kotlin
还是 2017 年,当时 Kotlin 还没扶正,也不是 Android 的官方开发语言。至于我是怎么被安利的,没记错的话,应该是 开源实验室 的 Kotlin 教程。当时身边几乎没有人在学 Kotlin,网上相关的资料也很少,我还翻译了一部分官网文档,写了一本 GitBook 。 当然现在有更好的 Kotlin 语言中文站 了,对于英文基础不是很好的同学,这是一个不错的入门资料。
两年半时间过来了,Kotlin 摇身一变,稳坐 Android 官方开发语言。尽管可能是为了避免与 Oracle 的诉讼,但我相信这绝对不是主要原因。被定义为 Better Java
的 Kotlin,的确做到了更好,也逐渐为更多人所使用。
越来越多的知名三方库都已经开始提供对 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 相关
给 String
和 ByteArray
提供了扩展方法,可以快捷的进行哈希。
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
, sha512
。MD5
已经不再安全,密码学上来说已经不再推荐使用。
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 还只是个孩子,做不到那么的尽善尽美,面面俱到。我会持续更新完善这个库,也希望大家可以多多提 issue 和 pr,提供您宝贵的意见!
在这里也十分感谢 Jie Smart 昨天的邮件反馈,提供了一些修改意见,也让我更有动力去继续维护。另外,最新版本的 AndroidUtilCodeKTX 我都会第一时间用在我的开源项目 wanandroid 上,欢迎大家 star ,fork !