Android

Abstract #

MVB is my new architecture standing for Model-View-Bridge. Now, it only has an Android implementation, but the pattern is applicable to every platform that supports Kotlin. The UI part uses the traditional View without DataBinding, and the Kt part hides ViewModel, resulting in much less code and clearer logic.

Other points:

  • Compose is the most concise declarative UI framework, and also the most flexible in terms of custom views. However, its recombination mechanism inevitably reconstructs massive objects, resulting in its low upper performance limit.

  • Regarding navigation parameters for Activity / Fragment, I have a proposal on Reddit.

  • RecyclerView.Adapter is the most common among components with complex writing styles, whereas my encapsulated KRecyclerViewAdapter arranges list items as Compose.

  • For the UI part, I have a new design pattern proposal of which the expected effect is much better than Compose and Flutter.

Overall, I suggest MVB if you haven’t switched to Compose from View at present. In the future, there would be a new imperative UI mode based on that proposal implementation in which MVB mode would still work.

Setup #

android{
    buildFeatures {
        viewBinding true
    }
}

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach{
    kotlinOptions.freeCompilerArgs += "-Xcontext-receivers"
}

dependencies {
    implementation 'io.github.shawxingkwok:android-util-view:1.0.8'
    implementation 'io.github.shawxingkwok:mvb-android:1.0.7'
}
android{
    buildFeatures {
        viewBinding = true
    }
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
    kotlinOptions.freeCompilerArgs += "-Xcontext-receivers"
}

dependencies {
    implementation("io.github.shawxingkwok:android-util-view:1.0.8")
    implementation("io.github.shawxingkwok:mvb-android:1.0.7")
}`

Usage #

Examples are all with Fragment, but also applicable on ComponentActivity (a super class of AppCompatAtivity).

rmb #

This is the abbreviation of remember, meaning the value is alive across the configuration change (e.g. rotation). Switch from the traditional way as below.

Initialization of the property declared with val is synchronized.

save #

Direct #

Saved values are not only alive across the configuration change, but also restorable from the killed process.

Saved value types should follow Parcelize rules. Please note that there is a mistake in the official document. Set is actually saved as Serialziable and can’t keep Parcelable.
I suggest to use Flow instead of LiveData, though it takes some time to learn.
Rules below are same to saveMutableStateFlow, saveMutableSharedFlow, saveMutableLiveData, and not limited with val / var.

Transform #

You could also save values with any type by appending transform after save with the converted values following Parcelize rules.

The lambda convert keeps alive after its lifecycle owner is killed by the application. Therefore, don’t reference callables from its owner to avoid memory leaks.

Parcelable component #

Sometimes you need to pass KClass<out Parcelable> as the parcelable component. Besides, there could be no more than 1 parcelable subtype.

observe #

The delegated values via rmb / save are easily observable if it’s Flow / LiveData. The latter observe lambdas are active between every onStart and onStop, which is generally used for linking value to UI state.

mvbScope #

This is viewModelScope of the hidden MVBViewModel. This coroutineScope is not affected by the configuration change, and cancelled only when its owner ComponentActivity / Fragment is killed by user presses back or low memory. Besides, it’s not static but actually an extensive property. Check out the source code, and you will find there is no memory leak.

Format #

Take an example of simulating the stopwatch page in the IOS clock with concise code below.

Utils and Components for reusable and complex parts #


See KRecyclerViewAdapter if it’s new to you.

Static processing #

For example, declaring components and enabling them in ComponentActivity.onCreate / Fragment.onViewCreated

Bridge #

Declaring variable data sources via rmb and save, and linking them to UI state via appending observe.

Fixed listeners #

I suggest to put fixed listeners here or below, allowing for variating data source or navigating to other pages.

Or this style

Some functions from other libraries are independent with MVB, e.g. binding(XxBinding::bind), onClick, and updateIf.

GitHub repo #