티스토리 뷰
동기(Sync) 및 비동기(Async) 방식의 차이를 알아보고, 안드로이드 앱 개발 시 비동기 방식을 어떻게 구현하는지 알아봅니다.
동기 방식은 어떤 작업을 순서대로 진행하는 것을 말하며, 비동기 방식은 순서대로 진행하지 않는 것을 의미합니다.
비동기 방식은 스레드를 따로 만들어서 작업을 처리합니다. 메인 스레드가 일을 처리할 때, 스레드를 만들어서 작업을 따로 처리하게 됩니다.
비동기 방식의 장점은 메인 스레드의 작업을 기다리게 할 필요가 없도록 합니다. 특히 비동기 방식은 네트워크 작업에 적합합니다. 서버에서 어떤 요청을 처리하는 사이 다른 작업이 발생할 때, 동기 방식이라면 순서를 기다려야 하지만, 비동기 방식을 기다릴 필요 없이, 별도의 스레드가 그 작업을 처리합니다.
비동기 방식의 단점은 재사용이 불가능합니다. 그리고 비동기가 구현된 액티비티가 종료되어 다른 액티비티로 넘어가더라도 이미 구현된 비동기는 멈추지 않고 계속 실행됩니다. 한 액티비티 내에서 AsyncTask는 하나만 실행될 수 있으며, 병렬처리가 되지 않습니다.
안드로이드에서 비동기 방식을 구현 시 AsyncTask를 상속받게 됩니다. 해당 클래스는 다음의 메소드를 오버라이딩할 수 있습니다.
1. onPreExcute : 이 메소드에는 메인 스레드와는 별도의 스레드가 시작되기 전에 수행되어야 할 작업을 기술합니다.
2. doInBackground: 메인 스레드 외의 다른 스레드가 하게 될 작업을 기술합니다.
3. onPregressUpdate: 마치 프로그레스 바와 비슷한 개념으로, 중간 중간에 메인 스레드로 돌아가 작업 상황을 알려주는 부분입니다.
4. onPostExcute: 다른 스레드가 작업을 다 마치면 메인 스레드로 넘어가는 부분입니다.
그럼 실제로 비동기를 어떻게 구현하는지 알아보도록 합니다.
비동기를 실행하면 프로그레스 바가 채워지고 중간에 취소도 할 수 있도록 해보겠습니다.
우선 액티비티를 만들고, 연관된 레이아웃에 프로그레스 바와 버튼 2개를 달아줍니다. 아래의 ProgressBar 뷰를 보면, 속성으로 스타일을 줄 수 있는데, style="?android:attr/progressBarStyleHorizontal" 이렇게 넣게 되면, 막대 형태의 프로그레스 바를 얻게 됩니다. max 값은 100으로 채워줍니다.
<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=".AsyncActivity">
<TextView
android:id="@+id/mytask"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AsyncTask"
android:textSize="50dp" />
<ProgressBar
android:id="@+id/progressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:max="100" />
<Button
android:id="@+id/start"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginTop="40dp"
android:text="시작" />
<Button
android:id="@+id/stop"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginTop="40dp"
android:text="취소" />
</LinearLayout>
이후에 액티비티로 돌아와서 비동기 클래스를 만들어줍니다. 먼저 코드를 살펴보면 다음과 같습니다.
class MyAsyncTask(
val progressbar: ProgressBar,
val progressText: TextView
) : AsyncTask<Int, Int, Int>() {
var percent: Int = 0
override fun onPreExecute() {
percent = 0
progressbar.setProgress(percent)
}
override fun doInBackground(vararg params: Int?): Int {
while (isCancelled() == false) {
percent++
Log.d("async", "percent : " + percent)
if (percent > 100) {
break
} else {
publishProgress(percent)
}
try {
Thread.sleep(100)
} catch (e: Exception) {
e.printStackTrace()
}
}
return percent
}
override fun onProgressUpdate(vararg values: Int?) {
progressbar.setProgress(values[0] ?: 0)
progressText.setText("진행상황 : " + values[0])
super.onProgressUpdate(*values)
}
override fun onPostExecute(result: Int?) {
progressText.setText("작업 완료")
}
override fun onCancelled() {
progressbar.setProgress(0)
progressText.setText("작업 취소")
}
}
MyAsyncTask 클래스는 progressBar와 진행 상황을 텍스트로 보여주기 위해 progressText 변수를 받습니다. 이 클래스는 AsyncTask를 상속 받으며, < > 부분의 제너릭 타입은 다음의 3가지에 해당하는 타입을 적어줘야 합니다. 여기서는 모두 Int 타입입니다.
1. params -> doInBackgoround에서 사용할 타입
2. progress -> onProgressUpdate에서 사용할 타입
3. result -> onPostExecute에서 사용할 타입
퍼센트 변수에는 처음에 0을 넣습니다.
class MyAsyncTask(
val progressbar: ProgressBar,
val progressText: TextView
) : AsyncTask<Int, Int, Int>() {
var percent: Int = 0
..이하 생략..
}
AsyncTask를 상속 받았으니, 필요한 메소드를 오버라이딩 해줍니다.
onPreExecute()는 스레드가 실행되기 전의 상황인데, 이때는 퍼센트가 0인 상황입니다. 아직 시작되지 않았으니 말이죠.
override fun onPreExecute() {
percent = 0
progressbar.setProgress(percent)
}
doInBackground(vararg params: Int?)는 ProgressBar 스레드가 진행되는 부분입니다. params 값으로 Int 형임을 확인하시기 바랍니다. AsyncTask의 제너릭 타입으로 이미 선언한 바 있습니다.
while 문을 사용해서 '취소되지 않은 경우(isCancelled == false)' 증감 연산자를 사용해 퍼센트를 올려 줍니다.
퍼센트가 100이 넘으면 멈추도록 브레이크를 걸어주고 percent를 리턴해줍니다.
만약 '취소된 경우(isCancelled == true)', 즉 else 부분에서 바로 publishProgress(percent) 해줍니다. 이렇게 하면, percent의 값이 다음 메소드인 onProgressUpdate의 values로 들어가게 됩니다.
프로그레스 바의 진행상황을 좀 느리게 만들어, 상황을 확인해주기 위해, thread.sleep 사용합니다. 문제가 있을 경우를 대비해 try ~ catch 구문을 통해 printStackTrace를 사용해 에러를 트랙하도록 합니다.
override fun doInBackground(vararg params: Int?): Int {
while (isCancelled() == false) {
percent++
Log.d("async", "percent : " + percent)
if (percent > 100) {
break
} else {
publishProgress(percent)
}
try {
Thread.sleep(100)
} catch (e: Exception) {
e.printStackTrace()
}
}
return percent
}
onProgressUpdate(vararg values: Int?) 부분은 프로그레스 바가 진행되는 상황을 메인 스레드로 전달하는 부분입니다. varag는 여러 값을 가질 수 있도록 합니다. 만약 values 값으로 1이 들어오면, values[0] = 1을 보여줄 수 있도록, progressbar에 setProgress에 values[0] 넣어줍니다. 널값이면 0이 나오도록 엘비스 연산자를 사용합니다.
override fun onProgressUpdate(vararg values: Int?) {
progressbar.setProgress(values[0] ?: 0)
progressText.setText("진행상황 : " + values[0])
super.onProgressUpdate(*values)
}
onPostExecute 부분은 스레드가 실행된 이후이므로 setText로 "작업 완료"를 넣어줍니다. onCancelled는 취소된 상황을 말하며, 프로그레스 바는 0, 텍스트는 "작업 취소"를 보여주도록 합니다.
override fun onPostExecute(result: Int?) {
progressText.setText("작업 완료")
}
override fun onCancelled() {
progressbar.setProgress(0)
progressText.setText("작업 취소")
}
이제 MyAsyncTask를 실행시키고, 정지시킬 수 있도록, AsyncActivity 클래스의 onCreate 부분에 다음과 같이 코딩합니다. 버튼에 리스너를 달아주고, BackgroundAsyncTask 메소드에는 변수로서 progressbar와 텍스트뷰인 mytask 뷰를 넣어줍니다. 이 태스크를 실행시키려면 exexcute( ) 메소드를 사용하면 됩니다.
stop 버튼에 리스너를 다는 부분에서 태스크를 취소하려면, cancel( ) 메소드를 사용합니다. 메소드 안에는 true 값을 넣어줍니다.
class AsyncActivity : AppCompatActivity() {
var task: BackgroundAsyncTask? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_async)
start.setOnClickListener {
task = BackgroundAsyncTask(progressbar, mytask)
task?.execute()
}
stop.setOnClickListener {
task?.cancel(true)
}
}
//만약 현재 액티비티의 스레드를 멈추고, 다른 액티비티로 넘어가고자 하는 경우
override fun onPause() {
task?.cancel(true)
super.onPause()
}
}
비동기에 대한 설명은 여기서 마치겠습니다.
'Android App Coding' 카테고리의 다른 글
안드로이드 네트워크 2편 Network (0) | 2021.07.18 |
---|---|
안드로이드 네트워크 Network (0) | 2021.07.13 |
안드로이드 데이터베이스(DB) 2편 Realm (0) | 2021.07.10 |
안드로이드 데이터베이스(DB) Shared Preference (0) | 2021.07.10 |
탭레이아웃 2편 TabLayout (0) | 2021.07.09 |
- Total
- Today
- Yesterday
- 대한민국 미제사건
- 안드로이드 앱 만들기
- 메소드 오버라이딩
- 미제사건
- 선형 레이아웃
- 탭레이아웃
- 2021년 사건사고
- bmi 계산기 만들기
- RecyclerView
- 안드로이드 프로젝트
- 2019년 사건사고
- tabLayout
- 리사이클러뷰
- addView
- 안드로이드 스튜디오 에러
- 애드뷰
- findViewById
- lazy init
- Bmi Calculator
- 상대적 레이아웃
- 뷰 바인딩
- 리스트뷰
- 자바스크립트 배열
- ToDo List 앱 만들기
- notifyDataSetChanged
- view binding
- android adapter
- 안드로이드 어댑터
- 2007년 사건사고
- 인텐트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |