Android Live Data

James Lin
8 min readAug 21, 2018

--

看完 CodeLab 的範例,我試著跟著做做看,來體驗 Live Data 的強大吧~~

LiveData 與 Room 的 Gradle

// Room components
implementation "android.arch.persistence.room:runtime:1.1.1"
kapt "android.arch.persistence.room:compiler:1.1.1"
androidTestImplementation "android.arch.persistence.room:testing:1.1.1"

// Lifecycle components
implementation "android.arch.lifecycle:extensions:1.1.1"
kapt "android.arch.lifecycle:compiler:1.1.1"
implementation 'com.android.support:recyclerview-v7:26.1.0'
implementation 'com.android.support:design:26.1.0'

使用 Room 搭配 Live Data

首先先 使用 Room 建立 SQLite。

接著 Create Repository,Repository 的職責是存取資料,可能是從 網路、資料庫 存取資料。
有 Repository 可以分隔 操作資料的行為。

class Repository {

private var wordDao: WordDao? = null
private var mAllWords
: LiveData<List<Word>>? = null

constructor
(context: Context) {
var db = WordRoomDatabase.getInstance(context)
wordDao = db.getWordDao()
mAllWords = wordDao!!.getAllWords()
}

fun insert(word: Word) {
insertAsyncTask(wordDao!!).execute(word)
}

fun getAllWords(): LiveData<List<Word>> {
return mAllWords!!
}

companion object {
private class insertAsyncTask : AsyncTask<Word, Unit, Unit> {
private var asyncTaskWordDao: WordDao

constructor(dao: WordDao) {
asyncTaskWordDao = dao
}

override fun doInBackground(vararg params: Word?) {
asyncTaskWordDao.insert(params[0]!!)
}
}
}
}

ViewModel 是持有資料的物件,也就是說要在 UI 顯示資料的話,都必須跟他拿,或者要 insert 也需要透過他,他會與 Reposity 溝通。

class WordViewModel : AndroidViewModel {
private var wordRepo: Repository
private var mAllWords: LiveData<List<Word>>

constructor(application: Application) : super(application) {
wordRepo = Repository(application)
mAllWords = wordRepo.getAllWords()
}

fun getAllWords(): LiveData<List<Word>> {
return mAllWords
}

fun insert(word: Word) {
wordRepo.insert(word)
}

}

MainActivity 會與 ViewModel 溝通,因為要拿取資料,由於資料的型態是 LiveData,所以有 observe 的方法,當資料有所變動的時候 就會呼叫到 onChanged 接著就更新 UI

var adapter = MyAdapter()
mWordViewModel = ViewModelProviders.of(this).get(WordViewModel::class.java)
mWordViewModel.getAllWords().observe(this, object : Observer<List<Word>> {
override fun onChanged(t: List<Word>?) {
// Toast.makeText(this@MainActivity, "Call", Toast.LENGTH_LONG).show()
adapter.setWords(t!!)
}

})
recyclerList.layoutManager = LinearLayoutManager(this)
recyclerList.adapter = adapter

LiveData 的好處,當 Activity 被 Destroy 的時候,LiveData 不會造成 Memory leak。

Transformations.map

簡單來說就是 LiveData 變動了就會觸發這個 map 去做對應的 事情。

這個東西可以配合 LiveData 使用,使用方式就像這樣

Transformations.map(queryLiveData, {
reposiory
.search(it)
})

那麼他是怎麼跑的呢?

看一下 source code 吧

@MainThread
public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,
@NonNull final Function<X, Y> func) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(func.apply(x));
}
});
return result;
}

看到這裡,也就是說 傳入的 LiveData 發生資料變動的時候,會觸發

func.apply(x),進而將變動的資料傳入一開始定義的 function( {
reposiory
.search(it)} ),那這個 function 又回傳什麼東西呢?

new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(func.apply(x));
}
}

Function 的 source code,這個 function 的 apply 會回傳 一個 Generic 那這個 Type 就是看你想回傳啥 Type 就回傳啥 Type。

public interface Function<I, O> {
/**
* Applies this function to the given input.
*
*
@param input the input
*
@return the function result.
*/
O apply(I input);

Transformations.switchMap

switchMap 與 Map 最大的差別是在 function,switchMap 裡面的 function 已經定義了 一定要回傳 LiveData,使用上並無啥差別~~

@NonNull final Function<X, LiveData<Y>> func@MainThread
public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,
@NonNull final Function<X, LiveData<Y>> func) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(trigger, new Observer<X>() {
LiveData<Y> mSource;

@Override
public void onChanged(@Nullable X x) {
LiveData<Y> newLiveData = func.apply(x);
if (mSource == newLiveData) {
return;
}
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) {
result.setValue(y);
}
});
}
}
});
return result;
}

使用

val repos: LiveData<List<Repo>> = Transformations.switchMap(repoResult, { it ->
it.data
}
)
val networkErrors: LiveData<String> = Transformations.switchMap(repoResult, { it -> it.networkErrors })

純粹 LiveData:GitHub

--

--

James Lin
James Lin

No responses yet