Android常用应用架构

Android

Posted by wushiqian on August 22, 2019

MVC

由于在 Android 中 xml 布局的功能性太弱,所以 Activity 承担了绝大部分的工作.

总结:

  • 具有一定的分层,model 解耦,controller 和 view 并没有解耦
  • controller 和 view 在 Android 中无法做到彻底分离,Controller 变得臃肿不堪
  • 易于理解、开发速度快、可维护性高

MVP

Android MVP 快速集成框架

通过引入接口 BaseView,让相应的视图组件如 Activity,Fragment去实现 BaseView,把业务逻辑放在 presenter 层中,弱化 Model 只有跟 view 相关的操作都由 View 层去完成。

总结:

  • 彻底解决了 MVC 中 View 和 Controller 傻傻分不清楚的问题
  • 但是随着业务逻辑的增加,一个页面可能会非常复杂,UI 的改变是非常多,会有非常多的 case,这样就会造成 View 的接口会很庞大
  • 更容易单元测试

MVVM-Jetpack

在 MVP 中 View 和 Presenter 要相互持有,方便调用对方,而在 MVP 中 View 和 ViewModel 通过 Binding 进行关联,他们之前的关联处理通过 DataBinding 完成。

总结:

  • 很好的解决了 MVC 和 MVP 的问题
  • 视图状态较多,ViewModel 的构建和维护的成本都会比较高
  • 但是由于数据和视图的双向绑定,导致出现问题时不太好定位来源

Android Jetpack系列教程

即学即用Android Jetpack - Navigation

谷歌实验室

Android官方架构组件Navigation:大巧不工的Fragment管理框架

Android Jetpack 导航组件 | Android 中文教学视频

Data Binding

即学即用Android Jetpack - Data Binding

DataBinding最全使用说明

ViewModelsa & LiveData

[Android Jetpack - ViewModel 中文教学视频](https://www.bilibili.com/video/av29949898)
[Android Jetpack: LiveData 和 Lifecycle 介绍 中文教学视频](https://www.bilibili.com/video/av29949898)

即学即用Android Jetpack - ViewModel & LiveData

Lifecycle

Room

即学即用Android Jetpack - Room

Android Room 框架学习

[Android Jetpack Room 中文教学视频](https://www.bilibili.com/video/av30617550)

Paging

即学即用Android Jetpack - Paging

Android官方架构组件Paging:分页库的设计美学

[Android Jetpack: 分页库 (Paging Library) 中文教学视频](https://www.bilibili.com/video/av35089294)

WorkManger

即学即用Android Jetpack - WorkManger

[Android Jetpack WorkManager Android 中文教学视频](https://www.bilibili.com/video/av56276889)

Jetpack

架构

使用示例

使用示例是Google的官方示例SunFlower

build.gradle

android {
    ···
    dataBinding {
        enabled = true
    }
}
dependencies {
    ···
    implementation "androidx.fragment:fragment-ktx:$rootProject.fragmentVersion"
    implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.lifecycleVersion"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.lifecycleVersion"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion"
}

fragment_plant_detail.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.google.samples.apps.sunflower.viewmodels.PlantDetailViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            ···
            android:text="@{viewModel.plant.name}"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

PlantDetailFragment.kt

class PlantDetailFragment : Fragment() {

    private val args: PlantDetailFragmentArgs by navArgs()
    private lateinit var shareText: String

    private val plantDetailViewModel: PlantDetailViewModel by viewModels {
        InjectorUtils.providePlantDetailViewModelFactory(requireActivity(), args.plantId)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
                inflater, R.layout.fragment_plant_detail, container, false).apply {
            viewModel = plantDetailViewModel
            lifecycleOwner = this@PlantDetailFragment
        }

        plantDetailViewModel.plant.observe(this) { plant ->
            // 更新相关 UI
        }

        return binding.root
    }
}

Plant.kt

data class Plant (
    val name: String
)

PlantDetailViewModel.kt

class PlantDetailViewModel(
    plantRepository: PlantRepository,
    private val plantId: String
) : ViewModel() {

    val plant: LiveData<Plant>

    override fun onCleared() {
        super.onCleared()
        viewModelScope.cancel()
    }

    init {
        plant = plantRepository.getPlant(plantId)
    }
}

PlantDetailViewModelFactory.kt

class PlantDetailViewModelFactory(
    private val plantRepository: PlantRepository,
    private val plantId: String
) : ViewModelProvider.NewInstanceFactory() {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return PlantDetailViewModel(plantRepository, plantId) as T
    }
}

InjectorUtils.kt

object InjectorUtils {
    private fun getPlantRepository(context: Context): PlantRepository {
        ···
    }

    fun providePlantDetailViewModelFactory(
        context: Context,
        plantId: String
    ): PlantDetailViewModelFactory {
        return PlantDetailViewModelFactory(getPlantRepository(context), plantId)
    }
}