MVVM with Kotlin

初始化项目

原文地址:https://proandroiddev.com/mvvm-with-kotlin-android-architecture-components-dagger-2-retrofit-and-rxandroid-1a4ebb38c699
项目地址:https://github.com/gahfy/MVVMPosts

image.png

Bases

添加一个base package到项目中

Lifecycle library

在项目的build.gradle文件中添加依赖

buildscript {
  ext.kotlin_version = '1.2.30'
  ext.lifecycle_version = '1.1.1'
}

接着在app/build.gradle文件中添加lifecycle依赖

dependencies {
  implementation "android.arch.lifecycle:extensions:$lifecycle_version"
}

添加BaseViewModel类

BaseViewModel

在base包中添加BaseViewModel类。

import android.arch.lifecycle.ViewModel

abstract class BaseViewModel: ViewModel(){

}

Model

定义Post对象,添加model包,然后在该包下创建Post类

/**
 * Class which provides a model for post
 * @constructor Sets all properties of the post
 * @property userId the unique identifier of the author of the post
 * @property id the unique identifier of the post
 * @property title the title of the post
 * @property body the content of the post
 */
data class Post(val userId: Int, val id: Int, val title: String, val body: String)

Retrofit

我们从JSONPlaceholder API
中获取Post列表,首先添加Retrofit的依赖到build.gradle文件中

buildscript {
  ext.kotlin_version='1.2.30'
  ext.lifecycle_version = '1.1.1'
  ext.retrofit_version = '2.4.0'
}

在app/build.gradle中添加如下依赖

dependencies {
    // ...
    
    // Retrofit
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
}

创建network包,然后创建PostApi接口用来获取Post列表。

/**
 * The interface which provides methods to get result of webservices
 */
interface PostApi {
    /**
     * Get the list of the pots from the API
     */
    @GET("/posts")
    fun getPosts(): Observable<List<Post>>
}

接下来在utils包中创建一个Kotlin文件,命名为Constants.kt用来定义一些常量,这里用来定义base url:

/** The base URL of the API */
const val BASE_URL: String = "https://jsonplaceholder.typicode.com"

Dagger2

在项目中的build.gradle文件中添加Dagger的版本号:

buildscript {
    ext.kotlin_version = '1.2.30'
    ext.lifecycle_version = '1.1.1'
    ext.retrofit_version = '2.4.0'
    ext.dagger2_version = '2.16'

    //...
}

接着在app/build.gradle文件中添加Dagger2依赖

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

// ...

dependencies {
    // ...

    // Dagger 2
    implementation "com.google.dagger:dagger:$dagger2_version"
    kapt "com.google.dagger:dagger-compiler:$dagger2_version"
    compileOnly "org.glassfish:javax.annotation:3.1.1"
}

注入Retrofit

首先创建一个module在ViewModel中注入Retrofit实例。我们将此Module命名为NetworkModule,并将其放在module包下面,module包必须在inject包下面,该包必须添加到应用程序的根包下。

我们将module和provider方法都设置成单例的以防每次使用都得初始化

/**
 * Module which provides all required dependencies about network
 */
@Module
// Safe here as we are dealing with a Dagger 2 module
@Suppress("unused")
object NetworkModule {
    /**
     * Provides the Post service implementation.
     * @param retrofit the Retrofit object used to instantiate the service
     * @return the Post service implementation.
     */
    @Provides
    @Reusable
    @JvmStatic
    internal fun providePostApi(retrofit: Retrofit): PostApi {
        return retrofit.create(PostApi::class.java)
    }

    /**
     * Provides the Retrofit object.
     * @return the Retrofit object
     */
    @Provides
    @Reusable
    @JvmStatic
    internal fun provideRetrofitInterface(): Retrofit {
        return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(MoshiConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
                .build()
    }
}

现在我们进一步开发应用程序。

Post MVVM

添加一个ui包,并且在ui包下面添加一个post包用来添加与Post相关的Views和ViewModel。

ViewModel组件和注入

创建一个PostListViewModel类用来从API获取数据并且显示在view中。

首先需要通过Dagger注入一个PostApi实例

class PostListViewModel:BaseViewModel(){
  @Inject
  lateinit var postApi: PostApi
}

然后在injection包中创建一个component包,然后创建一个ViewModelInjector:

/**
 *Component providing inject() methods for presenters.
 */
@Singleton
@Component(modules = [(NetworkModule::class)])
interface ViewModelInjector {
    /**
     * Injects required dependencies into the specified PostListViewModel.
     * @param postListViewModel PostListViewModel in which to inject the dependencies
     */
     fun inject(postListViewModel: PostListViewModel)

