鼓勵使用Kotlin, 鼓勵使用新特性, 提倡Kotlin嚴格Review
代碼規約 Coding Conventions
https://kotlinlang.org/docs/reference/coding-conventions.html
代碼樣例 kotlin Idiomatic
https://kotlinlang.org/docs/reference/idioms.html
Github中搜一下 kotlin Idiomatic 試試?
可能遇到的比較大的坑
代碼規范 or Bad Case【持續添加】
- 判空
nullable?.let{
//非空操作
}
//涉及bool
if(nullable?.isTrue == true) {
}
- evils 的作用范圍
val value = if(matches()) nullable :? 1 else nullable ?:2
val value = (if (matches()) nullable1 else nullable2) ?: 0
- 拒絕強制非空
//禁止出現!!強制非空操作
var value = InstanceManager.getInstance()!!.value
- 合理使用 run,let,with,apply 等
// Java 風格
fun trackFeedItemRemoved(concernId: Long, groupId: Long) {
val params = JSONObject()
params.putOpt("concern_id", concernId)
params.putOpt("group_id", groupId)
AppLogNewUtils.onEventV3("concern_topic_article_remove", params)
}
// 使用Apply
fun trackDescriptionRichContentClick(concernId: Long, text: String?) {
Bundle().apply {
putLong("concern_id", concernId)
putString("text", text)
}.let {//可以用run或者apply嗎? 邏輯上可以用,單從代碼語義上不建議**
AppLogNewUtils.onEventV3Bundle("ugc_topic_click", **it**)
}
}
- 單例
`// Object生成的是線程安全的餓漢式單例`
`// Lazy 實現線程安全的懶漢式單例`
`class SingletonDemo private constructor() { `
`companion object {`
`val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {`
` SingletonDemo() `
` } `
` } `
`}`
- kotlin可以省略Builder模式
`// Java Builder 風格的例子`
`val builder = PreLayoutTextViewConfig.builder()`
` .textViewWidth(PostTextLayoutProvider.INSTANCE.getWidthInPixel())`
` .textSize(PostTextLayoutProvider.INSTANCE.getTextSizeInPixel().toInt())`
` .textContent(content)`
` .textRichContentStr(commentBase.content_rich_span)`
` .ellipsizeContent(TTRichTextViewConfig.*ELLIPSIZE_ALL*)`
` .ellipsizeClickLength(2)`
` .maxLineCount(maxLine)`
` .defaultLineCount(defaultLine)`
`val interceptor = PostSpanInterceptor()`
`interceptor.cellRef = postCell`
`builder.interceptor(interceptor)`
`return builder.build()`
`//直接kotlin 可以省略build`
`//如果有需要從Java中調用還得寫Buidler模式...`
`val dataSource = BasicDataSource().apply **{**`
`driverClassName = "com.mysql.jdbc.Driver"`
`url = "jdbc:mysql://domain:3309/db"`
`username = "username"`
`password = "password"`
`maxTotal = 40`
`maxIdle = 40`
`minIdle = 4`
`**}**`
- 代碼可讀性
`// 隨便寫的一些代碼`
`// 用了很多kotlin函數省略了一些臨時變量,看上去很高端,但是代碼的可讀性很差`
`fun test(context: Context) {`
`*with*(View.inflate(context, R.layout.*abc*, null) as ViewGroup) **{**`
`*repeat*(*childCount*) **{**`
`*with*(getChildAt(**it**) as Button) **{**`
`*apply* **{**`
`*text* = "button$**it**"`
`setOnClickListener **{**`
`*takeIf* **{ it** == getChildAt(0) **}**?.*run* **{**`
`doClickTopButton()`
`**}**?.let **{**`
`doClickOthers()`
`**}**`
`** }**`
`** }**`
`** }**`
`** }**`
`** }**`
`}`
- 不安全的類型轉換
`// findViewById 是nullable`
`val readCountTv: TextView = itemView.findViewById(R.id.*read_count*) as TextView`
`val readCountWrapper: LinearLayout = itemView.findViewById(R.id.*read_count_wrapper*) as LinearLayout`
- kotlin 調用 Java 時, 屬性調用可能調用Java的get方法, 有些場景會有隱藏的坑
`override fun onBackPressed() {`
`//此處videoController 實際調用的是下面Java代碼的getVideoController()`
`//當videoController 為空時,反而會去創建一個新的實例`
`if (videoController?.isFullScreen == true && videoController.onBackPressed(this)) {`
`return`
` }`
` finish()`
`}`
`@Override`
`public IFeedVideoController getVideoController() {`
if (mVideoController == null) {
mVideoController = createVideoController()
initVideoListener();
if (mVideoController != null) {
mVideoController.addListener(mIVideoFullscreen);
}
}
return mVideoController;
`}`
kotlin構造函數 AS中show kotlin bytecode decode生成的java和最終打包生成的class不一致
用kotlinc 編譯了下,目前看來是一致的
- kotlin中實現if (not) null
`//空和非空都有處理時,建議還是使用Java風格的寫法 ?[kotlin if null 怎么寫更優雅](https://bytedance.feishu.cn/docs/doccnlAPdp8LyMvHrVnYj9yRb5d)? `
`var variable :Any? = null `
`if(variable != null) {`
` doSomeThing()`
`} else {`
` doSomeThingElse()`
`}`
- SDK項目中減少使用data class
`//主要是因為data支持解構,業務方如果是解構的寫法,sdk升級時如果不全量編譯可能有問題`
`//參考 https://jakewharton.com/public-api-challenges-in-kotlin/`
- Kotlin編譯后的隱式import(Kotlin的bug?),這樣可能會導致增量編譯的包有問題
`//從字節碼看有IRelationStateCallback的依賴`
`//實際Kotlin代碼中不會存在對RelationStatusCallback的直接依賴`
`byte var8;`
`label16: {`
` IRelationDepend var10000 = (IRelationDepend)ServiceManager2.getService (IRelationDepend.class);`
` if (var10000 != null) {`
`if (var10000.userIsFollowing(userId, (RelationStatusCallback)null)) {`
`var8 = 1;`
` break label16;`
` }`
` }`
`var8 = 0;`
`}`