[Android] ANR ์ •๋ฆฌ

Minjun Kimยท2023๋…„ 9์›” 2์ผ
0

Android

๋ชฉ๋ก ๋ณด๊ธฐ
30/47
post-thumbnail

๐Ÿ“ SeSAC์˜ 'JetPack๊ณผ Kotlin์„ ํ™œ์šฉํ•œ Android App ๊ฐœ๋ฐœ' ๊ฐ•์ขŒ๋ฅผ ์ •๋ฆฌํ•œ ๊ธ€ ์ž…๋‹ˆ๋‹ค.


๐Ÿ“‡ ANR ๊ฐœ์š”

Activity Not Response ์˜ ์•ฝ์–ด. ์•กํ‹ฐ๋น„ํ‹ฐ์—์„œ ์ผ์–ด๋‚˜๋Š” ์—๋Ÿฌ ์ƒํ™ฉ์„ ๋งํ•œ๋‹ค.

โ“ ์–ธ์ œ ๋ฐœ์ƒํ• ๊นŒ?

์œ ์ € ์ด๋ฒคํŠธ์— ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ 5์ดˆ ์ด๋‚ด์— ๋ฐ˜์‘ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ

์œ ์ €๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ€ํ•˜๋ฉด -> ์ด๋ฒคํŠธ๊ฐ€ ์ด๋ฒคํŠธ ํ์— ๋‹ด๊ธฐ๊ณ  -> ์‹œ์Šคํ…œ์—์„œ ์ด๋ฒคํŠธ ํ๋ฅผ ์ž๋™์œผ๋กœ ๊ฐ์ง€ -> ์ด๋ฒคํŠธ ํ์—์„œ ์ด๋ฒคํŠธ ์ถ”์ถœ ํ›„ ์—…๋ฌด ์ง„ํ–‰

โ— ANR ๋ฌธ์ œ๋ž€ ์ด๋ฒคํŠธ ํ์— ์ด๋ฒคํŠธ๊ฐ€ ๋‹ด๊ธฐ๋Š” ์ˆœ๊ฐ„๋ถ€ํ„ฐ 5์ดˆ ์ด๋‚ด์— ํ•ด๋‹น ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๊ฐ€ ์ง„ํ–‰๋˜์ง€ ์•Š์œผ๋ฉด ์‹œ์Šคํ…œ์—์„œ ๊ฐ•์ œ์ ์œผ๋กœ ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ์ข…๋ฃŒ์‹œํ‚ค๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.

๊ทธ๋ƒฅ ๋„คํŠธ์›Œํ‚น ์ด ๋“ค์–ด๊ฐ€๋Š” ์ฝ”๋“œ๋Š” ๋ฌด์กฐ๊ฑด ANR ๋ฌธ์ œ๋ฅผ ๊ณ ๋ คํ•ด์•ผ๋งŒ ํ•˜๋‹ค๊ณ  ์™ธ์šฐ์ž.

๐Ÿ“Œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

Thread - Handler ๋กœ ํ•ด๊ฒฐ

AsyncTask ๋กœ ํ•ด๊ฒฐ

Coroutine ์œผ๋กœ ํ•ด๊ฒฐ

์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์•ˆ๋“œ๋กœ์ด๋“œ 11๋ฒ„์ „์œผ๋กœ ์˜ฌ๋ผ๋ฉด์„œ ์•ž์˜ ๋‘ ๊ฐœ๋Š” deprecated ๋๋‹ค. ํ˜„์žฌ Coroutine ์œผ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ๋‹ค.


๐Ÿ“š Handler