     @Component.Builder
     interface Builder {
        fun build(): ViewModelInjector
        fun networkModule(networkModule: NetworkModule): Builder
     }
}

我们现在需要做的是在BaseViewModel类中注入所需的依赖项

abstract class BaseViewModel:ViewModel(){
    private val injector: ViewModelInjector = DaggerViewModelInjector
            .builder()
            .networkModule(NetworkModule)
            .build()

    init {
        inject()
    }

    /**
     * Injects the required dependencies
     */
    private fun inject() {
        when (this) {
            is PostListViewModel -> injector.inject(this)
        }
    }
}

在ViewModel中获取数据

现在我们已经注入了PostApi,然后从Api中获取数据,我们需要在后台线程中执行call方法,然后在主线程执行actions方法,为了实现这个功能,我们使用RxAndroid库,在build.gradle文件中添加依赖:

dependencies {
    //...

    //Rx
    implementation "io.reactivex.rxjava2:rxjava:2.1.15"
    implementation "io.reactivex.rxjava2:rxandroid:2.0.2"
}

现在到PostListViewModel类中编写获取结果的方法:

class PostListViewModel:BaseViewModel(){
    @Inject
    lateinit var postApi: PostApi

    private lateinit var subscription: Disposable

    init{
      loadPosts()
    }

    private fun loadPosts(){
        subscription = postApi.getPosts()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe { onRetrievePostListStart() }
                .doOnTerminate { onRetrievePostListFinish() }
                .subscribe(
                        { onRetrievePostListSuccess() },
                        { onRetrievePostListError() }
                )
    }
    private fun onRetrievePostListStart(){

    }

    private fun onRetrievePostListFinish(){

    }

    private fun onRetrievePostListSuccess(){

    }

    private fun onRetrievePostListError(){

    }
}

ViewModel onCleared()

在ViewModel的onCleared方法中销毁订阅

class PostListViewModel:BaseViewModel(){
    // ...

    private lateinit var subscription: Disposable
  
    // ...

    override fun onCleared() {
        super.onCleared()
        subscription.dispose()
    }

    // ...
}

LiveData

添加一个MutableLiveData用来观察数据更新时设置ProgressBar的可见性。

class PostListViewModel:BaseViewModel(){
    // ...

    val loadingVisibility: MutableLiveData<Int> = MutableLiveData()

    // ...

    private fun onRetrievePostListStart(){
        loadingVisibility.value = View.VISIBLE
    }

    private fun onRetrievePostListFinish(){
        loadingVisibility.value = View.GONE
    }

    private fun onRetrievePostListSuccess(){

    }

