-
[Firebase] Realtime Database(1)- push/child/setValue..etcFirebase(Android)/RealtimeDatabase 2021. 7. 26. 09:58
포스팅 맨 부분에 layout.xml과 액티비티 소스를 남겨뒀다. 드래그하여 복사하기만 하면된다.
FirebaseDatabase, DatabaseRef클래스의 기본사용방법에 대해 알아보고
코드를 변경시킬 때마다 빌드를 하는 것이 힘드므로 layout파일에 여러 EditText와 Button들을 추가하여
JSON tree의 동작을 관찰하자.
실시간 데이터베이스는 JSON형식으로 저장된다.
프로젝트 창의 Realtime Database 탭에서 데이터베이스를 만들면 처음에는 비어있으니 null으로 되어있다.
JSON구조를 알아먹기 힘들다면 오른쪽의 ...더보기를 눌러 JSON내보내기를 하여
다운로드된 JSON파일을 브라우저에 옮겨넣으면 현재 데이터베이스의 구조를 파악하기 쉽다.
브라우저에서 JSON트리 확인 결과
- FirebaseDatabase
public class FirebaseDatabase extends Object
데이터베이스 entry 포인트 클래스
파이어베이스 실시간 데이터베이스 인스턴스이다.
- static getInstance()
해당되는 FirebaseApp의 FirebaseDatabase인스턴스를 가져온다.
- 인스턴스.getReference()
메서드의 인자에 해당되는 JSON tree레벨의 DatabaseRef인스턴스를 가져온다.
- DatabaseRef
public class DatabaseReference extends Query
파이어베이스 데이터베이스 JSON 내부의 특정 트리레벨 위치 구간을 참조할 수 있는 클래스
setValue, push, child메서드에 관해 알아본다.
-setValue(Object obj)
현재 DatabaseRef에 해당하는 JSON 트리 레벨에 값 하나를 저장한다. 트리 레벨을 하나 더 추가시키기 위해서는 {} object형태로 저장되어야 하는데 setValue로 값을 저장하면 {} 가 아닌 값 하나로 저장되므로 해당되는 레벨에서는 더이상 하위 레벨로 연장이 불가능하다.
간단하게 root레벨의 값을 설정하는 코드를 보자.
public class FirebaseDatabaseActivity extends AppCompatActivity { //액티비티 FirebaseDatabase db; DatabaseReference root = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_realtime_database); db = FirebaseDatabase.getInstance(); //데이터베이스 인스턴스(default FirebaseApp) if (db == null) { printLog("firebase app or firebaseDatabase is null"); } else { root = db.getReference(); //테이터베이스 안에서 최상위 노드위치를 나타내는 인스턴스 if (root == null) { printLog("DatabaseReference is null"); } else { root.setValue("root의 값을 변경하였습니다"); //위치에 해당하는 value값 설정 } } } public static void printLog(String str) { //로그 출력 Log.d("REALTIMEDB", str); } }
실행후
프로젝트 화면( 업데이트 되면 자동으로 바뀜)
다운로드 폴더에서 기존JSON 삭제-> JSON내보내기-> 브라우저 새로고침
root레벨을 값 하나로 저장했기 때문에 이 값을 {}인 Object로 덮어씌우지 않고서는 하위 트리구조를 만들지 못한다.
프로젝트 화면에서 root이름과 데이터에 마우스 커서를 올려보면 하위레벨에 데이터를 추가하는 +기호가 나오지 않을 것이다.( 프로젝트창에서도 직접 데이터 추가가 가능하다)
-child(String pathString)
설명이 어렵다.... 일단 데이터베이스가 모두 비어있다고 치자.
rootRef.child("testKey").setValue("testValue"); 를 보자
rootRef.child("testKey") -> root기준으로 하나의 하위레벨인데 key가 "child"를 가진 곳에
setValue("value") -> value값을 추가하라는 것이다.
만약 하위 레벨에 데이터가 존재하지 않으면 자동으로 level들이 추가된다.
바로 위의 테스트가 진행된 상태에서 root.child("testKey").setValue("testValue"); 가 수행된다면
실행전
실행결과
"root의 값을....." String값이 지워지고 하위레벨이 추가되면서(레벨을 추가시키려면 { }가 사용되어야함) 덮어씌어졌다.
root.child("testKey").setValue("changedValue"); 실행
//key는 중복이 될 수 없으므로 기존 트리에 존재하는 값이 변경된다.
//key가 있으면 해당하는 key의 값에 덮어쓰고 key가 없는 값이면 key를 만들면서 key-value로 데이터를 저장한다.
결과
root.child("testKey2").setValue("testValue2"); 실행
현재 root기준으로 하위레벨이 존재한다. 그렇기 때문에 레벨추가는 진행되지 않고 하위레벨에 testKey2: testValue2 로 데이터를 쓴다.
결과
child메서드는 기준 level로부터 몇 레벨 하위인지 + key만 저장한다.
(child메서드로 return되는 DatabaseRef는 가상의 위치와 key만 기억하고) setValue가 추가될 때 그에 해당되는 위치까지 자동으로 레벨이 추가되고 key: value로 값이 써진다.
rootRef.child("keyChild/keyGrandChild").setValue("grandChild"); 실행
rootRef.child("keyChild/keyGrandChild2").setValue("grandChild2");
rootRef.child("keyChild/keyGrandChild3").setValue("grandChild3");
rootRef.child("keyChild").setValue("1");
rootRef.setValue("1");
추가한 sdk의 api를 확인하니 child메서드의 인자에 null을 넣어주면 NullPointerException이 발생하고, 빈스트링""은 인자로 넘겨주는게 가능하긴 하다.(쓰잘대기 없긴하겠지만)
rootRef.child("")는 rootRef 자기자신 Ref를 반환한다.
rootRef.child("").setValue("1");는 rootRef.setValue("1)과 같다고 보면 된다.
-push()
파이어베이스에서 자동으로 키를 발생시킨다. (규칙은 문서의 메서드와 realtime database부분 가이드를 참고하자)
자동으로 발생된 키를 가진 DatabseRef를 return한다.
null을 입력하여 데이터베이스 데이터를 다 지우고 시작하자.
rootRef.push().setValue("값1");
rootRef.push().setValue("값2");
rootRef.push().setValue("값3");
rootRef.push().push().setValue("값4");
setValue에는 다양한 타입을 저장할 수 있다. reference를 참고하자.
이제 설명할 내용은 간단한 메서드들인것 같다.
(DatabaseReference의 메서드들이다.)
-getKey()
DatabaseRef의 key String을 return
-getParent()
부모 DatabaseRef를 return (root에서 getParent()를 호출하면 null리턴)
-getRoot()
실시간데이터베이스의 루트 DatabaseRef를 return
-removeValue()해당 DatabaseRef의 값을 지운다. (null로 만듦) Task(작업) 상태를 return한다 ( return된 Task의 상태를 보고 작업이 완료되었는지 취소되었는지 확인 가능하다)(setValue(null); 와 동작은 같은데 setValue는 void를 리턴한다)
테스트 코드
Activity
import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.hyun.choi499.project.firstproject.R; public class FirebaseDatabaseActivity extends AppCompatActivity { FirebaseDatabase db; DatabaseReference root = null; DatabaseReference selectedRef = null; //선택된 DatabaseReference //데이터 추가 EditText keyChild; //key EditText valueChild; //value //현재 DatabaseRef 위치 출력 TextView textView4; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_firebase_database); //현재 선택된 DatabaseRef를 출력할 TextView textView4 = findViewById(R.id.textView4); keyChild = findViewById(R.id.editText); //key valueChild = findViewById(R.id.editText2); //value //key-value 직접 입력하여 데이터 추가 버튼 Button button = findViewById(R.id.btn_apply); //firebase에서 만들어주는 key로 value만 입력하여 데이터 추가 버튼 Button button2 = findViewById(R.id.btn_apply2); //선택된 DatabaseRef 값을 null로 만들어 하위 데이터베이스 초기화 Button button3 = findViewById(R.id.btn_apply3); //Reference level up and down Button button4 = findViewById(R.id.btn_move_up); Button button5 = findViewById(R.id.btn_move_down); //setValue Button button6 = findViewById(R.id.btn_set); //자질구레한 일회성 테스트 Button button7 = findViewById(R.id.btn_test); db = FirebaseDatabase.getInstance(); //데이터베이스 인스턴스(default FirebaseApp) root = db.getReference(); //테이터베이스 안에서 최상위 노드위치를 나타내는 인스턴스 selectedRef = root; //처음시작시 selectedRef는 root로 설정 textView4.setText(selectedRef.toString()); // if (db == null) { //Firebase App instance가 null이면 activity종료 printLog("Default FirebaseApp or FirebaseDatabase is null"); finish(); } else if (root == null) { //default FirebaseApp의 FirebaseDatabase가 null이면 Activity종료 printLog("DatabaseReference is null"); finish(); } //key로 value만 입력하여 데이터 추가 button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String str = keyChild.getText().toString(); String str2 = valueChild.getText().toString(); selectedRef.child(str).setValue(str2); } }); //value만 입력하여 데이터 추가 button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String str = valueChild.getText().toString(); selectedRef.push().setValue(str); } }); //선택된 DatabaseRef 값을 null로 만들어 하위 데이터베이스 초기화 button3.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { selectedRef.setValue(null); } }); //DatabaseRef level go up and down button4.setOnClickListener(new View.OnClickListener() { //up @Override public void onClick(View v) { moveUpReference(); } }); button5.setOnClickListener(new View.OnClickListener() { //down @Override public void onClick(View v) { moveDownReference(); } }); button6.setOnClickListener(new View.OnClickListener() { //setValue @Override public void onClick(View v) { selectedRef.setValue(valueChild.getText().toString()); } }); button7.setOnClickListener(new View.OnClickListener() { //자질구레한거 테스트 용도 @Override public void onClick(View v) { selectedRef.removeValue(); } }); } private void moveUpReference() { //parent로 이동 if (selectedRef.getParent() == null) { printToast("현재 위치 root. 더 이상 올라갈 수 없음"); } else { selectedRef = selectedRef.getParent(); textView4.setText(selectedRef.toString()); } } private void moveDownReference() { //key EditText사용 DatabaseReference swap = selectedRef.child(keyChild.getText().toString()); //child의 인자는 빈스트링은 되지만 null은 NPE발생 if (swap == null) { printToast("moveDownReference의 child가 null"); } else { selectedRef = swap; textView4.setText(selectedRef.toString()); } } private static void printLog(String str) { //로그 출력 Log.d("REALTIMEDB", str); } private void printToast(String str) { Toast.makeText(getApplicationContext(), str, Toast.LENGTH_LONG).show(); } }
Layout
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".realtimedatabase.FirebaseDatabaseActivity"> <TextView android:id="@+id/textView4" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="10sp" android:text="start at root" /> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="key" /> <EditText android:id="@+id/editText" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="key" android:inputType="textPersonName" android:text="" /> <TextView android:id="@+id/textView2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="value" /> <EditText android:id="@+id/editText2" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="value" android:text="" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginTop="20dp"> <Button android:id="@+id/btn_apply" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="key-value" /> <Button android:id="@+id/btn_apply2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="push" /> <Button android:id="@+id/btn_apply3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="selectedRef to null" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btn_move_up" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="up" /> <Button android:id="@+id/btn_move_down" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="down" /> </LinearLayout> <Button android:id="@+id/btn_set" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="set value" /> <Button android:id="@+id/btn_test" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="40dp" android:text="test" /> </LinearLayout>
Realtime Database(2)에서 계속