onCreate ๋ถ€ํ„ฐ ์‹คํ–‰์‹œ์ผœ ํ™”๋ฉด์„ ์ถœ๋ ฅ ์‹œํ‚ค๊ณ  ์œ ์ € ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ๋ผ๊ณ  ํ•œ๋‹ค. ๋˜๋Š” UI๋ฅผ ์ถœ๋ ฅ ํ•œ๋‹ค ํ•˜์—ฌ UI ์Šค๋ ˆ๋“œ๋ผ ๋ถ€๋ฅธ๋‹ค. ANR ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์™€ ๋ณ„๊ฐœ๋กœ ์—…๋ฌด ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•  ๊ฐœ๋ฐœ์ž ์Šค๋ ˆ๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๊ทธ๋ž˜์„œ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…๋“ค์€ ์Šค๋ ˆ๋“œ๋ฅผ ๋”ฐ๋กœ ๋„์ถœํ•ด์„œ ์ฒ˜๋ฆฌํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๊ฐœ๋ฐœ์ž ์Šค๋ ˆ๋“œ๋กœ ํ”„๋กœ๊ทธ๋žจ์„ ์งœ๋ฉด ANR ๋ฌธ์ œ๋Š” ํ”ผํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

์•กํ‹ฐ๋น„ํ‹ฐ ๋‚ด์—์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์™€ ๊ฐœ๋ฐœ์ž ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋‹ค. ๊ทธ ์™€์ค‘์— ๊ฐœ๋ฐœ์ž ์Šค๋ ˆ๋“œ๊ฐ€ ํ™”๋ฉด ์ถœ๋ ฅ ์š”์†Œ์ธ View ์— ์ ‘๊ทผ์„ ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ฆ‰, ๊ฐœ๋ฐœ์ž ์Šค๋ ˆ๋“œ ๋‚ด์—์„œ ํ™”๋ฉด ์ถœ๋ ฅ ์š”์†Œ์ธ View ์— ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ”„๋กœ๊ทธ๋žจ์„ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค. ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์— ์˜ํ•ด์„œ๋งŒ View ๊ฐ€ ๊ด€๋ฆฌ๋˜์–ด์•ผ๋งŒ ํ•œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŠน์„ฑ ์ƒ ํ™”๋ฉด ์ถœ๋ ฅ์ด ์—…๋ฌด์˜ ์•ŒํŒŒ์ด์ž ์˜ค๋ฉ”๊ฐ€์ด๋‹ค. ๊ฐœ๋ฐœ์ž ์Šค๋ ˆ๋“œ์—์„œ ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋Œ์–ด์™€๋„, ๊ฒฐ๊ตญ ๊ทธ๊ฑธ ํ™”๋ฉด์— ๋„์›Œ์•ผ ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ?

๐Ÿ“Œ Handler ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๋งŒ์•ฝ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•˜๋Š” View ๋ฅผ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค๋ฉด, ์ง์ ‘ ์ ‘๊ทผํ•˜์ง€ ๋ง๊ณ  Handler ํด๋ž˜์Šค ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Handler ํด๋ž˜์Šค๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—๊ฒŒ ์—…๋ฌด๋ฅผ ์˜๋ขฐํ•˜๋Š” ๊ฒƒ์œผ๋กœ, ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์— ์˜ํ•ด์„œ๋งŒ View๊ฐ€ ๊ด€๋ฆฌ ๋˜๊ฒŒ๋” ํ•œ๋‹ค.

์ด๊ฒƒ์„ ํ”ํžˆ ์Šค๋ ˆ๋“œ-ํ•ธ๋“ค๋Ÿฌ ๊ตฌ์กฐ ๋ผ๊ณ  ํ•œ๋‹ค. ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์œผ๋กœ ๊ฐœ๋ฐœ ์‹œ์— ์“ฐ๋ ˆ๋“œ ๊ฐ„์˜ ์—ญํ•  ๋ถ„๋‹ด์„ ์™„๋ฒฝํžˆ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ, ํ™”๋ฉด์„ ๊ด€๋ฆฌํ•˜๋Š” ์Šค๋ ˆ๋“œ์™€ ์—…๋ฌด๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ๋‚˜๋ˆˆ๋‹ค.

๐Ÿ“Œ Handler ํ•จ์ˆ˜