    private fun onRetrievePostListError(){

    }
}

Layout,DataBinding和BindingAdapters

这一部分使用DataBinding和DataBinders,首先需要添加依赖,然后还需要用到ConstraintLayout和RecyclerView。

buildscript {
    ext.kotlin_version = '1.2.30'
    ext.lifecycle_version = '1.1.1'
    ext.retrofit_version = '2.4.0'
    ext.dagger2_version = '2.16'
    ext.android_support_version = '28.0.0-alpha3'

    // ...
}
android {
    compileSdkVersion 28
    
    dataBinding {
        enabled = true
    }
    
    // ...
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation "com.android.support:appcompat-v7:$android_support_version"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    // RecyclerView
    implementation "com.android.support:recyclerview-v7:$android_support_version"

    // Constraint Layout
    implementation "com.android.support.constraint:constraint-layout:1.1.2"

    // LiveData & ViewModel
    implementation"android.arch.lifecycle:extensions:$lifecycle_version"

    // Data binding
    kapt "com.android.databinding:compiler:3.1.3"

    // Retrofit
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"

    // Dagger 2
    implementation "com.google.dagger:dagger:$dagger2_version"
    kapt "com.google.dagger:dagger-compiler:$dagger2_version"
    compileOnly "org.glassfish:javax.annotation:3.1.1"

    //Rx
    implementation "io.reactivex.rxjava2:rxjava:2.1.15"
    implementation "io.reactivex.rxjava2:rxandroid:2.0.2"
}

创建一个layout文件activity_post_list.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="viewModel"
            type="net.gahfy.mvvmposts.ui.post.PostListViewModel" />
    </data>
    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:mutableVisibility="@{viewModel.getLoadingVisibility()}"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/post_list"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />
    </android.support.constraint.ConstraintLayout>
</layout>

正如你看到的,我们添加了属性app:mutableVisibility="@{viewModel.getLoadingVisibility()}"到ProgressBar中,这个属性不存在,所以我们需要通过BindingAdapter定义它。

在utils包中创建一个文件命名为BindingAdapters.kt,在这个文件中,我们为mutableVisibility属性定义DataBinder。

@BindingAdapter("mutableVisibility")
fun setMutableVisibility(view: View, visibility: MutableLiveData<Int>?) {
    val parentActivity: AppCompatActivity? = view.getParentActivity()
    if(parentActivity != null && visibility != null) {
        visibility.observe(parentActivity, Observer { value -> view.visibility = value?:View.VISIBLE})
    }
}

其中View.getParentActivity()方法并不存在,所以我们需要添加一个扩展方法给View类,让我们在utils包中创建一个extension包,然后创建一个ViewExtension.kt文件:

fun View.getParentActivity(): AppcompatActivity?{
    var context = this.context
    while(context is ContextWrapper){
        if (context is AppcompatActivity) {
            return context
        }
        context = context.baseContext
    }
    return null
}

PostListActivity

现在创建PostListActivity类

class PostListActivity: AppCompatActivity() {
    private lateinit var binding: ActivityPostListBinding
    private lateinit var viewModel: PostListViewModel

    override fun onCreate(savedInstanceState: Bundle?){
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_post_list)
        binding.postList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)

        viewModel = ViewModelProviders.of(this).get(PostListViewModel::class.java)
        binding.viewModel = viewModel
    }
}

Manifest

现在在清单文件中添加Internet权限并且添加Activity。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.gahfy.mvvmposts">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".ui.post.PostListActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

现在可以运行该项目,可以看到ProgressBar出现然后消失。

错误处理

当发生错误的时候用SnackBar来显示错误信息,并且允许用户重新获取posts,首先需要添加SnackBar的依赖:

// ...
dependencies {
    // ...
    
    // Support Design
    implementation "com.android.support:design:$android_support_version"
}

现在添加显示错误时将使用的字符串资源:

<resources>
    <!-- ... -->

    <string name="retry">Retry</string>
    <string name="post_error">An error occurred while loading the posts</string>
</resources>

现在添加一个MutableLiveData和一个OnClickListener属性到ViewModel中。

class PostListViewModel:BaseViewModel(){
    // ...
    val errorMessage:MutableLiveData<Int> = MutableLiveData()
    val errorClickListener = View.OnClickListener { loadPosts() }

    // ...

    private fun onRetrievePostListStart(){
        loadingVisibility.value = View.VISIBLE
        errorMessage.value = null
    }

    private fun onRetrievePostListFinish(){
        loadingVisibility.value = View.GONE
    }

    private fun onRetrievePostListSuccess(){

    }

    private fun onRetrievePostListError(){
        errorMessage.value = R.string.post_error
    }
}

然后在PostListActivity中观察errorMessage并且当不为null的时候将错误信息显示在SnackBar中,当为null时隐藏SnackBar

class PostListActivity: AppCompatActivity() {
    // ...
    private var errorSnackbar: Snackbar? = null

    override fun onCreate(savedInstanceState: Bundle?){
        // ...
    
        viewModel = ViewModelProviders.of(this).get(PostListViewModel::class.java)
        viewModel.errorMessage.observe(this, Observer {
            errorMessage -> if(errorMessage != null) showError(errorMessage) else hideError()
        })
        binding.viewModel = viewModel
    }

