🍣

[Android Studio] Firebase RealtimeDatabaseをつかってみた

2022/06/21に公開

はじめに

firebase初心者である私が公式のサンプルを見つつ、データベースへ書き込みおよび読み取りをしてみたので残しときます。。(多分見る人が見れば公式だけでも事足りると思いますけど、私はわからなかったので書きます)

https://firebase.google.com/docs/database/android/read-and-write?hl=ja

では、いくぜい!!


基本的には以下、4つのクラスを実装して実行すればOK

Post.java
import com.google.firebase.database.Exclude;
import com.google.firebase.database.IgnoreExtraProperties;

import java.util.HashMap;
import java.util.Map;

@IgnoreExtraProperties
public class Post {

    public String uid;
    public String author;
    public String title;
    public String body;
    public int starCount = 0;
    public Map<String, Boolean> stars = new HashMap<>();

    public Post() {
        // Default constructor required for calls to DataSnapshot.getValue(Post.class)
    }

    public Post(String uid, String author, String title, String body) {
        this.uid = uid;
        this.author = author;
        this.title = title;
        this.body = body;
    }
    
    @Exclude
    public Map<String, Object> toMap() {
        HashMap<String, Object> result = new HashMap<>();
        result.put("uid", uid);
        result.put("author", author);
        result.put("title", title);
        result.put("body", body);
        result.put("starCount", starCount);
        result.put("stars", stars);

        return result;
    }
}
User.java
import com.google.firebase.database.IgnoreExtraProperties;

@IgnoreExtraProperties
public class User {

    public String username;
    public String email;

    public User() {
        // Default constructor required for calls to DataSnapshot.getValue(User.class)
    }

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }
}
ReadAndWriteSnippets.java
package test.com.test_firebase.RealtimeDatabase;

import android.util.Log;

import androidx.annotation.NonNull;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.MutableData;
import com.google.firebase.database.ServerValue;
import com.google.firebase.database.Transaction;
import com.google.firebase.database.ValueEventListener;

import java.util.HashMap;
import java.util.Map;

public class ReadAndWriteSnippets {

    private static final String TAG = "ReadAndWriteSnippets";

    // データベースでデータの読み書きを行うために使用
    private DatabaseReference mDatabase;

    // コンストラクタ
    public ReadAndWriteSnippets() {
        mDatabase = FirebaseDatabase.getInstance().getReference();
    }

    public void writeNewUser(String userId, String name, String email) {
        User user = new User(name, email);

        // 情報をDatabaseに設定
        // この方法では、特定の場所にあるデータ(子ノードも含む)が上書きされる
        mDatabase.child("users").child(userId).setValue(user);

        // この方法では、オブジェクト全体を書き換えずに子を更新することもできる。
        // mDatabase.child("users").child(userId).child("username").setValue(name);

        addPostEventListener(mDatabase);
    }

    // パスにあるデータを読み取って、変更をリッスンするには、addValueEventListener() メソッドを使用して
    // ValueEventListener を DatabaseReference に追加します。
    public void addPostEventListener(DatabaseReference mPostReference) {
        ValueEventListener postListener = new ValueEventListener() {
            @Override   // 特定のパスにあるコンテンツの静的スナップショットを、イベントの発生時に存在していたとおりに読み取ることができる
            // Listenerがアタッチされたときに1回、また、データ(子も含む)が変更されるたびにコールされる
            /**
             * @param dataSnapshot:その場所にあるすべてのデータ(子のデータも含む)を含んでいるスナップショット
             */
            public void onDataChange(DataSnapshot dataSnapshot) {
                /**
                 * Snapshot.getValue():そのデータの Java オブジェクト表現が返され、その場所にデータが存在しない場合、null が返される。
                 */
                Post post = dataSnapshot.getValue(Post.class);

                // realtime databaseから情報を読み取る
                String email = (String)dataSnapshot.child("users").child("1").child("email").getValue();
                String username = (String)dataSnapshot.child("users").child("1").child("username").getValue();
                Log.i(TAG, "email:" + email);
                Log.i(TAG, "username:" + username);
            }

            @Override   // 読み取りがキャンセルされた場合に呼び出される
            public void onCancelled(DatabaseError databaseError) {
                Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
            }
        };
        mPostReference.addValueEventListener(postListener);
    }