Handler ๋กœ main thread ์—๊ฒŒ ์ž‘์—… ์˜๋ขฐ

  • sendMessage(Message msg) : ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์€ ๋ฉ”์‹œ์ง€ ๊ฐ์ฒด๋ฅผ ๋ฐ›๋Š”๋‹ค.

  • sendMessageAtFrontOfQueue(Message msg) : View ์ž‘์—…์— ๋Œ€ํ•œ ์˜๋ขฐ๊ฐ€ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ UI Thread์—์„œ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์ด๋ฒˆ ์˜๋ขฐ๋ฅผ ๊ฐ€์žฅ ๋จผ์ € ์ฒ˜๋ฆฌํ•˜๋ผ๋Š” ์š”์ฒญ

  • sendMessageAtTime(Message msg, long uptimeMillis) : ์˜๋ขฐ๋ฅผ ์ง€์ •๋œ ์‹œ๊ฐ„์— ์ˆ˜ํ–‰

  • sendMessageDelayed(Message msg, long delayMillis) : ์ง€์—ฐ์‹œ๊ฐ„ ํ›„์— ์ˆ˜ํ–‰ ์‹œ์ผœ ๋‹ฌ๋ผ๋Š” ์š”์ฒญ

  • sendEmptyMessage(int what) : ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ์—†์ด ์˜๋ขฐํ•˜๋Š” ๊ฒฝ์šฐ

๐Ÿ“Œ Message ํ”„๋กœํผํ‹ฐ

Message ๊ฐ์ฒด๋Š” main thread ์—๊ฒŒ ๋„˜๊ธฐ๋Š” ๋ฐ์ดํ„ฐ

  • what : int ํ˜• ๋ณ€์ˆ˜๋กœ ๊ตฌ๋ถ„์ž. ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž„์˜ ์ˆซ์ž๊ฐ’์œผ๋กœ ์š”์ฒญ์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉ.

  • obj : UI Thread ์—๊ฒŒ ๋„˜๊ธธ ๋ฐ์ดํ„ฐ. Object ํƒ€์ž…์˜ ๋ณ€์ˆ˜.

  • arg1, arg2 : UI Thread ์—๊ฒŒ ๋„˜๊ธธ ๋ฐ์ดํ„ฐ. int ํƒ€์ž…์œผ๋กœ ๊ฐ„๋‹จํ•œ ์ˆซ์ž๊ฐ’์€ arg1, arg2 ๋ณ€์ˆ˜์— ๋‹ด์•„ ์ „๋‹ฌ.

์ˆซ์ž๊ฐ’ ๋„˜๊ธฐ๊ธฐ ๋“ฑ์˜ ๊ฐ„๋‹จํ•œ ์ž‘์—… ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” obj ๊ฐ€ ์•„๋‹Œ arg1, arg2 ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿงฉ ๊ฐœ๋ฐœ์ž ์Šค๋ ˆ๋“œ ์ฝ”๋“œ

var message = Message()			// ๋ฉ”์‹œ์ง€ ์ค€๋น„
message.what = 1				// ์‹๋ณ„์ž
message.arg1 = count			// ๋„˜๊ธฐ๋Š” ๋ฐ์ดํ„ฐ
handler.sendMessage(message)	// ํ•ธ๋“ค๋Ÿฌ ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ ์ฝœ

๐Ÿงฉ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ์ฝ”๋“œ

var handler: Handler = object : Handler() {		// Handler ํด๋ž˜์Šค ์ƒ์†
	override fun handleMessage(msg: Message) {	// handleMessage ํ•จ์ˆ˜ ์˜ค๋ฒ„๋ผ์ด๋“œ
    	if (msg.what === 1) {
        	textView.setText(String.valueOf(msg.arg1)) 
		} else if (msg.what === 2) {
        	textView.text = msg.obj as String
		}
	}
}
  • sendMessage ๋กœ ์˜๋ขฐ๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์— ์˜ํ•ด์„œ handleMessage ๊ฐ€ ์ž๋™ ์ฝœ ๋œ๋‹ค.
    ๊ทธ๋ฆฌ๊ณ  ๊ฐœ๋ฐœ์ž ์Šค๋ ˆ๋“œ์—์„œ ๋ณด๋‚ธ ๋ฉ”์‹œ์ง€ ๊ฐ์ฒด๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ ๋œ๋‹ค.

