【Android】Firebase配置與使用(上)
Firebase是一個支援實時資料庫管理、雲端儲存、推送分發、事件統計、身份驗證等功能的強大的後臺,常用於各個平臺的開發中。
在Android開發中,使用Firebase作為自己的app的後臺主要可以分為以下幾步:
- 為你的app配置Firebase服務
- 根據需要設計資料庫的結構
- 對實時資料庫進行增刪改查以及持久化操作
- 檔案上傳/下載
- #如有需要可以使用Firebase進行事件統計#
接下來對每一步進行介紹:
1.配置Firebase服務
首先,我們需要在Firebase console上新增我們的工程,表示我們的控制檯上有這樣一個專案需要Firebase提供服務,未來檢視資料庫結構時也可以從console進入。(以下均在Firebase的官方網站進行操作)
- 如果還沒有 Firebase 專案,可以在Firebase console 中建立一個。 如果已經有一個與Firebase專案,點選 Import Google Project。
- 點選 Add Firebase to your Android app 並按設定步驟進行操作。如果在匯入現有 Google 專案,這可能是自動進行的,只需下載配置檔案即可。
- 出現提示時,輸入您的應用的包名稱。輸入應用使用的包名稱十分重要。只有將一個應用新增至 Firebase 專案時才能進行此設定。
- 最後,下載一個 google-services.json 檔案。我們可以隨時重新下載此檔案。
注:如果您有多個構建變體含有已定義的不同包名稱,則必須在 Firebase console 中將每個應用新增到您的專案。
配置的過程在官方文件中有較為詳細的描述,這裡僅簡單記錄一下。
在伺服器的控制檯新增完我們的工程,接下來需要在我們的工程中匯入firebase服務:
在根目錄下的build.gradle中新增如下規則,從而匯入google服務的外掛:
buildscript {
// ...
dependencies {
// ...
classpath 'com.google.gms:google-services:3.0.0'
}
}
然後再在module的build.gradle中新增如下程式碼來啟用Gradle外掛:
apply plugin: 'com.android.application'
android {
// ...
}
dependencies {
// ...
compile 'com.google.firebase:firebase-core:9.6.1'
}
// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'
注意:dependencies中編譯的庫根據需求而變化,官方網站給出瞭如下的可用庫以及其對應的功能的列表:
以上Firebase的配置工作就完成了。
2.根據需要設計資料庫的結構
Firebase提倡使用JSON檔案來代替傳統資料庫,即把所有的Firebase Realtime Database資料都儲存為JSON格式。資料之間的層次通過JSON的巢狀來體現,而不存在傳統的表、行、列等。這裡可以理解成用JSON格式構建一顆樹,每條記錄就是樹中的一個節點。
值得一提的是,Firebase在設計資料庫的時候,不提倡使用多層的巢狀,而提倡使用扁平化的資料結構。例如我們在設計一個聊天應用的資料庫時,有兩種方式:
{
// 這是一個構建資料庫的錯誤示例,因為在獲取“chats”的標題時,我們不得不遍歷“chat”下的所有節點
// 這無形中增加了冗餘的下載量和作業時間
"chats": {
"one": {
"title": "聊天1",
"messages": {
"m1": { "sender": "張三", "message": "哈哈哈" },
"m2": { ... },
// 一系列的message…
}
},
"two": { ... }
}
}
這個結構中,我們可以看到“chats”節點下有許多聊天,各自有title、message等資訊。其中“title”巢狀在“one”節點下,如果我們要得到“one”的聊天標題,我們則需要便利完“one”這個節點,包括“message”節點下的所有內容,所以這個工作量是大而不必要的。
正確的做法應該是將資料庫展開:
{
"chats": {
"one": {
"title": "聊天1",
"lastMessage": "張三: 哈哈哈",
"timestamp": 1459361875666
},
"two": { ... },
"three": { ... }
},
//這個節點記錄聊天成員是否被包括在該聊天內
"members": {
"one": {
"張三": true,
"李四": true,
"王五": true
},
"two": { ... },
"three": { ... }
},
"messages": {
"one": {
"m1": {
"name": "張三",
"message": "哈哈哈",
"timestamp": 1459361875337
},
"m2": { ... },
"m3": { ... }
},
"two": { ... },
"three": { ... }
}
}
可以看到,這個結構中我們把聊天的每一項屬性展開到同一層樹,這樣獲取chats的相關資訊的時候就會更為簡單,而不進行多餘的下載和訪問。
以上是設計資料庫的一些注意事項。
3.對實時資料庫進行增刪改查以及持久化操作
為了對實時資料庫進行增刪改查,Firebase為我們封裝好了setValue()、push()、updateChildren()、runTransaction()四種方法,接下來逐一介紹:
setValue()
主要用於一些基本的寫入操作。引數可以是JSON可用的資料型別(String,Int,Long,Double,Boolean,Map
private void writeNewUser(String userId, String name, String email) {
User user = new User(name, email);
mDatabase.child("users").child(userId).setValue(user);
}
這裡的child(var1)表示取節點名為“var1”的節點,由此看來,setValue方法在修改子節點的資料時可以不用重寫修改內容無關的節點資料。setValue方法在對既有資料的節點進行操作的時候會覆蓋原有的資料,從而修改資料,對沒有資料的節點進行操作的時候則會新增資料。
push()
主要用於將資料追加到Json樹。如我們在設計一款圖片儲存app的時候,需要有不同的相簿(album)對圖片進行分類。新建一個相簿的時候,我們可以直接在我們要的節點處push()一個新節點,再對新節點使用setValue設定值。
DatabaseReference ref = getAlbumsHeaderNode().push();
//…
ref.setValue(album)
其中album代表存放相簿屬性的Java物件,DatabaseReference為節點類,getAlbumsHeaderNode()得到的是節點“Header”,上述程式碼在Header節點下面追加了一個子節點,並把album資料放入該節點中。
值得一提的是,push()方法追加節點時會產生一個唯一的ID,這個ID基於時間給出,這也使得多個終端可以對同一個節點進行追加子節點操作。這個ID可以通過getKey()方法獲得。
updateChildren()
用於同時更新多個子節點。如果要同時向多個節點的特定子節點寫入資料,而不覆蓋其他子節點,可以使用該方法。比如:
//先在“post”下建立一個節點,並獲取他的ID
String key = mDatabase.child("posts").push().getKey();
Post post = new Post(userId, username, title, body);
//將Post物件返回成一個map
Map<String, Object> postValues = post.toMap();
Map<String, Object> childUpdates = new HashMap<>();
childUpdates.put("/node01/" + key, postValues);
childUpdates.put("/node02/" + userId + "/" + key, postValues);
mDatabase.updateChildren(childUpdates);
...
程式碼中childUpdates這個map的鍵是節點名稱,值為節點資料。使用updateChildren()同時更新兩個節點下的子節點的值。
注意:通過這種方式同步更新具有原子性:要麼所有更新全部成功,要麼全部失敗。
setValue()方法和updateChildren()方法都可以設定回撥,來監聽更新是否成功,直接在函式後面加.addOnCompleteListener
即可。
runTransaction()
這個方法可以防止多個終端對同意資料進行操作的時候產生資料混亂。他的原理是:如果更新操作由於寫資料衝突被拒絕,則將當前值返回給客戶端,客戶端更新資料後重新進行寫操作。
例如,在社交部落格中,我們可以允許使用者對博文加星和取消加星,並跟蹤博文獲得的加星數,如下所示:
private void onStarClicked(DatabaseReference postRef) {
postRef.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
Post p = mutableData.getValue(Post.class);
if (p == null) {
return Transaction.success(mutableData);
}
if (p.stars.containsKey(getUid())) {
// 取消博文的贊並將使用者移出點贊列表
p.starCount = p.starCount - 1;
p.stars.remove(getUid());
} else {
// 為博文點贊,並且將使用者加入博文點贊列表
p.starCount = p.starCount + 1;
p.stars.put(getUid(), true);
}
//成功setValue
mutableData.setValue(p);
return Transaction.success(mutableData);
}
@Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
Log.d(TAG, "postTransaction:onComplete:" + databaseError);
}
});
}
這裡我們在點選星星時,出發了runTransaction方法,這個方法的引數是一個handler,在這個handler中有兩個回撥:doTransaction和onComplete,後者顯然就是在資料修改完成後進行,重點看一下doTransaction()。
這個函式的引數是MutableData的物件,通過閱讀文件得知,這個類的作用是封裝了某一位置(節點)將要被修改成的資料以及他的優先順序——這點很重要。我們使用mutableData.getValue()獲得了節點postRef處的Post物件p,對該物件進行一系列判斷和點贊/取消贊修改後,將p的值賦給mutableData,最後呼叫Transaction.success方法將mutableData的值賦給資料庫中的節點。
注:由於 doTransaction() 將多次呼叫,因此必須能夠處理 null 資料。 即使遠端資料庫中已有資料,但在執行事務處理函式時可能不會本地快取這些資料,從而導致 null 成為初始值。
以上為JSON樹的增改操作,刪除操作可以通過增改操作或者removeValue方法實現。
資料持久化方面:如果客戶端失去網路連線,應用將繼續正常執行。
Firebase會在本地建立一個臨時資料庫,寫入資料時,首先將其寫入此本地版本。 然後,Firebase 客戶端盡最大程度將這些資料與遠端資料庫伺服器以及其他客戶端同步。
因此,在將任何資料寫入伺服器之前,對資料庫執行的所有寫入會立即觸發本地事件。 這意味著應用將保持隨時響應,無論網路延遲或連線情況如何均是如此。
在重新建立連線之後,應用將收到一組適當的事件,以便客戶端與當前伺服器狀態同步,而不必編寫任何自定義程式碼。
開啟這個離線功能僅需要幾行程式碼:
在例項化FirebaseDatabase的後就設定
mDatabase.setPersistenceEnabled(true);
啟用離線功能,然後在對應的節點處,呼叫keepSynced(true)即可。
DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.keepSynced(true);
本文先就firebase的配置和增、刪、改、持久化的方法進行記錄。剩下的部分見下一篇部落格。
感謝閱讀,理解的不對的地方還請指正。