๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿค2024 ์•ˆ๋“œ๋กœ์ด๋“œ/โ˜€๏ธ ํ•˜๊ณ„ ์ธํ„ด ๊ฐœ๋ฐœ ํ”„๋กœ์ ํŠธ ๊ธฐ๋ก

0826 ANR ํ•ด๊ฒฐ ๊ณผ์ • ์ •๋ฆฌ

by hyeonha 2024. 12. 17.

๋ชฉ์ฐจ

    ANR ์ด์Šˆ

    ANR ๋ฐœ์ƒํ–ˆ๋˜ ์ƒํ™ฉ

    • ๋น„์ฝ˜ ๋ฐ์ดํ„ฐ ์Šค์บ”์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚˜๊ณ , ์Šค์บ๋„ˆ๊ฐ€ ๊บผ์ง€์ง€ ์•Š๊ณ  ๊ณ„์† ๋Œ์•„๊ฐ”์„ ๋•Œ
    • ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋ƒฅ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ฐ์ดํ„ฐ ์ธก์ •(์œ„์น˜, ๋น„์ฝ˜, ์„ผ์„œ)๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์„ ๋•Œ
    • ๋กœ๊ทธ์ธ ์„œ๋ฒ„ ์š”์ฒญ ์‹œ ์ง€์—ฐ์— ๋”ฐ๋ฅธ ํ™”๋ฉด ๋ฉˆ์ถค
    • ๋น„๋™๊ธฐ์™€ ์Šค๋ ˆ๋“œ์™€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๊ตฌ๋ถ„ ๋ช…ํ™•์น˜ ์•Š์Œ์ด ์›์ธ
    • ๊ฐ•์ œ ์ข…๋ฃŒ๊ฐ€ ๋ฐœ์ƒ๋จ
    • ํ™”๋ฉด์ด ํ•˜์–—๊ฒŒ ๋จ(UI ๋ฉˆ์ถค) : ๋กœ๊ทธ์ธ

    Android์•ฑ์˜ UI ์Šค๋ ˆ๋“œ๊ฐ€ ๋„ˆ๋ฌด ์˜ค๋žซ๋™์•ˆ ์ฐจ๋‹จ๋˜๋ฉด ANR ์˜ค๋ฅ˜๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ๋œ๋‹ค.

     

    ์™œ ๋ฌธ์ œ๊ฐ€ ๋˜๋‚˜?

    • UI ์—…๋ฐ์ดํŠธ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ๊ฐ€ ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ ๋˜๋Š” ๊ทธ๋ฆฌ๊ธฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜์—ฌ ์‚ฌ์šฉ์ž ๋ถˆ๋งŒ์„ ์ดˆ๋ž˜ํ•˜๋ฏ€๋กœ ๋ฌธ์ œ๊ฐ€ ๋œ๋‹ค.
    • ๋‚˜๋„ ๋กœ๊ทธ์ธ ์ค‘ ํ™”๋ฉด์ด ๋ฉˆ์ท„์„ ๋•Œ ๋‹ค๋ฅธ ๋ฒ„ํŠผ ํด๋ฆญ ๋ฐ ํ™”๋ฉด ์ž‘๋™์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํ™ฉ์ด ์•ฝ 4์ดˆ๊ฐ„ ์ง€์†๋จ์„ ๊ฒฝํ—˜ํ–ˆ๊ณ  ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์•ฑ์„ ๋‚˜๊ฐ€๊ฒŒ ๋˜์—ˆ๋‹ค.

    ์–ธ์ œ ANR์ด ๋ฐœ์ƒํ•˜๋Š”๊ฐ€?

    1. ์ž…๋ ฅ ์‘๋‹ต ์ง€์—ฐ
      1. ์‚ฌ์šฉ์ž๊ฐ€ ํ™”๋ฉด์„ ํ„ฐ์น˜ํ•˜๊ฑฐ๋‚˜ ํ‚ค๋ฅผ ๋ˆŒ๋ €์„ ๋•Œ, ์•ฑ์ด 5์ดˆ์ด๋‚ด์— ๋ฐ˜์‘ํ•˜์ง€ ์•Š์œผ๋ฉด ANR์ด ๋ฐœ์ƒ
      2. ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋ฌด๊ฑฐ์šด ์ž‘์—…์„ ์ˆ˜ํ–‰ ์‹œ ์ผ์–ด๋‚œ๋‹ค.
    2. ์„œ๋น„์Šค ์‹œ์ž‘ ์ง€์—ฐ
      1. ์„œ๋น„์Šค ์‹œ์ž‘ํ•  ๋•Œ onCreate, onStartCommand, onBind ๋ฉ”์„œ๋“œ๊ฐ€ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋˜์ง€ ์•Š์„ ๋Œ€ ๋ฐœ์ƒ
    3. ํฌ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ๋ฌธ์ œ
      1. startForegroundService()๋กœ ์„œ๋น„์Šค๋ฅผ ์‹œ์ž‘ํ–ˆ๋Š”๋ฐ, 5์ดˆ ๋‚ด์— startForeground()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด ANR์ด ๋ฐœ์ƒํ•ฉ
    4. ๋ธŒ๋กœ๋“œ ์บ์ŠคํŠธ ๋ฆฌ์‹œ๋ฒ„ ์ง€์—ฐ
      1. BroadcastReceiver๊ฐ€ ์ •ํ•ด์ง„ ์‹œ๊ฐ„(๋ณดํ†ต 5์ดˆ) ๋‚ด์— ์ž‘์—…์„ ์™„๋ฃŒํ•˜์ง€ ๋ชปํ•˜๋ฉด ANR์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ
    5. JobScheduler ๊ด€๋ จ ๋ฌธ์ œ
      1. JobService์˜ onStartJob() ๋˜๋Š” onStopJob() ๋ฉ”์„œ๋“œ๊ฐ€ ๋„ˆ๋ฌด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ฑฐ๋‚˜, ์‚ฌ์šฉ์ž ์‹œ์ž‘ ์ž‘์—…์—์„œ setNotification๋ฅผ ์ œ ๋•Œ ํ˜ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฐœ์ƒ

    ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

    • ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋ฌด๊ฑฐ์šด ์ž‘์—…์„ ํ”ผํ•˜๊ณ , ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ๋‚˜ ์ฝ”๋ฃจํ‹ด์„ ์‚ฌ์šฉํ•˜์ž
    • ์„œ๋น„์Šค, ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ๋ฆฌ์‹œ๋ฒ„,job service ๋“ฑ์—์„œ ๋น ๋ฅธ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ํ•„์š”ํ•˜๋ฉด ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ž.

    ์ƒํ™ฉ

    • ๋™๋ฃŒ ๊ฐœ๋ฐœ์ž๋ถ„์ด ์งœ์…จ๋˜ ๋กœ๊ทธ์ธ ๋กœ์ง์—์„œ ํ™”๋ฉด ๋ฉˆ์ถค ํ˜„์ƒ์ด ๋ฐœ์ƒํ–ˆ๋‹ค.
      • ๋กœ๊ทธ์ธ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์„œ๋ฒ„์— ์š”์ฒญ ~ ์‘๋‹ต๊นŒ์ง€ ์˜ค๋Š” ์‹œ๊ฐ„๋™์•ˆ UI๊ฐ€ ์ฐจ๋‹จ๋œ ๊ฒƒ์ด๋‹ค.
     // ๊ธฐ์กด 
        private fun login(id: String){
            val loginRequest = LoginRequest(id)
            RetrofitClient.instance.login(loginRequest).enqueue(object : Callback<LoginResponse>{
                override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
                    if(response.isSuccessful){
                        val loginResponse = response.body()
                        if (loginResponse?.success == true ){
                       
                            saveLoginState(true)
                            val intent = Intent(this@LoginActivity, AdminSettingActivity::class.java)
                            startActivity(intent)
                            finish()
                        }
                        else{
                            Log.d("๋กœ๊ทธ์ธ", "์žฌ์ž…๋ ฅ")
                        }
                    } else{
                        Log.d("๋กœ๊ทธ์ธ","๋กœ๊ทธ์ธ ์‹คํŒจ")
                        Log.d("๋กœ๊ทธ์ธ ์˜ค๋ฅ˜",response.errorBody().toString())
                    }

     

     // ํ˜„์žฌ
        private fun login(id: String) {
            val loginRequest = LoginRequest(id)
            CoroutineScope(Dispatchers.IO).launch {
                val result = runCatching {
                    RetrofitClient.instance.login(loginRequest)
                }
                withContext(Dispatchers.Main) {
                    result.onSuccess {
                        if (it.success) {
                            Toast.makeText(this@LoginActivity, "๋กœ๊ทธ์ธ ์™„๋ฃŒ", Toast.LENGTH_SHORT).show()
                            binding.loginButton.isEnabled = true
                            binding.loginButton.text = "ํ™•์ธ"
                            saveLoginState(true)
    
                            val intent = Intent(this@LoginActivity, AdminSettingActivity::class.java)
    
                            if (PreferenceUtil.getIpAddress(this@LoginActivity) == null) {
                                Toast.makeText(this@LoginActivity,"IP์ฃผ์†Œ๋ฅผ ์„ค์ •ํ•ด์ฃผ์„ธ์š”", Toast.LENGTH_SHORT).show()
                            }
                            startActivity(intent)
                            finish()
                        } else {
                            Toast.makeText(this@LoginActivity, "๋กœ๊ทธ์ธ ์‹คํŒจ", Toast.LENGTH_SHORT).show()
                            binding.loginButton.isEnabled = true
                            binding.loginButton.text = "ํ™•์ธ"
                            binding.loginButton.backgroundTintList =
                                ColorStateList.valueOf(Color.parseColor("#EE675C"))
                        }
                    }
                    result.onFailure {
                        binding.loginButton.isEnabled = true
                        binding.loginButton.text = "ํ™•์ธ"
                        Log.d("failed login ", "Network error")
                        Toast.makeText(this@LoginActivity, "๋กœ๊ทธ์ธ ์‹คํŒจ", Toast.LENGTH_SHORT).show()
                    }
                }
            }
    
    
        }

     

    ์ฐจ์ด

    • ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋ฐฉ์‹
      • Retrofit์˜ enqueue๋ฅผ ์‚ฌ์šฉํ•œ ์ฝœ๋ฐฑ
      • ์ฝ”๋ฃจํ‹ด์„ ์‚ฌ์šฉํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
    • ์—๋Ÿฌ ํ•ธ๋“ค๋ง
      • if-else
      • runCatching๊ณผ onSuccess, onFailure์„ ์‚ฌ์šฉ
    • ์Šค๋ ˆ๋“œ ๊ด€๋ฆฌ
      • ๋ช…์‹œ์ ์ธ ์Šค๋ ˆ๋“œ ๊ด€๋ฆฌ ์—†์Œ
      • Dispatchers.IO์™€ Dispatchers.Main์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ช…ํ™•ํ•œ ์Šค๋ ˆ๋“œ ๊ด€๋ฆฌ

    ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋ฐฉ์‹

    • Retrofit์˜ enqueue๋ฅผ ์‚ฌ์šฉํ•œ ์ฝœ๋ฐฑ
      • enqueue ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์‹œ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋œ๋‹ค.
      • ์ฝœ๋ฐฑ ์ธํ„ฐํŽ˜์ด์Šค์˜ onResponse๋‚˜ onFailure ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์Œ
      • ์žฅ์ 
        • ๊ฐ„๋‹จ & ์ง๊ด€
      • ๋‹จ์ 
        • ์—ฌ๋Ÿฌ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์—ฐ๊ณ„ํ•˜๊ฑฐ๋‚˜ ๋ณต์žกํ•œ ๋กœ์ง ์„ค๊ณ„ ์‹œ ์ฝœ๋ฐฑ ์ง€์˜ฅ์— ๋น ์งˆ ์ˆ˜ ์ž‡์Œ
    • ์ฝ”๋ฃจํ‹ด์„ ์‚ฌ์šฉํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
      • suspend ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ ์ž‘์—… ํ‘œํ˜„
        • suspend ํ•จ์ˆ˜์˜ ์˜๋ฏธ
      • launch ๋˜๋Š” async๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘

    ์Šค๋ ˆ๋“œ ๊ด€๋ฆฌ

    • Retrofit์˜ enqueue๋Š” ์ž์ฒด์ ์œผ๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ๋„คํŠธ์›Œํฌ ์ž‘์—…์„ ์ˆ˜ํ–‰
      • ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋จ
    • ์ฝ”๋ฃจํ‹ด
      • Dispatchers.IO : I/O ์ž‘์—…์— ์ตœ์ ํ™”๋œ ์Šค๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉ, ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด๋‚˜ ํŒŒ์ผ ์ž‘์—…
      • Dispatchers.Main : ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ž‘์—… ์‹คํ–‰, UI ์—…๋ฐ์ดํŠธ์— ์‚ฌ์šฉ
      • withContext(Dispatcher.Main) : ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋กœ ์ „ํ™˜

    ๋ฐ์ดํ„ฐ ์ €์žฅ ์œ„์น˜

    • ๋ ˆํŠธ๋กœํ• enqueue ์ด์šฉ ์ฝ”๋“œ
      • ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ saveLoginState ํ˜ธ์ถœ ์ค‘
    • ์ฝ”๋ฃจํ‹ด ์ด์šฉ ์ฝ”๋“œ
      • ๋™์ผํ•จ์„ ํ™•์ธ ํ›„ ์ˆ˜์ •
        • IO ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋„๋ก ์ˆ˜์ •
    728x90