๐Ÿงฉ ์‹ค์Šต ์˜ˆ์ œ

์œ ์ € ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„๋“ค์ด๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์™€ ์นด์šดํŠธ ๋‹ค์šด ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฐœ๋ฐœ์ž ์Šค๋ ˆ๋“œ๋กœ ๋ถ„๋ฆฌ ํ•ด๋ณด์ž.

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#1a237e">

    <TextView
        android:id="@+id/main_textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="10"
        android:textSize="80dp"
        android:textStyle="bold"
        android:textColor="@android:color/white"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="16dp">
        <ImageView
            android:id="@+id/main_startBtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:src="@drawable/ic_start"
            android:clickable="true"/>
        <ImageView
            android:id="@+id/main_pauseBtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:src="@drawable/ic_pause"
            android:clickable="true"/>
    </LinearLayout>
</RelativeLayout>

  • MainActivity.kt
package com.kotdev99.android.c47

class MainActivity : AppCompatActivity() {

	private lateinit var startView: ImageView
	private lateinit var pauseView: ImageView
	private lateinit var textView: TextView

	// ์Šค๋ ˆ๋“œ ์ œ์–ด boolean
	private var loopFlag = true
	private var isFirst = true
	private var isRun = false

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_main)

		startView = findViewById(R.id.main_startBtn)
		pauseView = findViewById(R.id.main_pauseBtn)
		textView = findViewById(R.id.main_textView)

		startView.setOnClickListener {
			if (isFirst) {
				isFirst = false
				isRun = true
				thread.start()
			} else {
				isRun = true
			}
		}
		pauseView.setOnClickListener {
			isRun = false
		}
	}

	var handler: Handler = object : Handler() {
		override fun handleMessage(msg: Message) {
			if (msg.what === 1) {
				textView.setText(String.valueOf(msg.arg1))
			} else if (msg.what === 2) {
				textView.text = msg.obj as String
			}
		}
	}

	var thread: Thread = object : Thread() {
		override fun run() {
			try {
				var count = 10
				while (loopFlag) {
					sleep(1000)
					if (isRun) {
						count--
						var message = Message()
						message.what = 1
						message.arg1 = count
						handler.sendMessage(message)
						if (count == 0) {
							message = Message()
							message.what = 2
							message.obj = "Finish!!"
							handler.sendMessage(message)
							loopFlag = false
						}
					}
				}
			} catch (e: Exception) {


			}
		}
	}
}

๐Ÿ“š AsyncTask

ANR ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜. deprecation ๋˜์—ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋‹ค.

Thread - Handler ๊ตฌ์กฐ๋ฅผ ์ถ”์ƒํ™”ํ•˜์—ฌ ์ข€ ๋” ์‰ฝ๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๊ฒŒ๋” ๋งŒ๋“ค์—ˆ๋‹ค.

AsyncTask ๋ฅผ ์ƒ์†๋ฐ›์•„ ์ž‘์„ฑํ•œ๋‹ค.

class MyAsyncTask: AsyncTask<Void?, Int?, String>() {
	//..............
}

๐Ÿ“Œ AsyncTask ํ•จ์ˆ˜

doInBackground(Params... params)
Thread์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋  ๋‚ด์šฉ์„ ๋‹ด๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜. ๋ฐ˜๋“œ์‹œ ์˜ค๋ฒ„๋ฆฌ์•„๋“œํ•ด์•ผ ํ•œ๋‹ค.

onPreExecute()
AsyncTask์˜ ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ํ˜ธ์ถœ. AsyncTask์—์„œ ๊ฐ€์žฅ ๋จผ์ € ํ•œ ๋ฒˆ ํ˜ธ์ถœ.

