【Android】Firebase配置與使用(上)

Firebase是一個支持實時數據庫管理、雲端存儲、推送分發、事件統計、身份驗證等功能的強大的後臺,經常使用於各個平臺的開發中。java

在Android開發中,使用Firebase做爲本身的app的後臺主要能夠分爲如下幾步:node

  1. 爲你的app配置Firebase服務
  2. 根據須要設計數據庫的結構
  3. 對實時數據庫進行增刪改查以及持久化操做
  4. 文件上傳/下載
  5. #若有須要可使用Firebase進行事件統計#

接下來對每一步進行介紹:android

1.配置Firebase服務

首先,咱們須要在Firebase console上添加咱們的工程,表示咱們的控制檯上有這樣一個項目須要Firebase提供服務,將來查看數據庫結構時也能夠從console進入。(如下均在Firebase的官方網站進行操做)web

  1. 若是尚未 Firebase 項目,能夠在Firebase console 中建立一個。 若是已經有一個與Firebase項目,點擊 Import Google Project。
  2. 點擊 Add Firebase to your Android app 並按設置步驟進行操做。若是在導入現有 Google 項目,這多是自動進行的,只需下載配置文件便可。
  3. 出現提示時,輸入您的應用的包名稱。輸入應用使用的包名稱十分重要。只有將一個應用添加至 Firebase 項目時才能進行此設置。
  4. 最後,下載一個 google-services.json 文件。咱們能夠隨時從新下載此文件。

注:若是您有多個構建變體含有已定義的不一樣包名稱,則必須在 Firebase console 中將每一個應用添加到您的項目。
配置的過程在官方文檔中有較爲詳細的描述,這裏僅簡單記錄一下。數據庫

在服務器的控制檯添加完咱們的工程,接下來須要在咱們的工程中導入firebase服務:
在根目錄下的build.gradle中添加以下規則,從而導入google服務的插件:json

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的配置工做就完成了。bash

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的配置和增、刪、改、持久化的方法進行記錄。剩下的部分見下一篇博客。

感謝閱讀,理解的不對的地方還請指正。