又是被 KMM 吊起來錘的一個月,
上次提到 MVIKotlin 能用卻難用,畢竟 Jetpack AAC 太好用了,于是我決定手動把 Jetpack AAC 移植到 KMM,由于 LiveData 可以使用 kotlinx.coroutines 的 StateFlow/SharedFlow 代替,所以理論上只需移植 Lifecycle 和 ViewModel 即可,Lifecycle 通過注解某個函式即可讓該函式在相應生命周期執行時即可運行的功能也要暫時閹割,因為注解處理器要能在 KMM 平臺使用必須使用 KCP,這個比較困難,可以先往后放放,
這樣算下來一共是閹割版的 Lifecycle 與 ViewModel 兩個庫需要移植,我希望移植版本的公有 API 與 Jetpack 版本盡可能一致,最好還能完全兼容 Jetpack,那基本的移植方式就是,先在 Common source set 用 expect 宣告公有 API,如果 API 是頂層函式/靜態方法,則在 Android source set 的 actual 實作中直接呼叫 Jetpack 版本就行了,在 iOS source set 的 actual 實作中可以直接仿寫 Jetpack 的實作代碼,然后把平臺相關的代碼魔改即可,如果 API 是類/介面等型別宣告,在 Android source set 的 actual 實作中直接用 typealias 等價橋接到 Jetpack 內的型別即可,而在 iOS source set 中仍然是仿寫 Jetpack 的宣告與實作代碼重新寫一份,從理論上來說,由于型別都用 typealias 橋接到了 Jetpack 內的宣告,那么無論是編譯期還是運行時 Common 層的 Lifecycle 與 ViewModel 都與 Jetpack 內完全等價,也我們可以省略很多實作代碼,例如 Fragment 以及 ComponentActivity 這些實作了 ViewModelStoreOwner 等介面的平臺相關組件,
理論雖然是很理想化的,但是實作起來就遇到了兩個個無解的問題:
1. Jetpack Lifecycle 與 ViewModel public API 會參考強平臺相關型別
例如 Java 反射的 Class<?>:
public open class ViewModelProvider(private val store: ViewModelStore, private val factory: Factory)
{
/**
* Implementations of `Factory` interface are responsible to instantiate ViewModels.
*/
public interface Factory {
/**
* Creates a new instance of the given `Class`.
*
* @param modelClass a `Class` whose instance is requested
* @return a newly created ViewModel
*/
public fun <T : ViewModel> create(modelClass: Class<T>): T
}
例如 ViewModelProvider 的 Factory,create 函式就需要用到 Class<?>,為了能夠在 KMM 中與其盡量保持一致,我們只能把 Class 改為 Kotlin 的 KClass<*>,但是一旦改寫,我們就無法通過 typealias 橋接 Factory,如果我們自己定義 Factory 介面,與 Jetpack 的兼容性也將受到破壞,
2. 反射在 Kotlin/Native 上不可用
上面提到 Jetpack 需要 Class<?> 物件,其根本目的是使用反射,例如使用反射創建物件:
// actually there is getInstance()
@Suppress("SingletonConstructor")
public open class NewInstanceFactory : Factory {
@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return try {
modelClass.newInstance()
} catch (e: InstantiationException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
catch (e: IllegalAccessException) {
throw RuntimeException("Cannot create an instance of $modelClass", e)
}
}
在 Kotlin 中,反射僅在 JVM 平臺完全支持,在 JS 平臺有限支持,而在 Native 平臺幾乎沒有支持,所以由于反射功能的缺失,許多 API 的設計要完全改變,例如創建物件可能需要將一個建構式或工廠函式作為引數,
我們該怎么做?
最初我們想實作兼容 Jetpack 的目的無非有三:
- 在 Android 平臺使用 Jetpack 的實作更穩定,
- 遷移現有 Android Kotlin 代碼至 KMM 更容易,
- 實作了 LifecycleOwner 或 ViewModelStoreOwner 等介面的根級 UI 組件例如 Fragment 或 Activity 不用重新定義,減少作業量,
既然兼容已經幾乎不可能實作,我們可以放棄兼容性只追求易用性,所以方案改為參照 Lifecycle 及 ViewModel 的設計自行撰寫一套 KMM 版本的架構組件,
這里先開個坑,等后面在 Github 上開個專案,如果思路大至可行,再記錄更新,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/347162.html
標籤:其他
上一篇:Android 使用Stringformat優化你的代碼吧
下一篇:安卓四大組件之二廣播