onPostExecute(Result result)
AsyncTask์˜ ๋ชจ๋“  ์ž‘์—…์ด ์™„๋ฃŒ๋œ ํ›„ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ํ•œ ๋ฒˆ ํ˜ธ์ถœ. doInBackground ํ•จ์ˆ˜์˜ ์ตœ์ข… ๊ฐ’์„ ๋ฐ›๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ.

onProgressUpdate(Progress... values)
doInBackground์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋˜๋Š” ์ค‘๊ฐ„์ค‘๊ฐ„ ๊ฐ’์„ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ˜ธ์ถœ. doInBackground์—์„œ publishProgress ํ•จ์ˆ˜๋กœ ๋„˜๊ธด ๊ฐ’์ด ์ „๋‹ฌ.

โ— ์ฃผ์˜์ 

์—…๋ฌด ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๊ฐ’์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์ œ๋„ค๋ฆญ์œผ๋กœ ์„ ์–ธํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ AsyncTask๋ฅผ ์ƒ์†๋ฐ›์„ ๋•Œ ์ œ๋„ค๋ฆญ์œผ๋กœ 3๊ฐœ์˜ ํƒ€์ž…์„ ์ง€์ •ํ•ด ์ค˜์•ผ ํ•œ๋‹ค.

class MyAsyncTask: AsyncTask<Void?, Int?, String>()
  • ์ฒซ ๋ฒˆ์งธ ํƒ€์ž… : Background ์ž‘์—…์„ ์œ„ํ•œ doInBackground ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž…๊ณผ ๋™์ผ. AsyncTask ์— ์˜ํ•ด Background ์ž‘์—…์„ ์˜๋ขฐํ•  ๋•Œ ๋„˜๊ธธ ๋ฐ์ดํ„ฐ์˜ ํƒ€์ž…. ์—†์œผ๋ฉด Void ๋กœ ์ง€์ •.

  • ๋‘ ๋ฒˆ์งธ ํƒ€์ž… : doInBackground ํ•จ์ˆ˜ ์ˆ˜ํ–‰์— ์˜ํ•ด ๋ฐœ์ƒํ•œ ๋ฐ์ดํ„ฐ๋ฅผ publishProgress() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์ „๋‹ฌํ•˜๋Š”๋ฐ, ์ด ๋•Œ์˜ ์ „๋‹ฌ ๋ฐ์ดํ„ฐ ํƒ€์ž…. ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ ๋ฐ›์„ onProgressUpdate ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํƒ€์ž…๊ณผ ๋™์ผํ•˜๊ฒŒ ์ง€์ •.

  • ์„ธ ๋ฒˆ์งธ ํƒ€์ž… : onPostExecute ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž…๊ณผ ๋™์ผํ•˜๊ฒŒ ์ง€์ •. doInBackground ํ•จ์ˆ˜์˜ ๋ฆฌํ„ด ํƒ€์ž…์ด๋ฉฐ ๋ฆฌํ„ด๋œ ๋ฐ์ดํ„ฐ๊ฐ€ onPostExecute ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋จ.

์ฆ‰ <doInBackground ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ , onProgressUpdate ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ , onPostExecute ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜>

๐Ÿงฉ ์‹ค์Šต ์˜ˆ์ œ

  • activity_main.xml

์œ„ ์‹ค์Šต๊ณผ ๋™์ผ

  • MainActivity.kt
package com.kotdev99.android.c48

class MainActivity : AppCompatActivity() {

	lateinit var startView: ImageView
	lateinit var pauseView: ImageView
	lateinit var textView: TextView

	var isFirst = true

