Firebaseを使った初めてのAndroidアプリ開発（MacOSX）


開発環境はMacOSXです。事前にAndroid Studioをインストールし、簡単で良いのでAndroidアプリの作り方を習得しておく必要があります。

FirebaseのRealtime Databaseを使ったAndroidアプリを開発します。

Firebaseについては以下のURLをご参照ください（「Firebaseとは」の検索で最初に出てくるURL）。
http://gihyo.jp/dev/serial/01/firebase/0001

![スクリーンショット 2017-02-08 13.01.01.png](https://qiita-image-store.s3.amazonaws.com/0/160696/185f5f00-dfe2-efda-edd3-a062988cc7ca.png "スクリーンショット 2017-02-08 13.01.01.png")


##プロジェクトの作成（Androidアプリ）

Android Studioを用いて新規プロジェクト「GroupTodo」を作成します。

複数人でTodoを扱うアプリを想定しています。

事前にAndroidManifest.xmlよりパッケージ名（package）を確認します（ここでは「com.hoge.grouptodo」とします）。

##デバッグ用の署名証明書

ターミナルを開き、下記のコマンドを実行します。
このとき、Java SDKのインストールを促された際は、指示に従いインストールします。

```
$ keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore 
```

パスワードを聞かれますが、デフォルトは「android」です。

パスワード入力後「証明書のフィンガプリント」に記載の「SHA1」の値を控えておきます。

##プロジェクトの作成（Firebase）

事前にGoogleアカウントでログインした状態で、下記にアクセスして新規プロジェクト「GroupTodo」を作成します。
国/地域は「日本」を選択します。

https://firebase.google.com/?hl=ja


次に「Android アプリに Firebase を追加」を選択します。

パッケージ名は、上記で確認した「com.hoge.grouptodo」を、デバッグ用の署名証明書は上記で控えたSHA-1の値を入力します。

次に「google-services.json」をダウンロードし、Android Studioの「GroupTodo」プロジェクトの「app」直下にインポートします。（このとき、表示を「Android」から「Project」に変更し、「app」直下にファイルをドラッグ&ドロップで移動できるようにします。）

パッケージレベルの「bundle.gradle」の「dependencies」に「classpath 'com.google.gms:google-services:3.0.0'」を追加します。

```
buildscript {
...
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        classpath 'com.google.gms:google-services:3.0.0'
    }
}

allprojects {
...
}

task clean(type: Delete) {
...
}
```

またアプリレベルの「bundle.gradle」の最下部に「apply plugin: 'com.google.gms.google-services'」を追加します。

また認証機能とRealtime Database機能を使用するために、「dependencies」の下部に「compile 'com.google.firebase:firebase-database:10.0.0'」を追加します。

```
apply plugin: 'com.android.application'

android {
...
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.1.1'
    testCompile 'junit:junit:4.12'

    // Firebase
    compile 'com.google.firebase:firebase-database:10.0.0'
}

apply plugin: 'com.google.gms.google-services'
```

ツールバーに「Sync now」というボタンが現れるので、クリックします。

##Realtime Databaseの設定

Firebaseのコンソール画面に戻り、「Database」をクリックします。

認証機能を無効にするため「ルール」タブを選択して下記の内容に置き換えます。

```
{
	"rules": {
  		".read": true,
    	".write": true
}
```

次に質問を定義します。

「todos」というくくりで、個々のTodo「todo」にはタイトル（title）と終了判定（isDone）の2つのデータを保持することにします。

```
{
		"rules": {
        ".read": true,
        ".write": true,

       "todos": {
            "$todo": {
            ".validate": "newData.hasChildren(['title','isDone']) 
                    && newData.child('title').isString() 
                    && newData.child('endDate').isBoolean()"
            }
        }
    }
}
```

サンプルの初期データを入力します。

「データ」タブを選択して、以下のように初期データを入力します。

ここで「00001」や「00002」は「key」として扱われます。

![スクリーンショット 2017-02-08 12.09.42.png](https://qiita-image-store.s3.amazonaws.com/0/160696/0a71790f-9297-4e20-7c2f-5a38593e5454.png "スクリーンショット 2017-02-08 12.09.42.png")


##Todoクラスの作成

1つ1つの「Todo」に対応するクラスを作成します。基本的な変数とコンストラクタ、ゲッター、セッターを用意します。また「toMap」メソッドはデータベースへのデータ送信（書き込み）時に利用します。

```
public class Todo {
    private String title;
    private Boolean isDone;

    public Todo(String title, Boolean isDone){
        this.title = title;
        this.isDone = isDone;
    }
    public String getTitle(){
        return title;
    }
    public Boolean isDone(){
        return isDone;
    }
    public void setDone(){
        this.isDone = true;
    }

    @Exclude
    public Map<String, Object> toMap(){
        HashMap<String, Object> hashmap = new HashMap<>();
        hashmap.put("title", title);
        hashmap.put("isDone", isDone);
        return hashmap;
    }
}
```

##データの読み込み

データベースへのリファレンスを用意し、下記のコードで「todos」以下のデータにアクセスすることができるようになります。

```
DatabaseReference　todosRef = FirebaseDatabase.getInstance().getReference("todos");
```

データを読み込む際は、不思議な感じがしますが、イベントリスナーを登録して行います。
イベントリスナーにはいろんな種類がありますが、「addListenerForSingleValueEvent」は最初に一度だけ読み込むもので、初期データの読み込みに適しています。

```
todosRef.addListenerForSingleValueEvent(new ValueEventListener() {
     @Override
     public void onDataChange(DataSnapshot snapshot) {
          for(DataSnapshot dataSnapshot : snapshot.getChildren()){
               String key = dataSnapshot.getKey();
               String title = (String) dataSnapshot.child("title").getValue();
               Boolean isDone = (Boolean) dataSnapshot.child("isDone").getValue();
               
               // このforループで、Todoごとのkey, title, isDoneが取得できているので、
               // Todoクラスを利用し、Hashmapに追加するなどして保存する。
          }
			// 保存した情報を用いた描画処理などを記載する。
      }

      @Override
      public void onCancelled(DatabaseError databaseError) {
          // ログを記録するなどError時の処理を記載
      }
});
```

LinearLayoutなどを用いてメイン画面に表示させると、ここまでの内容で次のようにシミュレーターを動かすことができます。

![スクリーンショット 2017-02-08 11.05.46.png](https://qiita-image-store.s3.amazonaws.com/0/160696/83e07b59-5eac-c475-4213-9b44247f8fd0.png "スクリーンショット 2017-02-08 11.05.46.png")

##データの送信

Todoの送信は下記のコードで行うことができます。
引数のTodo（titleやisDoneセット済み）の内容をデータベースに送ります。

```
public void sendTodo(Todo todo){
    String key = todosRef.push().getKey();
        
    Map<String, Object> map = new HashMap<>();
    map.put(key, todo.toMap());
    todosRef.updateChildren(map);
}
```

上記のコードにおいて「todosRef」は、データの読み込みのところでも利用したReferenceです。

```
DatabaseReference　todosRef = FirebaseDatabase.getInstance().getReference("todos");
```

まず最初に、データベースに対してkeyをリクエストします。これにより重複等を回避することができます。

次にHashmapを用いて送信するデータを作成し「updateChildren」メソッドでデータベースに書き込みます。

Firebaseのコンソール画面でデータが追加されていれば成功です。

![スクリーンショット 2017-02-08 12.08.28.png](https://qiita-image-store.s3.amazonaws.com/0/160696/68efd390-ff34-40c1-07c4-3fffe948a4d9.png "スクリーンショット 2017-02-08 12.08.28.png")

##データの更新

Firebaseのデータベースはデータが追加されていますが、Androidアプリの方には追加されていません。

FirebaseのRealTimeデータベースは、その名の通り、リアルタイムにデータを更新する仕組みを用意しています。

データベース（todos）のリファレンスに、「ChildEventListener」を追加し、下記のように、子要素に追加や変更があった際の処理を記述します（下記は追加時のみ記載しています）。

これにより、Todoの追加が他の場所で行われた際にも、瞬時にその情報が通知されて反映することができるようになります。

```
todosRef.addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
      String key = dataSnapshot.getKey();
      String title = (String) dataSnapshot.child("title").getValue();
      Boolean isDone = (Boolean) dataSnapshot.child("isDone").getValue();
	   
	   // 追加されたTodoのkey、title、isDoneが取得できているので、
	   // 保持しているデータの更新や描画処理を行う。
    }
    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String s) {
        // Changed
    }
    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
        // Removed
    }
    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String s) {
        // Moved
    }
    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Error
    }
});
```

##最後に

今回は基本的なデータベースとの連携（Todoの取得、追加）までを行いました。

次は同様にFirebaseが用意している認証機能等を追加するとともに、グループの管理とTodoの管理機能を追加していきたいと思います。