安卓& Kotlin Books 高级Android应用架构

12
使用Android架构组件的MVVM示例 由Aldo Olivares撰写

在上一章中,您了解了MVVM如何通过了解模型,视图和ViewModel如何相互作用,以及它们的职责和限制来工作。

在本章中,您将使用新收购的知识来重建电影应用程序来使用MVVM来集成 看法 model. LiveData. 组件来自 Android建筑组件 或者 AAC. .

到本章末尾,您将学习:

  • 如何将应用程序从MVC架构迁移到MVVM架构。
  • 如何将ViewModel与应用程序的视图层集成。
  • 如何将模型与ViewModels集成。
  • 如何为数据源创建一个集中式存储库。
  • 如何使用LiveData使用WebServices或API的异步响应。
  • 以及更多!

入门

首先打开本章的入门项目。如果您还没有这样做,需要一些时间熟悉代码。

笔记 :您可能会注意到Starter项目看起来与前一章中的一个不同。别担心,这意思是为本章节给你一个开始主管,我们很快就会探索它。

数据 包有三个与您的应用后端相关的包:

  • D b 包包含您房间数据库所需的文件: moviedatabase.kt.moviedai.kt. files.
  • 模型 包包含应用程序的型号: 电影 模型和 moviEriponse. model.
  • 包包含改装传送的文件所需的文件 TMDB. 网络服务: movieapi.kt.RetrofitClient.kt..

笔记 :为了在Wewatch应用程序中搜索电影,您必须首先从电影DB获取API密钥。要获取API自己的密钥,请注册帐户 www.themoviedb.org。然后,在网站上导航到您的帐户设置,查看API的设置,并注册键API开发人员。收到API密钥后,打开本章的入门项目并导航到 RetrofitClient.kt.. There, you can replace the existing value for API_KEY with your own.

看法 包包含与应用程序前端相关的三个包,例如活动和适配器。

您需要一直熟悉该项目。您将在每个文件上花费大量时间。

一旦你准备好了, 建造 设备或模拟器上的应用程序在操作中看到它。您现在应该看到基本应用程序运行。

当前的架构图层

在对应用程序的代码进行任何更改之前,请快速查看当前的体系结构,只是为了刷新:

创建电影存储库

您将首先创建一个集中式存储库来检索您的应用程序的电影 TMDB. API. 从你的 房间 database.

def lifecycle_version = '2.0.0-rc01'
implementation " 和 roidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation " 和 roidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
class MovieRepositoryImpl : MovieRepository {
  //1
  private val movieDao: MovieDao = db.movieDao()
  private val retrofitClient = RetrofitClient()
  private val allMovies: LiveData<List<Movie>>
  //2
  init {
    allMovies = movieDao.getAll()
  }
  //3
  override fun deleteMovie(movie: Movie) {
    thread {
      db.movieDao().delete(movie.id)
    }
  }
  //4
  override fun getSavedMovies() = allMovies
  //5
  override fun saveMovie(movie: Movie) {
    thread {
      movieDao.insert(movie)
    }
  }
  //6
  override fun searchMovies(query: String): LiveData<List<Movie>?> {
    val data = MutableLiveData<List<Movie>>()

    retrofitClient.searchMovies(query).enqueue(object : Callback<MoviesResponse> {
      override fun onFailure(call: Call<MoviesResponse>, t: Throwable) {
        data.value = null
        Log.d(this.javaClass.simpleName, "Failure")
      }

      override fun onResponse(call: Call<MoviesResponse>, response: Response<MoviesResponse>) {
        data.value = response.body()?.results
        Log.d(this.javaClass.simpleName, "Response: ${response.body()?.results}")
      }
    })
    return data
  }
}
fun getMovieRepository(): MovieRepository = MovieRepository(this)

创建ViewModels.

虽然可以从您的视图中访问电影存储库,但通常认为使您的活动或片段直接通信到您的后端的不良实践。

//1
class AddViewModel(private val repository: MovieRepository = MovieRepositoryImpl()): ViewModel()  {
  //2
  fun saveMovie(movie: Movie) {
    repository.saveMovie(movie)
  }
}
private lateinit var viewModel: AddViewModel
 看法 Model = ViewModelProviders.of(this).get(AddViewModel::class.java)
