MVVM的資料持久化(二)——ROOM的使用

朝陽楊大爺發表於2018-11-07

MVVM框架的搭建(一)——背景

MVVM框架的搭建(二)——專案搭建

MVVM框架的搭建(三)——網路請求

MVVM的資料持久化(一)——ROOM的整合

ROOM的使用以及實踐

上一篇文章,我們已經介紹了MVVM持久化的意義以及工具ROOM,下面我們介紹一下如何在我們專案當中使用,已達到對資料持久化。

修改Model層

這裡我們需要修改一下Model層,新增Repository作為ViewModel層的資料來源,在Repository裡我們進行資料的處理判斷

package yang.cehome.com.mvvmdemo.model.repository

import yang.cehome.com.mvvmdemo.model.local.dao.PostDao
import yang.cehome.com.mvvmdemo.model.remote.PostService

/**
 * @author yangzc
 *	@data 2018/11/6 11:55
 *	@desc PostRepo
 *
 */
class PostRepo  constructor(private val remote: PostService, private val local: PostDao){
    //首先檢視本地資料庫是否有資料
   fun getPostInfo() = local.getPostInfo()
           .onErrorResumeNext {
               //本地資料庫不存在,會丟擲會丟擲EmptyResultSetException
               //轉而進行獲取網路資料,成功後儲存在資料庫
               remote.getPostInfo().doOnSuccess { local.inserttPost(it) }
           }
}
複製程式碼

我們可以看到現在的專案結構為:

專案結構

修改我們的ViewModel層的資料來源

以前我們都是以PostService作為資料來源,現在我們要以PostRepo作為資料來源,這裡我們只需要修改

package yang.cehome.com.mvvmdemo.viewmodel

import android.databinding.ObservableField
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import yang.cehome.com.mvvmdemo.model.local.dao.PostEntity
import yang.cehome.com.mvvmdemo.model.repository.PostRepo

/**
 * @author yangzc
 *	@data 2018/11/7 10:26
 *	@desc  PostViewModel
 *
 */
class PostViewModel(private val repo: PostRepo) {
    /******data******/
    val postinfo = ObservableField<String>()

    /******binding******/
    fun loadpost() {
        repo.getPostInfo()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ t: PostEntity? ->
                    postinfo.set(t?.let { it.toString() })
                }, { t: Throwable? ->
                    postinfo.set(t?.message ?: "error")
                })
    }
}
複製程式碼
在View層當中引用
package yang.cehome.com.mvvmdemo.view

import android.databinding.DataBindingUtil
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.facebook.stetho.okhttp3.StethoInterceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import yang.cehome.com.mvvmdemo.R
import yang.cehome.com.mvvmdemo.databinding.ActivityMainBinding
import yang.cehome.com.mvvmdemo.model.data.Onclick
import yang.cehome.com.mvvmdemo.model.local.AppDatabase
import yang.cehome.com.mvvmdemo.model.remote.PostService
import yang.cehome.com.mvvmdemo.model.repository.PostRepo
import yang.cehome.com.mvvmdemo.viewmodel.OnclikViewModel
import yang.cehome.com.mvvmdemo.viewmodel.PostViewModel




/**
 * MVVM 當中的一個V層 將三者聯絡起來
 */
class MainActivity : AppCompatActivity() {
    private lateinit var mBinding: ActivityMainBinding
    private lateinit var mViewMode: OnclikViewModel
    private lateinit var mViewMode2: PostViewModel


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

        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        /////model
        val onclick = Onclick("me", 0)
        ///ViewModel
        mViewMode = OnclikViewModel(onclick)
        ///binding

        val client = OkHttpClient.Builder()
                .addNetworkInterceptor(StethoInterceptor())
                .build()

        val remote = Retrofit.Builder()
                .baseUrl("http://www.kuaidi100.com")
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build().create(PostService::class.java)
        val local= AppDatabase.getInstance(applicationContext).PostDao()
        val  repo = PostRepo(remote, local)

        ////ViewModel2
        mViewMode2 = PostViewModel(repo)
        mBinding.vm = mViewMode
        ////binding
        mBinding.post = mViewMode2
    }
}

複製程式碼
效果

根據上面的內容 我們最後看看效果如何

持久化資料效果

根據Stetho我們也可以看到我們本地的資料庫

利用Stetho檢視本地資料

若想了解Stetho請看這篇文章 安卓除錯神器-Stetho的使用

最後

至此關於MVVM的資料持久化的工作,我們就完成了。 但是就目前的方法來說,每次都需要寫的模板化的程式碼太多了,那麼我們有沒有什麼辦法簡化呢,答案必然是有的。 後面的文章我們會持續介紹到,希望大家持續關注。

問題

我們可以看到,對於ROOM的儲存包括之前用的GreenDao這種類似的儲存都有一個問題,就是根據資料庫對應的格式一個鍵裡面有對應相應的值,那麼當我們的Json返回資料當中包含JsonArray的話,在資料存和取資料就比較麻煩,類似如下

{
    "com": "zhongtong",
    "condition": "F00",
    "data": [
        {
            "context": "【寧波市】 快件到達 【寧波中轉部】",
            "ftime": "2018-10-11 20:41:45",
            "location": "寧波中轉部",
            "time": "2018-10-11 20:41:45"
        },
        {
            "context": "【寧波市】 快件離開 【寧波】 發往 【寧波中轉部】",
            "ftime": "2018-10-11 18:23:24",
            "location": "寧波",
            "time": "2018-10-11 18:23:24"
        },
        {
            "context": "【寧波市】 【寧波】(0574-88014756、0574-88016531、0574-88014575) 的 寧海電商產業園區 (15990572220) 已攬收",
            "ftime": "2018-10-11 17:14:34",
            "location": "寧波",
            "time": "2018-10-11 17:14:34"
        }
    ],
    "ischeck": "1",
    "message": "ok",
    "nu": "7510054353700",
    "state": "3",
    "status": "200"
}
複製程式碼

我們直接生成實體,在用Room建庫比較麻煩,以前用的方法是,建一個實體 在存的時候使用

     public static String boxing(List<T> List) {
        if (List == null || List.size() == 0) {
            return "";
        } else {
            StringBuffer buffer = new StringBuffer();
            for (int index = 0; index < payloadList.size(); ++index) {
                T t = List.get(index);
                Parcel p = Parcel.obtain();
                p.writeValue(t);
                byte[] bytes = p.marshall();
                buffer.append(Base64.encodeToString(bytes, Base64.DEFAULT));
                if (index < List.size() - 1) {
                    buffer.append(SPLIT_CHAR);
                }
                p.recycle();
            }
            return buffer.toString();
        }
    }
複製程式碼

這樣一個JsonArry就存成了一個String

取的時候採用


     public static List<T> unBoxing(String listString) {
        List<T> list = new ArrayList<>();
        if (!TextUtils.isEmpty(listString)) {
            String[] array = listString.split(SPLIT_CHAR);
            for (String str : array) {
                Parcel p = Parcel.obtain();
                byte[] ba = Base64.decode(str, Base64.DEFAULT);
                p.unmarshall(ba, 0, ba.length);
                p.setDataPosition(0);
                list.add((T) p.readValue(T.class.getClassLoader()));
                p.recycle();
            }
        }
        return list;
    }
複製程式碼

這樣取得時候String又變成了List

但是這個方法我終究覺得不是一個理想的解決方案,不知道有沒有什麼好的建議,我們一起討論一下。

專案地址

github.com/yang0range/…

相關文章