안드로이드/앱개발(Android)

(Android) 심리테스트 앱 - 직접 구현해보기 (1)

김염인 2022. 4. 8. 14:38

 

ㅁ Tech Stack

  • Retrofit2
  • Viewpager
  • BottomNavigationBar
  • Firebase 
  • Corutine

안드로이드 앱 출시 목표로 심리테스트를 할 수 있는 앱을 초기 부분을 구현해 보았다.

현재 앱을 실행하면 Splash Screen이 2초간 보인 뒤 main ui로 넘어가는 기능을 구현하였다.

 

1. Splash Screen

여기서 사용된 비동기 처리 방식은 coroutine이다. splash 화면이 진행 되는 동안의 2초간 Delay에 Coroutine Scope를 사용하여 Firebase에 있는 JsonData를 불러오는 것을 목표로 하였다.

 

 

 

 

 

ㅁ 구현 목표는 첫번째 아래와 같이 Firebase에 있는 json형태로 저장돼 있는 DB를 가져오는 것이다. (내가 임의로 만들어 봤다)

기타심리테스트 / Simple_test_third

 

 

 

FirebaseRemoteConfig의 Json 형태의 데이터 매개변수 3개가 있다.

  • Simple_test : 연애 심리테스트 목록
  • Simple_test_second : 성격 심리 테스트 목록
  • Simple_test_third : 기타 심리 테스트 목록

( 변수 네이밍 신경을 써야 하지만 초기 단계라....신경을 쓰지 못했당)

 

이렇게 구성 된 Json Db를 Splash Activity(화면이 켜지는 시간 동안) 가져와서 Intent를 통해 데이터를 MainActivity로 넘겨주었다.

그러면 자연스럽게 Main에서 DB를 가져오는 것을 기다릴 필요 없이 바로 데이터를 가져다 쓸 수 있다.

 

 

 

CoroutineScope 구현
CoroutineScope(Dispatchers.Main).launch {

    CoroutineScope(Dispatchers.IO).launch {
        initData("simple_test")
        initData("simple_test_second")
        initData("simple_test_third")
    }

    launch {
        subroutine()
    }
}

 

 

  • Dispatchers.Main - 이 디스패처를 사용하여 기본 Android 스레드에서 코루틴을 실행합니다. 이 디스패처는 UI와 상호작용하고 빠른 작업을 실행하기 위해서만 사용해야 합니다. 예를 들어 suspend 함수를 호출하고 Android UI 프레임워크 작업을 실행하며 LiveData 객체를 업데이트합니다.
  • Dispatchers.IO - 이 디스패처는 기본 스레드 외부에서 디스크 또는 네트워크 I/O를 실행하도록 최적화되어 있습니다. 예를 들어 회의실 구성요소를 사용하고 파일에서 읽거나 파일에 쓰며 네트워크 작업을 실행합니다.
  • Dispatchers.Default - 이 디스패처는 CPU를 많이 사용하는 작업을 기본 스레드 외부에서 실행하도록 최적화되어 있습니다. 예를 들어 목록을 정렬하고 JSON을 파싱합니다.

 

 

코루틴 권장사항에 따라 DB를 가져오는 작업인 initData는 Dispatchers.IO를 사용, Splash 화면은 Dispatchers.Main에서 실행시켜 주었다.

 

 

 

 

2. Json Parsing

suspend fun initData(dataKey: String) {
    val remoteConfig = Firebase.remoteConfig
    remoteConfig.setConfigSettingsAsync(
        remoteConfigSettings {
            minimumFetchIntervalInSeconds = 0
        }
    )

    remoteConfig.fetchAndActivate().addOnCompleteListener {
        if (it.isSuccessful) {
            val tests = parseQuotesJson(remoteConfig.getString("${dataKey}"))

            val article = Article(tests)
            articles.add(article)
            keys.add(dataKey)

        }
    }
}

private fun parseQuotesJson(json: String): List<LoveTest> {
    val jsonArray = JSONArray(json)

    var jsonList = emptyList<LoveTest>()

    for (index in 0 until jsonArray.length()) {
        val jsonObject = jsonArray.getJSONObject(index)

        val answerArray = jsonObject.getJSONArray("answer")
        val resultArray = jsonObject.getJSONArray("result")
        val questionArray = jsonObject.getJSONArray("question")

        val answerArrayList = ArrayList<String>()
        val resultArrayList = ArrayList<String>()
        val questionArrayList = ArrayList<String>()

        for (index_j in 0 until answerArray.length()) {
            answerArrayList.add(answerArray.getString(index_j))
        }
        for (index_j in 0 until resultArray.length()) {
            resultArrayList.add(resultArray.getString(index_j))
        }
        for (index_j in 0 until questionArray.length()) {
            questionArrayList.add(questionArray.getString(index_j))
        }

        val loveTestSample = LoveTest(
            jsonObject.getString("title"),
            jsonObject.getString("description"),
            jsonObject.getString("imgurl"),
            questionArrayList,
            answerArrayList,
            resultArrayList
        )

        jsonList = jsonList + loveTestSample
    }

    return jsonList.map {
        LoveTest(
            title = it.title,
            description = it.description,
            imgurl = it.imgurl,
            question = it.question,
            answer = it.answer,
            result = it.result
        )
    }
}

Firebase를 통해 가져온 Json 파일을 Intent를 통해 넘겨주기 위해선 Json을 객체에 담아 줄 수 있게 끔 parsing이 필요하다.(조금 귀찮은 작업 이지만 중요하니깐!!) Json data를 parsing하여 객체에 담아 주는 작업을 진행하였다.

 

 

 

 

@Parcelize
data class LoveTest(
    val title: String?,
    val description: String?,
    val imgurl: String?,
    val question: ArrayList<String>?,
    val answer: ArrayList<String>?,
    val result: ArrayList<String>?,
) : Parcelable
@Parcelize
data class Article(
    val article : List<LoveTest>
) : Parcelable

@Parcelize 형태로 data Class를 구축하면 intent를 통해 한꺼번에 data 객체를 넘겨 줄 수 있다.

 

 

 

 

3.  Parsing된 Data 넘겨주기

suspend fun subroutine() {
    delay(SPLASH_DELAY)
    var intent = Intent(this@SplashActivity, MainActivity::class.java)

    intent.putExtra("${keys[0]}", articles[0])
    intent.putExtra("${keys[1]}", articles[1])
    intent.putExtra("${keys[2]}", articles[2])

    startActivity(intent)
    finish()
}

마지막으로 MainActivity에 데이터를 넘겨준다 CoroutineScope에서 맨마지막으로 SPLASH_DELAY에 2000을 넣어주어 data가 Init된것을 2초간 기다리고  배열로 저장된 Article Json 객체를 넘겨준다.

 

 

 

 

 

 

MainActivity.kt

one = intent.getParcelableExtra<Article>("simple_test")!!
two = intent.getParcelableExtra<Article>("simple_test_second")!!
three = intent.getParcelableExtra<Article>("simple_test_third")!!

- getParcelableExtra를 통해 intent를 받아준다. 그러면 Json Data가 Intent돼서 Main 화면으로 잘 전달 됐다.

 

 

 

 

 

 

 

 

느낀점 : 더 좋은 방법으로 구축 하는 방법이 있을것 같지만 아직 지식이 넓지 않아 최선의 방법을 통해 구축해보았다. Room Db를 이용하면 전역적으로 DB접근이 가능할 것 같다는 생각도 들어서 Companion Object를 이용한 Appdatabase를 구축 해볼까 생각중이다.