1.Kotlin文档,
- 官方文档
- Google官方说Kotlin First,
- 中文博客,
- 练习网站,
- 入坑 Kotlin 开发前,这些项目能让你少走弯路
- Kotlin 条件控制
2.kotlin使用Dagger2
-
Kotlin
中使用Dagger2
可能导致错误"Dagger does not support injection into private fields"
https://blog.csdn.net/xuhanbing/article/details/76212338 - 如果是新项目,建议使用koin进行注入
3.java中调用Kotion写的控件失败
原因:没有引入kotlin相关插件
解决:首先在Project
的build.gradle
中的dependencies
加入 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.21"
,然后在Module
的build.gradle
中引入插件plugins { id 'com.android.application' id 'kotlin-android' }
4.不要再使用Kotlin Extensions Gradle
插件,推荐使用ViewDatabind
优雅地封装和使用 ViewBinding
5.插件
名称 | 描述 |
---|---|
JsonToKotlinClass | Json数据格式化Kotlin的插件 |
BugKotlinDocument-Github | 输入/**就出来注释 |
Android Studio离线安装插件plugins
6.创建一个Handler
private var mHandler = Handler(Looper.getMainLooper()) {
false
}
注意1:要在内部使用mHandler.sendEmptyMessage(0)
,可以通过msg.target
得到mHandler
对象后发送
注意2:mainLooper
可能为空,你要确定你是在主线程调用此mHandler
,否则推荐使用Looper.getMainLooper()
7.适用于Kotlin的依赖注入框架Koin,用于替代Dragger2
koin-Github
适用于Kotlin的超好用依赖注入框架
8.库
说明 | 引入路径 |
---|---|
生命周期-数据绑定ViewModel |
官网ViewModelimplementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' |
协程 | 官网协程简介 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" |
依赖注入框架 | koin,适用于Kotlin中替代Dragger,博客介绍Koin |
数据存储 | MMKV参考:SharedPreferences替换:MMKV集成与原理,一文读懂 SharedPreferences 的缺陷及一点点思考 |
图片加载 | coil用kotlin 写的图片加载库,可替代Glide |
9.DSL篇
Kotlin之美——DSL篇
10.网络
使用协程,让网络世界更加美好
Retrofit + Kotlin请求接口时,遇到问题:Parameter type must not include a type variable or wildcard
使用Moshi更好地兼容Kotlin空安全
11.协程
kotlin coroutines guide-官方文档
Kotlin 协程-掘金博客
Kotlin Coroutines(协程)讲解
Kotlin Jetpack 实战:图解协程原理
- Flow
深入学习Kotlin之Flow(一),什么是Flow?Flow的基本使用
Kotlin官方文档flow异步流
官方推荐 Flow 取代 LiveData,有必要吗?
理解协程、LiveData 和 Flow
Android app中这样用flow更方便-加载列表数据
实战 | 使用 Kotlin Flow 构建数据流 "管道"
从 LiveData 迁移到 Kotlin 数据流
12.如何使用kapt
apply plugin: 'kotlin-kapt'
kapt {
generateStubs = true
}
kapt 'androidx.room:room-compiler:2.2.3'
注意:annotationProcessor
和kapt
不能共存,如果你是java
和Kotlin
混用的项目,那么使用了kapt
后,其他的使用annotationProcessor
也要改为kapt
,不然构建项目就会报报错(我就是因为这个原因,搞了一天)
13.KTX扩展项
官方KTX介绍
14.泛型
我的Kotlin 学习之路(七)Kotlin之泛型、泛型约束、协变(Variance)
15.扩展函数
inline、noinline、crossinline傻傻分不清楚
16.序列化parcelize
首先引入插件
apply plugin: 'kotlin-parcelize'
然后,需要序列化的类:
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
class UserBean(val name:String,val id:Int):Parcelable
17.单例模式
Kotlin中带参的单例模式
18.Java 中的写法在Kotlin 中是怎么写的?
Java 中的写法在Kotlin 中是怎么写的?
19.Kotlin for循环的几种使用方法
Kotlin for循环的几种使用方法
20.Jetpack Compose
Jetpack Compose Beta 版现已发布!-掘金翻译的官方文档
Jetpack Compose 环境基础,编写示例
21.数据存储
当前最好用的用于替代SharedPreferences
的存储是MMKV
,由于MMKV
存储对象必须是实现了Parcelable
的对象,而如果你还保留了大量java
代码写的只实现了Serializable
接口的对象。可用用如下方式读写:
/**
* desc:保存对象
* @param key
* @param obj
* @remind 要保存的对象,只能保存实现了serializable的对象
*/
@JvmStatic
fun saveObject(key: String?, obj: Serializable?) {
try {
//先将序列化结果写到byte缓存中,其实就分配一个内存空间
val bos = ByteArrayOutputStream()
val os = ObjectOutputStream(bos)
//将对象序列化写入byte缓存
os.writeObject(obj)
//将序列化的数据转为16进制保存
val bytesToHexString = bytesToHexString(bos.toByteArray())
//保存该16进制数组
mmkv.putString(key, bytesToHexString)
} catch (e: IOException) {
e.printStackTrace()
}
}
@JvmStatic
fun readObject(key: String?): T? {
val string = mmkv.getString(key, "")
string?.let {
//将16进制的数据转为数组,准备反序列化
return try {
val stringToBytes = StringToBytes(string)
val bis = ByteArrayInputStream(stringToBytes)
val ois = ObjectInputStream(bis)
//返回反序列化得到的对象
ois.readObject() as T
} catch (e: Exception) {
null
}
}
return null
}
/**
* desc:将数组转为16进制
* @param bArray
* @return modified:
*/
private fun bytesToHexString(bArray: ByteArray?): String? {
if (bArray == null) return null
if (bArray.isEmpty()) return ""
val sb = StringBuffer(bArray.size)
var sTemp: String
for (i in bArray.indices) {
sTemp = Integer.toHexString(0xFF and bArray[i].toInt())
if (sTemp.length < 2) sb.append(0)
sb.append(sTemp.toUpperCase(Locale.getDefault()))
}
return sb.toString()
}
/**
* 把16进制字符串转换成字节数组 @param hex @return
*/
private fun StringToBytes(hex: String): ByteArray {
val len = hex.length / 2
val result = ByteArray(len)
val achar = hex.toCharArray()
for (i in 0 until len) {
val pos = i * 2
val toBe1 = toByte(achar[pos])
val a = toBe1 shl 4
val b = toByte(achar[pos + 1])
result[i] = ((a or b).toByte())
}
return result
}
private fun toByte(c: Char): Int {
return "0123456789ABCDEF".indexOf(c)
}
但建议还是把对象改为实现Parcelable
,因为Parcelable
的效率是Serializable
的10多倍。
而且用kotlin
实现也很方便,参考上面16
22.坑
1.如果要使用@BindingAdapter
对数据进行绑定,那么需要添加@JvmStatic
注释,否则报错
//普通网络图片
@JvmStatic
@BindingAdapter("url")
fun bindUrl(view: ImageView, imageUrl: String?) {
Glide.with(view).load(imageUrl).into(view)
}
2.java
一键转kotlin
后会出现很多!!
标记的内容,使用了!!
的字段要非常确定不为空null
,
解决方案:让你的 Kotlin 代码远离 !!
3.友盟统计SDK
不支持kotlin
,以前是java
的时候有数据,改为kotlin
的一周,发现没有统计数据了
解决:MyApplication
改用java
代码编写,然后在里面做友盟的初始化操作
4.高德地图黑屏:神奇的bug
最开始以为是kotlin的问题,用了java写页面,问题依旧,最后发现三个条件导致页面黑屏,a.文本数据设置中包含了特殊字符:()或· b.使用了高德地图mapView.onCreate(savedInstanceState),c.红米k30手机(目前测试其他手机正常)
- 单独使用地图,没有问题(说明高德没有)
- 单独对文本设置包含了特殊字符的文本没有问题(说明可以设置特殊字符的文本:重庆市渝中区菜袁路渝中区旭庆·江湾国际花都(菜袁路西))
- 不使用红米手机K30,设置了地图也设置了包含了特殊字符的文本也没问题
最终结论:手机有问题
解决方案一:去掉特殊文本中的特殊字符:重庆市渝中区菜袁路渝中区旭庆江湾国际花都菜袁路西(不合理,显示的内容缺少)
解决方案二:不使用红米手机(测试不干)
解决方案三:猜想黑屏是渲染导致,那么可以延迟处理其中一方,地图因为要跟随onCreate的创建而绑定,没法弄,于是考虑延迟300豪秒来设置特殊文本到文本控件中
23.关键字
Kotlin系列之let、with、run、apply、also函数的使用
字段 | 用法 | 说明 |
---|---|---|
also |
object.also{//todo} |
also 函数返回的则是传入对象的本身,和let 函数使用类似 |
24.MVVM记录关键点
- ViewModel
ViewModel-Google官网
ViewModel
【背上Jetpack之ViewModel】即使您不使用MVVM也要了解ViewModel ——ViewModel 的职能边界 - LivewData
LiveData数据改变时通知相应的界面代码进行更新
1.要绑定每次数据的更新,需要如下代码
binding.setLifecycleOwner(this)//关键代码:绑定每次LiveData数据的更新
2.如果想多个页面使用观察同一个数据,可以设置此数据为静态变量
companion object{
private var userData = MutableLiveData()
}
25.代码混淆
Android 代码混淆,到底做了什么?
26.zxing二维码扫描
zxing-android-embedded
27.RecyclerView 配合 DiffUtil,好用到飞起
https://www.cnblogs.com/plokmju/p/7385136.html
28.Fragment的添加,删除,替换,https://cloud.tencent.com/developer/article/1036708
29.在Kotlin中对空字段的处理
val submitTime:String=""
get() = if(TextUtils.isEmpty(field))"--" else TimeUtils.ymdhmsToYmdhm(field)
30.kotlin 字符串_10个有用的Kotlin字符串扩展
https://blog.csdn.net/weixin_26727575/article/details/108497391
31.AS编译报错:More than one file was found with OS independent path META-INF/library_release.kotlin_module
https://blog.csdn.net/jabony/article/details/112930562
32.kotlin - 实现静态单例的方式
https://www.jianshu.com/p/fe44743d0f01
33.hook:无所不能的 hook,让应用不再崩溃
https://mp.weixin.qq.com/s/6IgiJQEWUvfzxfP1gJFVLQ
https://github.com/eleme/lancet
34.构建异常:
引入最新ExoPlayer:The minCompileSdk (31) specified in adependency's AAR metadata (META-INF/com/android/build/gradle/aar-metadata.properties)is greater than this module's compileSdkVersion (android-30).Dependency: androidx.core:core:1.7.0.
解决一:https://www.cjavapy.com/article/2241/【无效】
解决二:我是因为引入了implementation 'com.google.android.exoplayer:exoplayer:2.16.1'
去掉后即可
修改buildToolsVersion '31.0.0'后报错,Installed Build Tools revision 31.0.0 is corrupted. Remove and install again
解决:https://blog.n0ts.cn/1435.html
修改compileSdkVersion 31和 targetSdkVersion 31报错:> Manifest merger failed : Apps targeting Android 12 and higher are required to specify an explicit value for
android:exportedwhen the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.
解决: 给有过滤器的页面添加android:exported
属性
添加:android:exported="true"
后依然报错
解决:
异常:The minCompileSdk (31) specified in adependency's AAR metadata (META-INF/com/android/build/gradle/aar-metadata.properties)
配置冲突:https://www.cjavapy.com/article/2241/
35.自定义View-第十四步:setShadowLayer阴影与SetMaskFilter发光效果
https://www.jianshu.com/p/2f1024f9c554
36.过时的OnActivityResult替代品registerForActivityResult
https://www.jianshu.com/p/b6798dcf090a
ActivityResultContract
抽象类
ActivityResultCallback
接口回调
ActivityResultRegistry
抽象类
@NonNull
@Override
public final ActivityResultLauncher registerForActivityResult(
@NonNull ActivityResultContract contract,
@NonNull ActivityResultCallback callback) {
return registerForActivityResult(contract, mActivityResultRegistry, callback);
}
@NonNull
@Override
public final ActivityResultLauncher registerForActivityResult(
@NonNull final ActivityResultContract contract,
@NonNull final ActivityResultRegistry registry,
@NonNull final ActivityResultCallback callback) {
return registry.register(
"activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
}