	lateinit var asyncTask: MyAsyncTask

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_main)

		startView = findViewById(R.id.main_startBtn)
		pauseView = findViewById(R.id.main_pauseBtn)
		textView = findViewById(R.id.main_textView)

		startView.setOnClickListener {
			if (isFirst) {
				asyncTask.isRun = true
				asyncTask.execute()     // execute ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ˆœ๊ฐ„ doInBackground ํ•จ์ˆ˜ ์‹คํ–‰
				isFirst = false
			} else {
				asyncTask.isRun = true
			}
		}

		pauseView.setOnClickListener {
			asyncTask.isRun = false
		}

		asyncTask = MyAsyncTask()
	}

	inner class MyAsyncTask : AsyncTask<Void?, Int?, String>() {
		var loopFlag = true
		var isRun = false

		override fun doInBackground(vararg p0: Void?): String {
			var count = 10
			while (loopFlag) {
				SystemClock.sleep(1000)
				if (isRun) {
					count--
					publishProgress(count)
					if (count == 0) {
						loopFlag = false
					}
				}
			}
			return "Finish!!"
		}

		// ์ด ํ•จ์ˆ˜๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์— ์˜ํ•ด์„œ ํ˜ธ์ถœ๋จ. ์ฆ‰, ๋ทฐ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Œ.
		// doInBackground ํ•จ์ˆ˜์—์„œ publishProgress ๊ฐ€ ์ฝœ ๋˜์—ˆ์„ ๋•Œ ํ˜ธ์ถœ ๋œ๋‹ค.
		override fun onProgressUpdate(vararg values: Int?) {
			super.onProgressUpdate(*values)
			textView.setText(values[0].toString())
		}

		// doInBackground ํ•จ์ˆ˜๊ฐ€ ์ตœ์ข… ์ข…๋ฃŒ๋œ ์ˆœ๊ฐ„์— ๋งˆ์ง€๋ง‰ ๋ฆฌํ„ด ๊ฐ’์„ ๋ฐ›์•„์„œ ํ˜ธ์ถœ ๋œ๋‹ค.
		override fun onPostExecute(result: String?) {
			super.onPostExecute(result)
			textView.setText(result)
		}
	}
}

๐Ÿ“š Coroutine

์ฝ”๋ฃจํ‹ด์˜ ๊ฒฝ์šฐ ๋…์ž ๊ฐ•์˜ ์ฝ”์Šค๊ฐ€ ๋งŒ๋“ค์–ด์งˆ ์ •๋„๋กœ ๋นก์„ธ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ANR ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ์ •๋„๋กœ๋งŒ ๋‹ค๋ฃฌ๋‹ค.

ํ”ํžˆ Non-Blocking lightweight thread ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

๊ตฌ๊ธ€ ๊ณต์‹ ๋ฌธ์„œ์— ์ ํ˜€์žˆ๋Š” ์žฅ์ ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • ๊ฒฝ๋Ÿ‰์ด๋‹ค.

  • ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์ ๋‹ค.

  • ์ทจ์†Œ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋ฒ•์„ ์ง€์›ํ•˜๋‹ค.

  • ๋งŽ์€ JetPack ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ ์šฉ๋˜์–ด ์žˆ๋‹ค.

์Šค๋ ˆ๋“œ๋Š” ์•„๋‹ˆ์ง€๋งŒ ์Šค๋ ˆ๋“œ์™€ ๋™์ผํ•˜๊ฒŒ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์™€๋Š” ๋ณ„๊ฐœ๋กœ ์ž‘์—… ์ˆ˜ํ–‰์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์‚ฌ์šฉํ•˜๋ ค๋ฉด ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")

๐Ÿ“Œ Scope

์ฝ”๋ฃจํ‹ด ์ž‘์—…์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์šฐ์„  ์Šค์ฝ”ํ”„ ๋ผ๋Š” ๊ฒŒ ํ•„์š”ํ•˜๋‹ค.

  • ์Šค์ฝ”ํ”„ : ์ฝ”๋ฃจํ‹ด์ด ์‹คํ–‰๋˜๋Š” ์˜์—ญ

  • CoroutineScope ์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค

  • GlobalScope, ActorScope, ProducerScope ๋“ฑ ์ œ๊ณต

val backgroundScope = CoroutineScope(Dispatchers.Default + Job())

๐Ÿ“Œ Dispatcher