    private fun showError(@StringRes errorMessage:Int){
        errorSnackbar = Snackbar.make(binding.root, errorMessage, Snackbar.LENGTH_INDEFINITE)
        errorSnackbar?.setAction(R.string.retry, viewModel.errorClickListener)
        errorSnackbar?.show()
    }

    private fun hideError(){
        errorSnackbar?.dismiss()
    }
}

当你在手机飞行模式下运行应用程序,就会先看到ProgressBar然后是显示错误信息的SnackBar。

显示Posts列表

接下来显示Posts列表,首先为列表中的每一个条目创建一个ViewModel,在ui.post包下创建一个PostViewModel。

class PostViewModel:BaseViewModel() {
    private val postTitle = MutableLiveData<String>()
    private val postBody = MutableLiveData<String>()

    fun bind(post: Post){
        postTitle.value = post.title
        postBody.value = post.body
    }

    fun getPostTitle():MutableLiveData<String>{
        return postTitle
    }

    fun getPostBody():MutableLiveData<String>{
        return postBody
    }
}

现在创建item的布局,命名为item_post.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="viewModel"
            type="net.gahfy.mvvmposts.ui.post.PostViewModel" />
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="16dp"
        android:paddingRight="16dp">

        <TextView
            android:id="@+id/post_title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textStyle="bold"
            app:mutableText="@{viewModel.getPostTitle()}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/post_body"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            app:mutableText="@{viewModel.getPostBody()}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/post_title" />
    </android.support.constraint.ConstraintLayout>
</layout>

现在需要编辑BindingAdapters.kt用来添加app:mutableText属性。

Adapter和ViewModel

在ui.post包中创建PostListAdapter:

class PostListAdapter: RecyclerView.Adapter<PostListAdapter.ViewHolder>() {
    private lateinit var postList:List<Post>

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostListAdapter.ViewHolder {
        val binding: ItemPostBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.item_post, parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: PostListAdapter.ViewHolder, position: Int) {
        holder.bind(postList[position])
    }

    override fun getItemCount(): Int {
        return if(::postList.isInitialized) postList.size else 0
    }

    fun updatePostList(postList:List<Post>){
        this.postList = postList
        notifyDataSetChanged()
    }

    class ViewHolder(private val binding: ItemPostBinding):RecyclerView.ViewHolder(binding.root){
        fun bind(post:Post){
            // ...
        }
    }
}

现在让我们来关注ViewHolder类,我们知道如何将ViewModel和Activity关联,因为我们添加了View.getParentActivity()扩展方法,通过ViewModelProviders.of(binding.root.getParentActivity()).get(PostViewModel::class.java)可以特别方便的获取PostViewModel的实例,问题是与Activity关联的ViewModel是单例的,所以会让列表中每行都显示相同的数据。

因为我们使用了MutableLiveData,所以我们不必为每一行实例化一个新的PostViewModel,而只用为每个ViewHolder实例化一个就行了。

class PostListAdapter: RecyclerView.Adapter<PostListAdapter.ViewHolder>() {
    // ...

    class ViewHolder(private val binding: ItemPostBinding):RecyclerView.ViewHolder(binding.root){
        private val viewModel = PostViewModel()

        fun bind(post:Post){
            viewModel.bind(post)
            binding.viewModel = viewModel
        }
    }
}

为RecyclerView设置adapter

现在我们只需要为RecyclerView设置PostListAdapter就行了,首先编辑PostListViewModel:

class PostListViewModel:BaseViewModel(){
    //...
    val postListAdapter: PostListAdapter = PostListAdapter()

    // ...
    private fun loadPosts(){
        subscription = postApi.getPosts()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe { onRetrievePostListStart() }
                .doOnTerminate { onRetrievePostListFinish() }
                .subscribe(
                        // Add result
                        { result -> onRetrievePostListSuccess(result) },
                        { onRetrievePostListError() }
                )
    }

    private fun onRetrievePostListStart(){
        loadingVisibility.value = View.VISIBLE
        errorMessage.value = null
    }

