gdg tokyo firebaseを使った androidアプリ開発
TRANSCRIPT
Firebaseを使った Androidアプリ開発
2015-10-10 @GDG Tokyo Meeting
自己紹介• 白山 文彦
• 株式会社マナボ 技術者
• @fushiroyama
• http://blog.shiroyama.us/
• http://qiita.com/FumihikoSHIROYAMA
Firebaseとは
• リアルタイム同期型クラウドデータベース
• JSONストア型NoSQL
• データの柔軟なセキュリティアクセス制限
• オフラインサポート
• Twitter, Facebook, GitHub, Google などのアカウント認証と容易な連携
中央サーバ
ローカルコピー ローカルコピー
• アプリはJSONをツリーとみなし任意の位置を参照(reference)する
• 参照した位置から読みだしたり書き込んだりする
• データはすぐさま同期される(400ms前後)
• オフライン状態でも書き込み・読み出し可能(なように見える。後述)
何が出来るかデモ
• リアルタイムな同期!
• オフラインで書き込み、オンライン復帰時に同期
• オンライン復帰時にも書き込んだ時間順に並ぶ
Signup
https://www.firebase.com/signup/
Android Quickstart
build.gradle
dependencies { compile 'com.firebase:firebase-client-android:2.4.0+' }
build.gradleandroid { ... packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/LICENSE-FIREBASE.txt' exclude 'META-INF/NOTICE' } }
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
CustomApplication@Override public void onCreate() { super.onCreate(); Firebase.setAndroidContext(this); // other setup code }
Activity
Firebase myFirebaseRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
Create Reference
Writing Data
myFirebaseRef.child("message") .setValue("Do you have data? You'll love Firebase.");
Child Node
key
write!
Reading Data
myFirebaseRef.child("message").addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { //prints "Do you have data? You'll love Firebase." System.out.println(snapshot.getValue()); } @Override public void onCancelled(FirebaseError error) { } });
Child Node
key
read!
• Firebaseアプリ開発はイベントドリブンモデル
• 任意のキーを参照し、受け取りたいイベントリスナを設定していく
• 保存は Firebase#setValue(Object)
• ObjectはいわゆるPOJO(フィールド名がキーに対応)
• とくにアノテーション不要
• データは DataSnapshot#getValue() で受け取る
• キャストして使うか getValue(Class<T>) で受け取る
有効なデータ型• String
• Boolean
• Long
• Double
• Map<String, Object>
• List<Object>
• およびこれらの再帰的なデータ構造
つまりJSONで表現できる 任意のデータ型
全てはJSON。設計はプログラマに委ねられる
Arrayに注意• 純粋なArrayは提供されない。
• ["hoge", "fuga"]は内部的に{0: "hoge", 1: "fuga"}
• 理由は並行操作を安全に行うため(添字は要素の削除で振り直される)
• ただしArrayのように見えるものはArrayListに自動変換
• https://www.firebase.com/docs/android/guide/understanding-data.html
条件あり! 以下リンク参照
色々な保存方法
• setValue()
• updateChildren()
• push()
• runTransaction()
setValue
• 一番基本的な保存方法
• キーに対応する値をそのまま子ノードも含めて上書き保存/変更
• POJOはアノテーションとかなくてもちゃんとオブジェクトとして保存できる。
エンティティを表現するPOJO
完了コールバック
updateChildren
• あるオブジェクト(ディクショナリー)一部の更新に使う
• 例えばHashMapにたいしてsetValue()してしまうと全てが書き換わるので、一部のkey/valueだけ更新したいときとかに使う
ディクショナリーを取得
push
• 同じキーに同時に書き込みがあると新しい方で上書きされる。同時に書き込みがあるチャットシステムなどではこれはマズイので、一意なキーを払い出す仕組み。
• キーはタイムスタンプを元に作られているので時系列でソートされる。
runTransaction
• いわゆるトランザクション
• 同時並行操作で不整合が起きるような処理をアトミックに行うことが出来る
カウンタの更新のような アトミックな操作
色々な読み出し方法• addValueEventListener
• addChildEventListener
• onChildAdded
• onChildChanged
• onChildDeleted
• onChildMoved
addValueEventListener
• 一番シンプルな取得方法
• 指定したキー以下を全部取得
• 初回に全部取得、以後データに更新があるたびにまた全部取得
Firebase ref = new Firebase("https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"); // Attach an listener to read the data at our posts reference ref.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { System.out.println(snapshot.getValue()); } @Override public void onCancelled(FirebaseError firebaseError) { System.out.println("The read failed: " + firebaseError.getMessage()); } });
getValue(Class<T> clazz) だと型安全
addChildEventListener
onChildAdded• リストを参照しているときに子ノードが追加されると呼ばれる
• 初回は子ノードの数だけコールバックされる
• 以後は追加されるたび、そのノードがコールバックされる
• snapShot.getKey()でそのキーを取れるので、キーのインデックス、値のインデックスを保存しておいてListViewなどのアダプタに持っておくのはよくある運用かも
Firebase ref = new Firebase("https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts");ref.addChildEventListener(new ChildEventListener() { // Retrieve new posts as they are added to the database @Override public void onChildAdded(DataSnapshot snapshot, String previousChildKey) { BlogPost newPost = snapshot.getValue(BlogPost.class); System.out.println("Author: " + newPost.getAuthor()); System.out.println("Title: " + newPost.getTitle()); } //... ChildEventListener also defines onChildChanged, onChildRemoved, // onChildMoved and onCanceled, covered in later sections.});
onChildChanged• onChildChangedはデータパス以下のノードが変更されたときにコールバックされる。子ノードのどんな子孫に対するいかなる変更でも通知される。
• onChildAddedやonChildRemovedと組み合わせてよく使われる。
• コールバックには変更のあったノードの情報がスナップショットとして渡される。
onChildDeleted
• 削除されたときにコールバックされてくる。あとはさっきのと同じ。
• コールバックに渡されるスナップショットは削除されたノード。
onChildMoved
• ノードの移動で発火される。あとはさっきのとry
イベントに関する保証• ローカルの状態変更でもこれらのイベントはトリガされる
• イベントは最終的には常にデータの正しい状態を反映する。つまり、ネットワークの瞬断などでローカルの操作やタイミングが一時的にローカルデータとの差異を生んだとしても。
• ある1人のユーザからの書き込みは常にサーバに書き込まれ、その後他人にブロードキャストされるという順番が守られる。
• ValueEventは常に最後にトリガされ、それより前に起こったいかなるイベントからの差分も含まれていることが保証されている。
Security
• あるキー以下のアクセス制御
• 読み込み/書き込み
• 認証済みユーザのみ書き込みなど柔軟
auth変数
• auth 変数はアクセス制御の基本
• 認証プロバイダに依らず一意保証
• auth 変数は認証処理後に生成される
User Authentication
• Facebook, Twitter, Google, GitHubなどの認証機構と連携 (3rd Party)
• Firebase独自の認証機構 (Email & Password)
• 匿名ログイン (Anonymous)
• 自前の認証機構と連携 (Custom)
認証の有効化
ログイン処理
今日は Email & Password Authentication
ユーザ作成
作成だけで ログインまではしない
よくある権限書き込みは認証ユーザ かつログイン済みだけ
読み込みは認証ユーザなら だれでもOK
その他
• Twitterの例
• https://www.firebase.com/docs/android/guide/login/twitter.html
その他TIPS• リスナの解除には ref.removeEventListener(originalListener); を呼ぶ。
• 複数のリスナを登録している場合にはその分だけ全部解除を呼んでやる必要がある(めんどくさい・・・)
• 親ノードへのリスナ登録、そしてそれを解除したからといって更に下位のノードへのリスナが自動で解除されるわけではない。全部登録した数だけ手動で解除する必要がある。
• 初回だけ実行されてあとは何もしないのが都合がいいリスナがある場合は addListenerForSingleValueEvent を使うと便利だよ。
まとめ• オフライン機能とリアルタイム同期があるので、今まで困っていたSocket.IO(WebSocket)などのオフライン -> オンライン復帰時の再送みたいな処理を、複雑なコードを一切書かずに実現できるのが素晴らし過ぎる。
• 並行操作の制約上、純粋な配列がサポートされていないとか、NoSQLなのでJOINが使えないとか、性質を理解して使わないといけない部分もある。
• ちゃんと使うなら意外に高額になる?
まとめ
• それでもなお余りある魅力!
• 是非一度評価してみてください!驚きます!
素晴らしきサンプルたち
• https://www.firebase.com/docs/android/examples.html
• https://github.com/firebase/AndroidChat
• https://github.com/firebase/AndroidDrawing
公式リファレンスショートカット• Quickstart
• https://www.firebase.com/docs/android/quickstart.html
• データ構造の理解
• https://www.firebase.com/docs/android/guide/understanding-data.html
• データ保存
• https://www.firebase.com/docs/android/guide/saving-data.html
• データ取得
• https://www.firebase.com/docs/android/guide/retrieving-data.html
• 効率的なデータ構造設計への手引き
• https://www.firebase.com/docs/android/guide/structuring-data.html
• アクセス制限
• https://www.firebase.com/docs/android/guide/understanding-security.html
• 認証
• https://www.firebase.com/docs/android/guide/user-auth.html
時間が余ったら チャットアプリの実践例を コード見ながら解説!
ご清聴ありがとうございました。
• 本日の資料
マナボはCS担当者を募集しています!