2016/10/03 : orderByChild の処理 を追加しました
記事末に、@IgnoreExtraProperties アノテーションの説明を追加しました。
記事末に、『JSON データを直接使用する』もあります。
ArrayList として取得
HashMap として取得
Userクラスとして取得
全体ソース
まず、環境とプロジェクトの作成方法は、『Firebase API + Android Studio : Database 処理の基本設定』にまとめました。そちらを参照して下さい。
Firebase の Database は、JSON をイメージした格納方法になっており、データはツリー構造の末端にあると考えて、そこまでのパスを指定して読み出します。ここでは、以下のようなデータを想定しており、0、1、2、の部分は配列のインデックスを意味しています。
ArrayList として取得
users を パスとして与えて取得すると、その下は配列なので ArrayList として取得する事ができます。また、users/0 のパスで取得するデータは クラス(User)を作成して、User.class を使用して Userクラスのインスタンスに直接セットする事ができます。そして、users を パス とした場合は ArrayList<User> としても取得する事ができます。
GenericTypeIndicator<ArrayList<User>> t = new GenericTypeIndicator<ArrayList<User>>() {};
ArrayList<User> user = dataSnapshot.getValue(t);
Iterator<User> it = user.iterator();
while( it.hasNext() ) {
User work = it.next();
Log.i("lightbox", work.getCode() );
Log.i("lightbox", work.getName() );
}
DataSnapshot dataSnapshot は、Database のデータを取得する onDataChange イベントのパラメータとして使用可能なもので、この中にデータがセットされています。
そして、ArrayList は 以下のようにして配列に変換して利用してもいいでしょう。
User[] users = user.toArray(new User[0]);
for( int i = 0; i < users.length; i++){
Log.i("lightbox", users[i].getCode() );
Log.i("lightbox", users[i].getName() );
}
HashMap として取得
User クラスを作成しない場合、users を パスとした場合の一般的な方法として ArrayList<HashMap<String,Object> として取得する事ができます。
GenericTypeIndicator<ArrayList<HashMap<String,Object>>> t2
= new GenericTypeIndicator<ArrayList<HashMap<String,Object>>>() {};
ArrayList<HashMap<String,Object>> useList = dataSnapshot.getValue(t2);
Iterator<HashMap<String,Object>> it2 = useList.iterator();
while( it2.hasNext()) {
HashMap<String,Object> userMap = it2.next();
Log.i( "lightbox", userMap.get("code").toString() );
Log.i( "lightbox", userMap.get("name").toString() );
}
Userクラスとして取得
User user = dataSnapshot.getValue(User.class);
TextView tv1 = (TextView) MainActivity.this.findViewById(R.id.textCode);
tv1.setText(user.getCode());
TextView tv2 = (TextView) MainActivity.this.findViewById(R.id.textName);
tv2.setText(user.getName());
※ クラスで定義するフィールドの整数は long で定義して下さい。(関連 API ドキュメント情報)
クラス内の変数は、public のフィールドで定義してもかまいませんし、setter / getter で定義してもかまいません。排除したいフィールドは、@Exclude アノテーションを使用できます。
また、クラスには引数の無い処理の無いデフォルトコンストラクタを定義しておいて下さい。以下のような但し書きがあります
Default constructor required for calls to DataSnapshot.getValue(User.class)
※ 概要印刷用 PDF
※ ソース全体印刷用 PDF
データを取得する為の addListenerForSingleValueEvent
ドキュメントには、『Read data once』とタイトルして解説されていますが、必要な時にデータを取得する為のイベントの登録処理です。他のリスナーは、状態が変化した時に取得可能なもので、上のリンク先のページの『Listen for events』で解説されています。
addListenerForSingleValueEvent は、一般的にサーバよりデータを取得するメソッドとして使用され、この中のイベントで DataSnapshot を使ってデータにアクセスします。
3種類の取得パターンの全体ソース
public class MainActivity extends AppCompatActivity {
private FirebaseDatabase database;
private DatabaseReference mDatabase;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
database = FirebaseDatabase.getInstance();
mDatabase = database.getReference();
Button readButton = (Button) MainActivity.this.findViewById(R.id.readButton);
readButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if ( dataSnapshot.exists() ) {
Log.i("lightbox", dataSnapshot.getValue().toString());
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
mDatabase.child("users").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if ( dataSnapshot.exists() ) {
Log.i("lightbox", "-- users の状態");
Log.i("lightbox", dataSnapshot.getValue().toString());
Log.i("lightbox", "-- ArrayList<User>");
GenericTypeIndicator<ArrayList<User>> t = new GenericTypeIndicator<ArrayList<User>>() {};
ArrayList<User> user = dataSnapshot.getValue(t);
Iterator<User> it = user.iterator();
while( it.hasNext() ) {
User work = it.next();
Log.i("lightbox", work.getCode() );
Log.i("lightbox", work.getName() );
}
Log.i("lightbox", "-- 配列");
User[] users = user.toArray(new User[0]);
for( int i = 0; i < users.length; i++){
Log.i("lightbox", users[i].getCode() );
Log.i("lightbox", users[i].getName() );
}
Log.i("lightbox", "-- ArrayList<HashMap<String,Object>>");
GenericTypeIndicator<ArrayList<HashMap<String,Object>>> t2
= new GenericTypeIndicator<ArrayList<HashMap<String,Object>>>() {};
ArrayList<HashMap<String,Object>> useList = dataSnapshot.getValue(t2);
Iterator<HashMap<String,Object>> it2 = useList.iterator();
while( it2.hasNext()) {
HashMap<String,Object> userMap = it2.next();
Log.i( "lightbox", userMap.get("code").toString() );
Log.i( "lightbox", userMap.get("name").toString() );
}
}
else {
Log.i("lightbox","データを読み込めませんでした");
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.i("lightbox","onCancelled");
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
databaseError.toException().printStackTrace(pw);
pw.flush();
String stackTrace = sw.toString();
Log.i("lightbox",stackTrace);
}
});
}
});
Button readUserButton = (Button) MainActivity.this.findViewById(R.id.readUserButton);
readUserButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mDatabase.child(String.format("users/%d",0)).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if ( dataSnapshot.exists() ) {
Log.i("lightbox", dataSnapshot.getValue().toString());
User user = dataSnapshot.getValue(User.class);
TextView tv1 = (TextView) MainActivity.this.findViewById(R.id.textCode);
tv1.setText(user.getCode());
TextView tv2 = (TextView) MainActivity.this.findViewById(R.id.textName);
tv2.setText(user.getName());
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
}
public static class User {
private String code;
private String name;
public User() {}
public User(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
public void setCode(String code) {
this.code = code;
}
public void setName(String name) {
this.name = name;
}
}
}
orderByChild の処理
ツリーが 配列では無くHashMap で構成されているデータは、直接 HashMap に格納できますが、順序が保証されないので、orderByChild メソッドを使用してデータをソートしてから取得します。その場合は、DataSnapshot から getChildren メソッドで一覧を取得する必要があります
mDatabase.child("class").orderByChild("code").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if ( dataSnapshot.exists() ) {
userList = new ArrayList<User>();
Iterator<DataSnapshot> child = dataSnapshot.getChildren().iterator();
while(child.hasNext()) {
DataSnapshot next = child.next();
User user = next.getValue(User.class);
userList.add(user);
}
}
else {
Log.i("lightbox","データを読み込めませんでした");
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.i("lightbox","onCancelled");
}
});
※ 印刷用 PDF
※ 印刷用 PDF(Module Gradle)
@IgnoreExtraProperties アノテーションについて
結局付けても付けなくても直接の動作には影響ありません。ただ、付けない場合はログにワーニングが表示され、付けるとワーニングは表示されません。
これは、開発中に Database 側にあって、クラス側に無いデータをクラスを使って読み込んだ時に確認できる内容になると思います。
Firebase ドキュメント : Update your Java model objects
@Exclude アノテーションについて
上のリンク先では、@Exclude についても記述されています。これを使用すると、意図的にそのフィールドを排除できます。サンプルでは、public なフィールドに設定されていますが、setter/getter に対して指定できます。
補足 : JSON データを直接使用する
データベースのルールが ".read": true になっている場合は、Firebase のプロジェクトの Database の URL で直接 JSON 文字列を使用できます。ルートは https://プロジェクトid.firebaseio.com/.json で取得でき、他のパスは、.json を拡張子として付加すると取得できます。
※ ?print=pretty というパラメータを指定できます(整形します)
▼ 公開サンプル
https://json-sample-b69b7.firebaseio.com/.json?print=pretty
https://json-sample-b69b7.firebaseio.com/users/0.json
▼ JSONLint にて整形
http://jsonlint.com/?json=https://json-sample-b69b7.firebaseio.com/.json
また、このアクセスは、Access-Control-Allow-Origin:* を返すので、jQuery で簡単に利用可能です。(ルールが要ログインの場合は、アクセストークンが必要になり、Android だけで利用するのは結構面倒になります)
関連する記事
Firebase API + Android Studio : Database にデータを保存は単純で、DatabaseReference の setValue メソッドを使用します
Firebase API + Android Studio : Database 処理の基本設定
Android Studio 2.2 で新規プロジェクトを作成すると『Could not reserve enough space for 1572864KB object heap』というエラーが出る場合の対処方法