    private fun onRetrievePostListFinish(){
        loadingVisibility.value = View.GONE
    }

    private fun onRetrievePostListSuccess(postList:List<Post>){
        postListAdapter.updatePostList(postList)
    }

    private fun onRetrievePostListError(){
        errorMessage.value = R.string.post_error
    }
}

接下来在activity_list_post布局文件中设置adapter:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="viewModel"
            type="net.gahfy.mvvmposts.ui.post.PostListViewModel" />
    </data>
    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:mutableVisibility="@{viewModel.getLoadingVisibility()}"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/post_list"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:adapter="@{viewModel.getPostListAdapter()}"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />
    </android.support.constraint.ConstraintLayout>
</layout>

然后添加一个BindingAdapter用来直接设置adapter到RecyclerView:

@BindingAdapter("adapter")
fun setAdapter(view: RecyclerView, adapter: RecyclerView.Adapter<*>) {
    view.adapter = adapter
}

现在运行程序就可以看到post列表了

Room

添加依赖

buildscript {
    ext.kotlin_version = '1.2.30'
    ext.lifecycle_version = '1.1.1'
    ext.retrofit_version = '2.4.0'
    ext.dagger2_version = '2.16'
    ext.android_support_version = '28.0.0-alpha3'
    ext.room_version = '1.1.1'
    
    // ...
}
// ...

dependencies {
    // ...

    // Room
    implementation "android.arch.persistence.room:runtime:$room_version"
    kapt "android.arch.persistence.room:compiler:$room_version"
}

Entity

现在更新Post类,用@Entity注解,可以被保存在数据库中。

@Entity
data class Post(
        val userId: Int,
        @field:PrimaryKey
        val id: Int,
        val title: String,
        val body: String
)

Dao

添加一个DAO类用来从数据库中插入和获取Posts,在model中添加一个接口命名为PostDao。

@Dao
interface PostDao {
    @get:Query("SELECT * FROM post")
    val all: List<Post>

    @Insert
    fun insertAll(vararg posts: Post)
}

Database

最后添加AppDatabase类:

@Database(entities = arrayOf(Post::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun postDao(): PostDao
}

在ViewModel中使用Context Dependent实例

现在我们需要添加一个PostDao参数到PostListViewModel构造函数中,我们需要做的就是调用insert()和all()方法。

class PostListViewModel(private val postDao: PostDao):BaseViewModel(){
    // ...

    private fun loadPosts(){
        subscription = Observable.fromCallable { postDao.all }
                .concatMap {
                    dbPostList ->
                        if(dbPostList.isEmpty())
                            postApi.getPosts().concatMap {
                                            apiPostList -> postDao.insertAll(*apiPostList.toTypedArray())
                                 Observable.just(apiPostList)
                                       }
                        else
                            Observable.just(dbPostList)
                }
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe { onRetrievePostListStart() }
                .doOnTerminate { onRetrievePostListFinish() }
                .subscribe(
                        { result -> onRetrievePostListSuccess(result) },
                        { onRetrievePostListError() }
                )
    }
  
    // ...
}

ViewModelProvider.Factory

现在我们需要在injection包中创建一个ViewModelFactory类,让ViewModelProvider知道如何初始化ViewModel。

class ViewModelFactory(private val activity: AppCompatActivity): ViewModelProvider.Factory{
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(PostListViewModel::class.java)) {
            val db = Room.databaseBuilder(activity.applicationContext, AppDatabase::class.java, "posts").build()
            @Suppress("UNCHECKED_CAST")
            return PostListViewModel(db.postDao()) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")

    }
}

接着告诉provider使用factory来初始化PostViewModel类,接下来更新PostListActivity中的onCreate方法。

class PostListActivity: AppCompatActivity() {
    // ...

    override fun onCreate(savedInstanceState: Bundle?){
        // ...

        viewModel = ViewModelProviders.of(this, ViewModelFactory(this)).get(PostListViewModel::class.java)
        
        // ...
    }
  
    // ...
}

现在运行应用程序,一旦你进入一次之后就可以将手机切换到飞行模式,然后post数据在之后就会自动显示出来。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容