์ฝ”๋ฃจํ‹ด์„ ๊ตฌ๋™์‹œํ‚ฌ ๋•Œ ์ด ์ฝ”๋ฃจํ‹ด์„ ์–ด๋–ค ์Šค๋ ˆ๋“œ๊ฐ€ ๋งก์„ ๊ฒƒ์ธ๊ฐ€๋ฅผ ์ง€์ •

  • Dispatchers.Main : ์•กํ‹ฐ๋น„ํ‹ฐ์˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•˜๋Š” ์ฝ”๋ฃจํ‹ด์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๋””์ŠคํŒจ์ฒ˜. ๋ทฐ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ ๋””์ŠคํŒจ์ฒ˜. UI ๊ด€๋ จ ์ž‘์—…์€ ๋ฌด์กฐ๊ฑด ์ด๊ณณ์—์„œ ์ฒ˜๋ฆฌ ๋˜์–ด์•ผ๋งŒ ํ•œ๋‹ค.

  • Dispatchers.IO : ํŒŒ์ผ์— ์ฝ๊ธฐ ํ˜น์€ ์“ฐ๊ธฐ ๋˜๋Š” ๋„คํŠธ์›Œํฌ ์ž‘์—… ๋“ฑ์— ์ตœ์ ํ™”๋œ ๋””์ŠคํŒจ์ฒ˜

  • Dispatchers.Default : CPU๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ์ž‘์—…์„ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰ํ•  ๋ชฉ์ ์˜ ๋””์ŠคํŒจ์ฒ˜

Scope ์— Dispatcher ๋ฅผ ์ง€์ •ํ•ด์„œ ์ฝ”๋ฃจํ‹ด์„ ๊ตฌ๋™์‹œํ‚ค๋ฉด, ์ฝ”๋ฃจํ‹ด ์ด Dispatcher ์— ์˜ํ•ด์„œ ์‹คํ–‰์ด ๋œ๋‹ค.

๐Ÿ“Œ Coroutine ์‹คํ–‰

Coroutine ์€ launch, async ๋“ฑ์˜ ํ•จ์ˆ˜์— ์˜ํ•ด ์‹คํ–‰

launch, async ๋Š” Coroutine Builder ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

backgroundScope.launch {
	//.........
}

๐Ÿงฉ ์‹ค์Šต ์˜ˆ์ œ

CLICK ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์—ฐ์‚ฐ ์ž‘์—…์„ ์ˆ˜ํ–‰.

๊ทธ ์™€์ค‘์— EditText์— ์œ ์ € ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ANR ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๊ฒƒ์„ ์ฝ”๋ฃจํ‹ด์œผ๋กœ ํ•ด๊ฒฐ ํ•ด๋ณด์ž.

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="click" />

    <TextView
        android:id="@+id/resultView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textStyle="bold" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

  • MainActivity.kt
package com.kotdev99.android.c49

class MainActivity : AppCompatActivity() {

	// ๊ต‰์žฅํžˆ ํฐ ์—ฐ์‚ฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•˜๋Š” ์ฝ”๋ฃจํ‹ด
	val backgroundScope = CoroutineScope(Dispatchers.Default + Job())

	lateinit var button: Button
	lateinit var resultView: TextView

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_main)

		button = findViewById(R.id.button)
		resultView = findViewById(R.id.resultView)

		button.setOnClickListener {
			backgroundScope.launch {
				var sum = 0L
				var time = measureTimeMillis {
					for (i in 1..2_000_000_000) {
						sum += i
					}
				}
				// Dispatchers.Main ์—๊ฒŒ ๊ฒฐ๊ณผ ๊ฐ’์„ ํ™”๋ฉด์— ์ฐ์–ด ๋‹ฌ๋ผ๊ณ  ์˜๋ขฐ
				withContext(Dispatchers.Main) {
					resultView.text = "sum : $sum"
				}
			}
		}
	}
}

๐Ÿ“ฒ ๊ฒฐ๊ณผ

profile
์‘์•  ๋‚˜ ์•„๊ธฐ ๋‰ด๋น„

0๊ฐœ์˜ ๋Œ“๊ธ€