ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Firebase] Realtime Database(1)- push/child/setValue..etc
    Firebase(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)에서 계속

    댓글

Designed by Tistory.