首先来看看如何取用 Firebase SDK 的服务:
val firestore = FirebaseFirestore.getInstance()
要取用 firestore 的服务非常简单,只要呼叫 getInstance()
就好,也不用自己再去写一次 Singleton pattern 的双重锁定,写了只是浪费时间,Firebase 已经帮我们写好了,相关实作可以在原始码中找到:
// FirebaseFirestore.java
@NonNull
public static FirebaseFirestore getInstance() {
FirebaseApp app = FirebaseApp.getInstance();
if (app == null) {
throw new IllegalStateException("You must call FirebaseApp.initializeApp first.");
}
return getInstance(app, DatabaseId.DEFAULT_DATABASE_ID);
}
// FirebaseApp.java
@NonNull
public static FirebaseApp getInstance() {
synchronized (LOCK) {
FirebaseApp defaultApp = INSTANCES.get(DEFAULT_APP_NAME);
if (defaultApp == null) {
throw new IllegalStateException(
"Default FirebaseApp is not initialized in this "
+ "process "
+ ProcessUtils.getMyProcessName()
+ ". Make sure to call "
+ "FirebaseApp.initializeApp(Context) first.");
}
return defaultApp;
}
}
基於我们之前所新增的资料,那时候新增了一个 "Notes" 的 Collection,所以现在我们要想办法使用 firestore api 来查询这些资料,其查询方式也非常简单:
private val query = firestore.collection(COLLECTION_NOTES)
.limit(100)
Firestore 是使用类似 builder pattern 的方式来组合出查询,以上述的查询为例,我们指定了要查询 COLLECTION_NOTES
这个 Collection ,而且数量限制 100 笔,组合出查询之後,接着就是使用该查询来获取资料:
query.addSnapshotListener { result, e ->
result?.let { onSnapshotUpdated(it) }
}
addSnapshotListener
可以让我们随时都收到最新的资讯,只要有新的更新,这个 Listener 就会再呼叫一次。其中所有更新的资讯都在 result
里面,如果发生错误,result
就会是空值,错误的内容将会在 e
得知,下图的说明为 Firestore 的源码:
Firestore 转换资料有两种方式,第一个是使用反射帮你转换成 Model,这机制跟 Gson 是一样的,第二个是自己写转换资料的逻辑,可以想像成是自己使用 JsonObject 跟 JsonArray 来做反序列化。
Gson 是一个很常使用於 JSON 资料转换的函式库,其特点是只要定义好了 Model ,而且这 Model 的格式跟 JSON 是可以一对一互相对映的,Gson 就可以使用反射的机制帮我们产生 runtime model ,专案也可以因此大大减少样板程序码(Boilerplate code)。
最後我选择了後者,自己写转换资料格式的逻辑,原因如下:
Note
,这样算下来,开发的时间反而还变长了。下面程序码是方案二的实作:
private fun onSnapshotUpdated(snapshot: QuerySnapshot) {
val allNotes = snapshot
.map { document -> documentToNotes(document) }
// Use allNotes as an Observable event
}
private fun documentToNotes(document: QueryDocumentSnapshot): Note {
val data: Map<String, Any> = document.data
val text = data[FIELD_TEXT] as String
val color = YBColor(data[FIELD_COLOR] as Long)
val positionX = data[FIELD_POSITION_X] as String? ?: "0"
val positionY = data[FIELD_POSITION_Y] as String? ?: "0"
val position = Position(positionX.toFloat(), positionY.toFloat())
return Note(document.id, text, position, color)
}
附上今天专案中所用到的所有常数:
companion object {
const val COLLECTION_NOTES = "Notes"
const val FIELD_TEXT = "text"
const val FIELD_COLOR = "color"
const val FIELD_POSITION_X = "positionX"
const val FIELD_POSITION_Y = "positionY"
}
修改资料非常简单,只要将每个栏位都储存到 map 结构中,再呼叫 set 即可,:
private fun setNoteDocument(note: Note) {
val noteData = hashMapOf(
FIELD_TEXT to note.text,
FIELD_COLOR to note.color.color,
FIELD_POSITION_X to note.position.x.toString(),
FIELD_POSITION_Y to note.position.y.toString()
)
firestore.collection(COLLECTION_NOTES)
.document(note.id)
.set(noteData)
}
而且很方便的是,如果该 id 不存在,Firestore 就会自动帮我们建立一个新的 Document。
由於已经完成大部分实作,其余的部分只剩下 RxJava 的整合,这里使用的是 BehaviorSubject 来接收以及发送资料:
class FirebaseNoteRepository: NoteRepository {
private val firestore = FirebaseFirestore.getInstance()
private val notesSubject = BehaviorSubject.createDefault(emptyList<Note>())
private val query = firestore.collection(COLLECTION_NOTES)
.limit(100)
init {
query.addSnapshotListener { result, e ->
result?.let { onSnapshotUpdated(it) }
}
}
override fun getAllNotes(): Observable<List<Note>> {
return notesSubject.hide()
}
override fun putNote(note: Note) {
setNoteDocument(note)
}
private fun onSnapshotUpdated(snapshot: QuerySnapshot) {
val allNotes = snapshot
.map { document -> documentToNotes(document) }
notesSubject.onNext(allNotes)
}
private fun setNoteDocument(note: Note) {
val noteData = hashMapOf(
FIELD_TEXT to note.text,
FIELD_COLOR to note.color.color,
FIELD_POSITION_X to note.position.x.toString(),
FIELD_POSITION_Y to note.position.y.toString()
)
firestore.collection(COLLECTION_NOTES)
.document(note.id)
.set(noteData)
}
private fun documentToNotes(document: QueryDocumentSnapshot): Note {
val data: Map<String, Any> = document.data
val text = data[FIELD_TEXT] as String
val color = YBColor(data[FIELD_COLOR] as Long)
val positionX = data[FIELD_POSITION_X] as String? ?: "0"
val positionY = data[FIELD_POSITION_Y] as String? ?: "0"
val position = Position(positionX.toFloat(), positionY.toFloat())
return Note(document.id, text, position, color)
}
companion object {
const val COLLECTION_NOTES = "Notes"
const val FIELD_TEXT = "text"
const val FIELD_COLOR = "color"
const val FIELD_POSITION_X = "positionX"
const val FIELD_POSITION_Y = "positionY"
}
}
串完了 Firestore SDK ,我们就来实际跑看看吧!程序运行的结果如下:
移动的方式好奇怪!怎麽会抖来抖去的呢?到底发生了什麽事呢?我在这里先卖个关子,大家可以猜猜看,答案明天揭晓!
<<: Day16:[搜寻演算法]Binary search - 二分搜寻法
>>: [Day 16] JavaScript 网页事件处理
堆积排序法(Heap Sort)原理是利用「堆积」的资料结构为基础来完成排序。 堆积的介绍可以参考此...
http://www.nltk.org/ NLTK 是一个主流用於自然语言处理的 Python 库 ...
Q1. Floyd-Warshall 是什麽 一种利用 Dynamic Programming ,求...
第十五天 各位点进来的朋友,你们好阿 小的不才只能做这个系列的文章,但还是希望分享给点进来的朋友,知...
如果我们想要强迫传来的Prop是某种型态或是强迫某个Prop一定要被传入的话, 我们可以使用Prop...