안드로이드

[Android/Firebase/Kotlin] realtime database의 setValue() 메서드가 한 활동 내에 여러 프래그먼트에서 정상적으로 사용되지 않는 이유 분석 및 해결

란서 2022. 4. 8. 21:57

목적

개인 프로젝트 Solaroid를 진행하던 중 Firebase의 라이브러리 firebase:database를 이용하여 realtime database를 사용하려고 함. 그런데 database 내에 데이터를 쓰는 것이 정상 가동되지 않았고, 이에 대한 이유가 무엇이고 어떤 해결방법을 찾았는지 적어내고자 함.

 

문제점

구조

MainActivity에서는 FragmentContainerView를 보여주며 해당 컨테이너에는 NavHost를 이용하여 메인 액티비티 내에서 여러 프래그먼트를 navigate 를 할 수 있다.

 

그래서 각 프래그먼트에서

 

val firebaseDB = Firebase.database
val ref = firebaseDB.reference

와 같은 변수를 정의하고, 

 

ref.child("tmp").setValue(value)

 

와 같이 setValue()메서드를 호출하는 방식으로 db내에 데이터를 쓰려고 한다.

 

 

문제 발생

그런데 각 프래그먼트에서 ref를 이용해 위와 같은 메서드를  호출 했을 때, 하나의 프래그먼트에서만 setValue가 정상적으로 실행되고, 그 외의 다른 프래그먼트에서는 ref의 setValue() 메서드가 전혀 작동이 되지 않는 모습을 보였다.

예를 들어

 

//In FrameContainerFragment

val firebaseDB = Firebase.database
val ref = firebaseDB.reference

val value = "wow"
ref.child("tmp").setValue(value).addOnCompleteListener{
	if(it.successful) Log.i("FrameContainer", "성공")
    else Log.d("FrameContainer", "실패", it.exception)
}

 

와 같은 코드를 구현하였을 때 정상적으로 db/tmp/ 경로에 wow : String 라는 데이터가 쓰여지는 것 확인.

 

그런데

 

// In CreateFragment

val firebaseDB = Firebase.database
val ref = firebaseDB.reference

val value = "wow"
ref.child("tmp").setValue(value).addOnCompleteListener{
	if(it.successful) Log.i("CreateFragment", "성공")
    else Log.d("CreateFragment", "실패", it.exception)
}

똑같은 코드를 CreateFragment 에서 실행하였을 때 어떠한 Log 없이 해당 메서드가 무시되는 문제 발생.

success 또는 failure도 아닌 상태.

 

 

문제 분석

CreateFragment에서 ref.setValue() 메서드를 실행하였을 때 아무런 반응(성공 또는 실패)이 없는 것을 확인하고 ref 또는 database쪽에 문제가 있다고 판단.

 

FrameContainerFragmentCreateFragmentdatabase의 값을 log로 찍어봤다.

 

//각각 프래그먼트에서

Log.i("CreateFragment", "데이터베이스 : ${firebaseDB}, 참조값 : ${ref}")

 

그런데 두 database의 값이 다른 것을 확인할 수 있었다. 

 

여기서 든 의문은

 

  1. 가장 먼저 Firebase.database를 정의한 쪽에서 정상적으로 db를 사용할 수 있다? (database가 이미 연결되면 다른 곳에서는 사용 불가능한 개념?)
  2. 그렇다면 먼저 데이터베이스를 정의한 프래그먼트에서 다른 프래그먼트로 이동할 때 이전에 데이터베이스에 대한 참조?를 제거?해야 하는가?

그런데 먼저 정의한 database를 제거하는 메서드를 사용해야 하는데 아직 발견하지 못했고, 이와 관련한 글을 firebase내 codelab이나 문서에서 발견하지 못함. (내가 못찾은걸지도..)

 

 

 

문제 해결

가장 먼저 정의한 database가 서버와 연결이 되서 정상 작동이 되는거라면 해당 database를 모든 프래그먼트에서 사용할 수 있도록 통일 해주면 되겠다는 아이디어.

 

따라서 Firebase Database를 프로퍼티로 갖는 ViewModel 클래스 "FirebaseDatabaseViewModel" 를 만들고, 가장 먼저 database값을 초기화.

이 후 해당 viewModel을 프래그먼트의 상위 UI component 인 Activity가 소유함으로써 하위 프래그먼트들이 viewModel을 초기화할 때 프래그먼트들은 viewModel 을 통해 database를 이용할 수 있다.

 

 

//In FirebaseDatabaseViewModel

class FirebaseDatabaseViewModel : ViewModel() {
	val firebaseDB = Firebase.database
    val ref = database.reference
    
    fun <T> setValue(v:T) {
    	ref.child("tmp").setValue(v).setOnCompleteListener{
        	if(it.successful) {
            	Log.i("ViewModel","성공")
                
            } else {
            	Log.d("ViewModel","실패",it.exception)
            }
        }
    }
}

 

// any 프래그먼트

val viewModel = ViewModelProvider(requireActivity())[FirebaseDatabaseViewModel::class.java]

val value = "wow"
viewModel.setValue(value)