Android
Firebaseリアルタイムデータベース
サーチ…
備考
Firebase Realtime DataBaseイベントハンドラ
最初にFirebaseDatabaseを初期化する:
FirebaseDatabase database = FirebaseDatabase.getInstance();
あなたのデータベースに書き込みます:
// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("message");
myRef.setValue("Hello, World!");
あなたのデータベースから読む:
// Read from the database
myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
String value = dataSnapshot.getValue(String.class);
Log.d(TAG, "Value is: " + value);
}
@Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
Androidイベントのデータを取得する:
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postComments:onCancelled", databaseError.toException());
Toast.makeText(mContext, "Failed to load comments.",
Toast.LENGTH_SHORT).show();
}
};
ref.addChildEventListener(childEventListener);
クイックセットアップ
インストールと設定の部分を完了して、あなたのアプリをFirebaseに接続します。
これでFirebaseにプロジェクトが作成されます。Firebase Realtime Databaseの依存関係をモジュールレベルの
build.gradle
ファイルに追加します:
compile 'com.google.firebase:firebase-database:10.2.1'
これで、Androidのリアルタイムデータベースで作業する準備が整いました。
たとえば、 Hello World
メッセージをmessage
キーの下のデータベースに書き込みます。
// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("message");
myRef.setValue("Hello, World!");
Firebaseデータベースからリアルタイムデータを取得する方法の設計と理解
この例では、Firebase Realtime Databaseをすでに設定していることを前提としています。初心者の方は、AndroidプロジェクトにFirebaseを追加する方法について、 ここにご報告ください。
まず、Firebaseデータベースの依存関係をappレベルのbuild.gradleファイルに追加します。
compile 'com.google.firebase:firebase-database:9.4.0'
さて、Firebaseデータベースにデータを保存するチャットアプリを作ってみましょう。
ステップ1:チャットという名前のクラスを作成する
チャットに必要ないくつかの基本変数を持つクラスを作成するだけです:
public class Chat{
public String name, message;
}
ステップ2:いくつかのJSONデータを作成する
Firebaseデータベースとの間でデータの送受信を行うには、JSONを使用する必要があります。いくつかのチャットがすでにデータベースのルートレベルに格納されていると仮定します。これらのチャットのデータは次のようになります。
[
{
"name":"John Doe",
"message":"My first Message"
},
{
"name":"John Doe",
"message":"Second Message"
},
{
"name":"John Doe",
"message":"Third Message"
}
]
ステップ3:リスナーを追加する
リスナーには3つのタイプがあります。次の例では、 childEventListener
を使用しchildEventListener
。
DatabaseReference chatDb = FirebaseDatabase.getInstance().getReference() // Referencing the root of the database.
.child("chats"); // Referencing the "chats" node under the root.
chatDb.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
// This function is called for every child id chat in this case, so using the above
// example, this function is going to be called 3 times.
// Retrieving the Chat object from this function is simple.
Chat chat; // Create a null chat object.
// Use the getValue function in the dataSnapshot and pass the object's class name to
// which you want to convert and get data. In this case it is Chat.class.
chat = dataSnapshot.getValue(Chat.class);
// Now you can use this chat object and add it into an ArrayList or something like
// that and show it in the recycler view.
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
// This function is called when any of the node value is changed, dataSnapshot will
// get the data with the key of the child, so you can swap the new value with the
// old one in the ArrayList or something like that.
// To get the key, use the .getKey() function.
// To get the value, use code similar to the above one.
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// This function is called when any of the child node is removed. dataSnapshot will
// get the data with the key of the child.
// To get the key, use the s String parameter .
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
// This function is called when any of the child nodes is moved to a different position.
// To get the key, use the s String parameter.
}
@Override
public void onCancelled(DatabaseError databaseError) {
// If anything goes wrong, this function is going to be called.
// You can get the exception by using databaseError.toException();
}
});
ステップ4:データベースにデータを追加する
チャットクラスオブジェクトを作成し、次のように値を追加するだけです:
Chat chat=new Chat();
chat.name="John Doe";
chat.message="First message from android";
検索セッションで行われたように、チャットノードへの参照を取得します。
DatabaseReference chatDb = FirebaseDatabase.getInstance().getReference().child("chats");
データの追加を開始する前に、チャットノードには複数のノードが追加されているため、新しいチャットを追加するとチャットの詳細を含む新しいノードが追加されるため、さらに深い参照が必要です。 DatabaseReference
オブジェクトのpush()
関数を使用してノードの新しい一意の名前を生成できます。これは、別のDatabaseReference
を返します。これは、新しく形成されたノードをチャットデータを挿入することを示します。
例
// The parameter is the chat object that was newly created a few lines above.
chatDb.push().setValue(chat);
setValue()
関数は、アプリケーションのすべてのonDataChanged
関数が(同じデバイスを含めて)呼び出されていることを確認します。これは、「チャット」ノードの接続リスナです。
非正規化:フラットなデータベース構造
個別化されたコールを効率的にダウンロードするには、非正規化とフラットなデータベース構造が必要です。以下の構造では、双方向関係を維持することも可能です。この方法の欠点は、常に複数の場所でデータを更新する必要があることです。
たとえば、ユーザーがメッセージを自分自身(メモ)に保存できるようにするアプリを考えてみましょう。
望ましいフラットデータベース構造:
|--database
|-- memos
|-- memokey1
|-- title: "Title"
|-- content: "Message"
|-- memokey2
|-- title: "Important Title"
|-- content: "Important Message"
|-- users
|-- userKey1
|-- name: "John Doe"
|-- memos
|-- memokey1 : true //The values here don't matter, we only need the keys.
|-- memokey2 : true
|-- userKey2
|-- name: "Max Doe"
使用されたメモクラス
public class Memo {
private String title, content;
//getters and setters ...
//toMap() is necessary for the push process
private Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<>();
result.put("title", title);
result.put("content", content);
return result;
}
}
ユーザーのメモを取得する
//We need to store the keys and the memos seperately
private ArrayList<String> mKeys = new ArrayList<>();
private ArrayList<Memo> mMemos = new ArrayList<>();
//The user needs to be logged in to retrieve the uid
String currentUserId = FirebaseAuth.getInstance().getCurrentUser().getUid();
//This is the reference to the list of memos a user has
DatabaseReference currentUserMemoReference = FirebaseDatabase.getInstance().getReference()
.child("users").child(currentUserId).child("memos");
//This is a reference to the list of all memos
DatabaseReference memoReference = FirebaseDatabase.getInstance().getReference()
.child("memos");
//We start to listen to the users memos,
//this will also retrieve the memos initially
currentUserMemoReference.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Here we retrieve the key of the memo the user has.
String key = dataSnapshot.getKey(); //for example memokey1
//For later manipulations of the lists, we need to store the key in a list
mKeys.add(key);
//Now that we know which message belongs to the user,
//we request it from our memos:
memoReference.child(key).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Here we retrieve our memo:
Memo memo = dataSnapshot.getValue(Memo.class);
mMemos.add(memo);
}
@Override
public void onCancelled(DatabaseError databaseError) { }
});
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) { }
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) { }
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) { }
@Override
public void onCancelled(DatabaseError databaseError) { }
}
メモを作成する
//The user needs to be logged in to retrieve the uid
String currentUserUid = FirebaseAuth.getInstance().getCurrentUser().getUid();
//This is the path to the list of memos a user has
String userMemoPath = "users/" + currentUserUid + "/memos/";
//This is the path to the list of all memos
String memoPath = "memos/";
//We need to retrieve an unused key from the memos reference
DatabaseReference memoReference = FirebaseDatabase.getInstance().getReference().child("memos");
String key = memoReference.push().getKey();
Memo newMemo = new Memo("Important numbers", "1337, 42, 3.14159265359");
Map<String, Object> childUpdates = new HashMap<>();
//The second parameter **here** (the value) does not matter, it's just that the key exists
childUpdates.put(userMemoPath + key, true);
childUpdates.put(memoPath + key, newMemo.toMap());
FirebaseDatabase.getInstance().getReference().updateChildren(childUpdates);
プッシュまたはデータベースが次のようになったら:
|--database
|-- memos
|-- memokey1
|-- title: "Title"
|-- content: "Message"
|-- memokey2
|-- title: "Important Title"
|-- content: "Important Message"
|-- generatedMemokey3
|-- title: "Important numbers"
|-- content: "1337, 42, 3.14159265359"
|-- users
|-- userKey1
|-- name: "John Doe"
|-- memos
|-- memokey1 : true //The values here don't matter, we only need the keys.
|-- memokey2 : true
|-- generatedMemokey3 : true
|-- userKey2
|-- name: "Max Doe"
firebase JSONデータベースについて
コードで手を汚す前に、データがfirebaseにどのように格納されているかを理解する必要があると感じています。リレーショナルデータベースとは異なり、firebaseはJSON形式でデータを格納します。リレーショナルデータベースの各行はJSONオブジェクト(基本的に順序付けられていないキーと値のペア)です。したがって、列名はキーになり、ある特定の行の列に格納されている値が値になります。このようにして、行全体がJSONオブジェクトとして表され、これらのリストはデータベーステーブル全体を表します。私がこれを見るとすぐに得られる利点は、スキーマの変更が古いRDBMSに比べてはるかに安価な操作になることです。テーブル構造を変更するよりも、JSONに2つ以上の属性を追加する方が簡単です。
firebaseにデータがどのように格納されるかを示すJSONのサンプルを以下に示します。
{
"user_base" : {
"342343" : {
"email" : "[email protected]",
"authToken" : "some string",
"name" : "Kaushal",
"phone" : "+919916xxxxxx",
"serviceProviderId" : "firebase",
"signInServiceType" : "google",
},
"354895" : {
"email" : "[email protected]",
"authToken" : "some string",
"name" : "devil",
"phone" : "+919685xxxxxx",
"serviceProviderId" : "firebase",
"signInServiceType" : "github"
},
"371298" : {
"email" : "[email protected]",
"authToken" : "I am batman",
"name" : "Bruce Wayne",
"phone" : "+14085xxxxxx",
"serviceProviderId" : "firebase",
"signInServiceType" : "shield"
}
},
"user_prefs": {
"key1":{
"data": "for key one"
},
"key2":{
"data": "for key two"
},
"key3":{
"data": "for key three"
}
},
//other structures
}
これは、リレーショナルデータベースに保存したデータをJSON形式で保存する方法を明確に示しています。次に、アンドロイドデバイスでこのデータを読む方法を見てみましょう。
firebaseからのデータの取得
私はあなたがすでにアンドロイドスタジオにgradle依存関係firebaseを追加することを知っていると仮定します。あなたがここからのガイドに従っていないならば。依存関係を追加した後、firebase console、gradle sync android studioであなたのアプリを追加してください。すべての依存関係は、firebaseデータベースとfirebase authだけでは必要ありません。
データがどのように格納され、どのようにgradle依存関係を追加するかを知ったので、インポートされたfirebase android SDKを使ってデータを取得する方法を見てみましょう。
firebaseデータベースリファレンスを作成する
DatabaseReference userDBRef = FirebaseDatabase.getInstance().getReference();
// above statement point to base tree
userDBRef = DatabaseReference.getInstance().getReference().child("user_base")
// points to user_base table JSON (see previous section)
ここから、興味のあるデータを指すように複数のchild()メソッド呼び出しを連鎖させることができます。たとえば、前のセクションで示したようにデータが保存されていて、Bruce Wayneユーザーを指し示す場合は、
DatabaseReference bruceWayneRef = userDBRef.child("371298");
// 371298 is key of bruce wayne user in JSON structure (previous section)
または、JSONオブジェクトへの参照全体を渡すだけです:
DatabaseReference bruceWayneRef = DatabaseReference.getInstance().getReference()
.child("user_base/371298");
// deeply nested data can also be referenced this way, just put the fully
// qualified path in pattern shown in above code "blah/blah1/blah1-2/blah1-2-3..."
フェッチするデータの参照ができたので、リスナーを使用してアンドロイドアプリでデータを取得できます。レトロフィットまたはボレーを使用してREST APIコールを発信する従来のコールとは異なり、ここでは単純なコールバックリスナーがデータを取得する必要があります。 Firebase sdkがコールバックメソッドを呼び出すと、完了です。
基本的に2つのタイプのリスナーがあります.1つはValueEventListenerで、もう1つはChildEventListenerです (次のセクションで説明します)。値のイベントリスナーは、JSON構造体全体を返し、子イベントリスナーは、変更が発生した特定の子を返します。これらの両方は、それぞれ独自の方法で役立ちます。 firebaseからデータを取得するには、firebaseデータベース参照(先ほど作成したuserDBRefのリスト)に1つまたは複数のリスナーを追加できます。
いくつかのサンプルコード(コードの後のコードの説明)は次のとおりです。
userDBRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
User bruceWayne = dataSnapshot.child("371298").getValue(User.class);
// Do something with the retrieved data or Bruce Wayne
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.e("UserListActivity", "Error occured");
// Do something about the error
});
渡されたクラスの型に気づいたか? DataSnapshotは、JSONデータを定義されたPOJOに変換できます。単純な正しいクラスタイプを渡します。
少しの変更が発生するたびにデータ全体(この場合はuser_baseテーブル)を必要としない場合や、データを一度だけ取得したい場合は、データベース参照のaddListenerForSingleValueEvent()メソッドを使用できます。コールバックは1回だけ発生します。
userDBRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Do something
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Do something about the error
});
上記のサンプルは、JSONノードの価値を示します。キーを取得するには:
String myKey = dataSnapshot.getKey();
子の更新を聞く
チャットアプリやコラボレーティブな食料品リストアプリ(基本的には、ユーザー間でオブジェクトのリストを同期させる必要があります)のようなユースケースを用意しましょう。 firebaseデータベースを使用して、チャット親ノードまたは食料品リストの親ノードに値イベントリスナーを追加すると、チャットノードが追加されるたびに(チャットの開始を意味する)最初からチャット構造全体が終了しますつまり誰もがこんにちは)。私たちが望んでいないこと、私たちが興味を持っているのは、新しいノードだけ、あるいは削除または変更された古いノードだけです。変更されていないノードは返されません。
この場合、 ChildEvenListenerを使用できます。それ以外のコードサンプルはありません(サンプルJSONデータの前のセクションを参照してください):
userDBRef.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
//If not dealing with ordered data forget about this
}
@Override
public void onCancelled(DatabaseError databaseError) {
});
メソッド名は自明です。新しいユーザーが追加されたり、既存のユーザーの一部のプロパティが変更されたり、ユーザーが削除または削除されたりすると、関連するデータとともに子イベントリスナーの適切なコールバックメソッドが呼び出されます。ですから、チャットアプリケーションのためにUIをリフレッシュしておく場合は、onChildAdded()からJSONを取得してPOJOにパースし、UIにフィットさせてください。ユーザーが画面を離れるときにリスナーを削除することを忘れないでください。
onChildChanged()は、変更されたプロパティ(新しいもの)で子値全体を返します。
onChildRemoved()は、削除された子ノードを返します。
ページネーションによるデータの取得
巨大なJSONデータベースを所有している場合、値イベントリスナーを追加することは意味がありません。それは巨大なJSONを返し、それを解析することは時間がかかるでしょう。このような場合、ページ分割を使用してデータの一部を取り出して表示または処理することができます。ユーザーがクリックして古いチャットを表示すると、古いチャットをフェッチするような、怠惰なローディングのようなものです。この場合、 Queryが使用できます。
これまでのセクションの古い例を見てみましょう。ユーザーベースには3人のユーザーが含まれています。ユーザー数が30万人に増え、50個のバッチでユーザーリストを取得する場合は、
// class level
final int limit = 50;
int start = 0;
// event level
Query userListQuery = userDBRef.orderByChild("email").limitToFirst(limit)
.startAt(start)
userListQuery.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Do something
start += (limit+1);
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Do something about the error
});
ここで、値イベントまたは子イベントを追加して聴くことができます。もう一度クエリーを呼び出して次の50 を取得します。orderByChild()メソッドを必ず追加してください 。これは、これがなければ動作しません。 Firebaseは、ページングする順序を知る必要があります。