fun addMovieClicked(view: View) {
  if (titleEditText.text.toString().isNotBlank()) {
    viewModel.saveMovie(Movie(
      title= titleEditText.text.toString(),
      releaseDate = yearEditText.text.toString()))
    finish()
  } else {
    showMessage(getString(R.string.enter_title))
  }
}

使用LiveData与ViewModels

In an earlier section, you added a method named searchMovie() to your repository which returned a LiveData list of movies, but what is LiveData?

fun getUsers(): List<User> {
  return userDao().getAll()
}
fun getUsers(): LiveData<List<User>> {
  return users
}
getUsers().observe(this, Observer { users ->
  //Update UI with list of users
})
class MainViewModel(private val repository: MovieRepository = MovieRepositoryImpl()) : ViewModel() {
  //1
  private val allMovies = MediatorLiveData<List<Movie>>()
  //2
  init {
    getAllMovies()
  }
  //3
  fun getSavedMovies() = allMovies
  //4
  private fun getAllMovies() {
    allMovies.addSource(repository.getSavedMovies()) { movies ->
      allMovies.postValue(movies)
    }
  }
  //5
  fun deleteSavedMovies(movie: Movie) {
    repository.deleteMovie(movie)
  }
}
private lateinit var viewModel: MainViewModel
 看法 Model = ViewModelProviders.of(this).get(MainViewModel::class.java)
showLoading()
viewModel.getSavedMovies().observe(this, Observer { movies ->
  hideLoading()
  movies?.let {
    adapter.setMovies(movies)
  }
})
for (movie in adapter.selectedMovies) {
  viewModel.deleteSavedMovies(movie)
}

创建SearchViewModel.

在创建新ViewModel之前打开 movierepository.kt. file and analyze the searchMovies() method bit by bit:

override fun searchMovies(query: String): LiveData<List<Movie>?> {
  //1  
  val data = MutableLiveData<List<Movie>>()
  //2
  retrofitClient.searchMovies(query).enqueue(object : Callback<MoviesResponse> {
    //3
    override fun onFailure(call: Call<MoviesResponse>, t: Throwable) {
      data.value = null
      Log.d(this.javaClass.simpleName, "Failure")
    }
    //4
    override fun onResponse(call: Call<MoviesResponse>, response: Response<MoviesResponse>) {
      data.value = response.body()?.results
      Log.d(this.javaClass.simpleName, "Response: ${response.body()?.results}")
    }
  })
  return data
}
class SearchViewModel(private val repository: MovieRepository = MovieRepositoryImpl()): ViewModel()  {

  fun searchMovie(query: String): LiveData<List<Movie>?> {
    return repository.searchMovies(query)
  }

  fun saveMovie(movie: Movie) {
    repository.saveMovie(movie)
  }
}
private lateinit var viewModel: SearchViewModel
 看法 Model = ViewModelProviders.of(this).get(SearchViewModel::class.java)
private fun searchMovie() {
  showLoading()
  viewModel.searchMovie(title).observe(this, Observer { movies ->
    hideLoading()
    if (movies == null) {
      showMessage()
    } else {
      adapter.setMovies(movies)
    }
  })
}
 看法 Model.saveMovie(movie)
searchMovie()
searchMovie()

MVVM架构

在本章的开头,您可以在没有MVVM的情况下看到当前应用程序的架构。

关键点

  • 看法 model. class is designed to store and manage UI-related data in a lifecycle-aware way.
  • 看法 model. class allows data to survive configuration changes, such as screen rotations.
  • LiveData. 是数据持有者类,就像列表或hashmap,可以在给定生命周期内的任何更改中观察到。
  • 具有像MVVM这样的强大架构使您的代码可扩展且易于维护。

然后去哪儿?

虽然重构应用程序可能看起来像是一个令人生畏的任务,但它在长期运行中得到了回报。具有像MVVM这样的强大架构使您的代码可扩展且易于维护。

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里 .

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC

您可以免费读取,本章的部分显示为 混淆了 文本。解锁这本书,以及我们整个书籍和视频目录,带有Raywenderlich.com的专业订阅。

现在解锁

要突出或记笔记,您需要在订阅中拥有这本书或自行购买。