    // データベースから一度だけ情報を読み取る
    public void getSnapshot(String userId) {
        mDatabase.child("users").child(userId).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
            @Override
            public void onComplete(@NonNull Task<DataSnapshot> task) {
                if (!task.isSuccessful()) {
                    Log.e("firebase", "Error getting data", task.getException());
                }
                else {

                    Log.d("firebase", String.valueOf(task.getResult().getValue()));
                }
            }
        });
    }

    // 投稿を作成して、それと同時にその投稿から最近のアクティビティ フィードと投稿ユーザーのアクティビティ フィードを更新する
    private void writeNewPost(String userId, String username, String title, String body) {
        // Create new post at /user-posts/$userid/$postid and at /posts/$postid simultaneously
        // push() を使用して、/posts/$postid にある全ユーザーの投稿が格納されているノード内に投稿を作成すると同時に、
        // getKey() でキーを取得しています。
        String key = mDatabase.child("posts").push().getKey();

        Post post = new Post(userId, username, title, body);
        Map<String, Object> postValues = post.toMap();

        Map<String, Object> childUpdates = new HashMap<>();
        childUpdates.put("/posts/" + key, postValues);
        childUpdates.put("/user-posts/" + userId + "/" + key, postValues);

        // 他の子ノードを上書きすることなく、ノードの特定の複数の子に同時に書き込む
        // updateChildren() を 1 回呼び出すだけで JSON ツリー内の複数の場所に対して更新を同時に実行できる。
        mDatabase.updateChildren(childUpdates);
    }

    private String getUid() {
        return "";
    }

    private void onStarClicked(DatabaseReference postRef) {
        postRef.runTransaction(new Transaction.Handler() {
            @Override   // 投稿にスターを付ける/スターを外すユーザーのIDと、インクリメントされたスターの数をデータベースに書き込む。
            public Transaction.Result doTransaction(MutableData mutableData) {
                Post p = mutableData.getValue(Post.class);
                if (p == null) {
                    return Transaction.success(mutableData);
                }

                if (p.stars.containsKey(getUid())) {
                    // Unstar the post and remove self from stars
                    // 投稿のスターを取り消す操作
                    p.starCount = p.starCount - 1;
                    p.stars.remove(getUid());
                } else {
                    // Star the post and add self to stars
                    // 投稿にスターを付ける操作
                    p.starCount = p.starCount + 1;
                    p.stars.put(getUid(), true);
                }

                // Set value and report transaction success
                mutableData.setValue(p);
                return Transaction.success(mutableData);
            }

            @Override
            public void onComplete(DatabaseError databaseError, boolean committed,
                                   DataSnapshot currentData) {
                // Transaction completed
                Log.d(TAG, "postTransaction:onComplete:" + databaseError);
            }
        });
    }

    // ユーザーが投稿にスターを付けていることがわかっている場合は、
    // トランザクションではなくアトミックなインクリメント オペレーションを使用できます。
    private void onStarClicked(String uid, String key) {
        Map<String, Object> updates = new HashMap<>();
        updates.put("posts/"+key+"/stars/"+uid, true);
        updates.put("posts/"+key+"/starCount", ServerValue.increment(1));
        updates.put("user-posts/"+uid+"/"+key+"/stars/"+uid, true);
        updates.put("user-posts/"+uid+"/"+key+"/starCount", ServerValue.increment(1));
        mDatabase.updateChildren(updates);
    }
}
MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
	
	ReadAndWriteSnippets readAndWriteSnippets = new ReadAndWriteSnippets();
	readAndWriteSnippets.writeNewUser("1", "Jack", "jack@gmail.com");
    }
}

ReadAndWriteSnippetsクラスではほかに、DBから値を1度だけ取得することが出来るgetSnapshot()や投稿の作成及びアクティビティの取得を行うwriteNewPost()、投稿にスターを取り付けるonStartClicked()などが用意されている。

Discussion