Android에서 비동기 처리를 위해 많이 사용되던 AsyncTask가 deprecated 되었는데, 프로젝트에 아직도 사용되는 부분들이 존재하여 Coroutine을 활용하여 비동기 처리하도록 수정을 진행했다.
public class CheckApiTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
//Task 실행 전 UI 작업
}
@Override
protected Boolean doInBackground(String[] params) {
try {
//Background 작업
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
//Main UI 작업
}
AsyncTask가 deprecated 된 이유는 다음과 같다.
1. 순차 실행 및 예외 처리 기능성 부족으로 인한 유연성 저하
2. 실행 순서의 보장 불가
3. 메모리 누수 가능성
이제 빈자리를 메꿔 줄 Coroutine은 비동기 프로그래밍을 위한 Kotlin 특징 중 하나로서, 여러 개의 비동기 작업을 순차 또는 병렬로 실행 가능하다. 또한 코드 가독성 유지 보수성도 향상되는 측면을 가지고 있다.
장점을 몇 가지 더 정리해 보자면..
1. 가독성과 간결성
2. 비동기 코드의 순차적 구조 확립 가능
3. 자원 효율성 (경량 스레드 방식 동작)
4. 취소 및 예외 처리 기능 지원
5. 다양한 스케쥴러를 활용한 할당 기능
Coroutine을 활용한 새로운 AsyncTask는 아래와 같이 구성했다.
abstract class AsyncTaskBase<T> {
abstract suspend fun execute() : T
fun executeAsync(onResult : (T) -> Unit, onFailed: (Throwable) -> Unit = {}) {
CoroutineScope(Dispatchers.IO).launch {
try {
val result = execute()
withContext(Dispatchers.Main) {
onResult(result)
}
} catch (e: Throwable) {
withContext(Dispatchers.Main) {
onFailed(e)
}
}
}
}
}
추상 클래스를 활용하여 실제 doInBackground()의 역할을 suspend function인 execute() 내부에서 구현하도록 처리했다.
그리고 반환 타입을 제네릭으로 명시하여 Task 마다 각기 다른 타입으로 반환될 수 있도록 했다.
executeAsync 함수는 비동기 처리를 시작하는 주요 함수로서 onResult와 onFailed라는 두 개의 callback을 받고 있다.
먼저 IO Dispatcher Coroutine으로 크게 묶여있고, 내부에서 Main Dispatcher Coroutine이 사용되는데,
Coroutine Dispatcher는 Coroutine이 실행될 스레드 풀을 지정하는 역할을 한다.
이것에 따라 네트워크 작업이 가능한지, 대용량 처리 작업이 가능한지, UI 작업이 가능한지 구분된다.
1. Main : UI와 상호작용 시에 사용한다.
2. IO : 네트워크 상호작용 및 파일 입출력과 같은 I/O 작업에 사용된다.
3. Default : CPU 밀집적인 작업에 사용된다.
4. Unconfined : 현재 스레드에서 즉시 실행된다. 혼동하지 않도록 유의해야 한다.
5. newSingeThreadContext : 새로운 스레드를 생성하여 실행한다. 특정 작업을 위해 새로운 스레드를 할당해야 할 때 사용한다. 사용이 완료 이후에는 반드시 close() 호출하여 자원을 해제하도록 한다.
val myThread = newSingleThreadContext("MyThread")
GlobalScope.launch(myThread) {
// 새로운 스레드에서 실행
}
// 사용 후에는 반드시 close 호출
myThread.close()
이에 따라 execute() 내부에서는 DB 작업 또는 네트워크 통신 작업을 수행하고, 그 결과 값을 제네릭으로 반환하여 특별한 예외가 발생하지 않는다면 onResult를 통해 Main thread로 결과 값을 반환하게 되고, 중간에 예외가 발생했다면 해당 예외를 기반으로 로그를 남기거나 오류 Toast를 팝업 할 수 있도록 Main thread로 Throwable을 반환하게 된다.
class TestTask: AsyncTaskBase<Boolean>() {
override suspend fun execute(): Boolean {
val isSuccessFirst = TaskFirst()
if (!isSuccessFirst) return false
val isSuccessSecond = TaskSecond()
if (!isSuccessSecond) return false
return LastTask()
}
}
위와 같이 execute()를 구현하고,
TestTask().executeAsync({
//TODO: 성공 시
}, {
//TODO: 실패 시
})
Activity 등에서는 위와 같이 호출하여 사용할 수 있다.
Android에서 비동기 처리를 위해 많이 사용되던 AsyncTask가 deprecated 되었는데, 프로젝트에 아직도 사용되는 부분들이 존재하여 Coroutine을 활용하여 비동기 처리하도록 수정을 진행했다.
public class CheckApiTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
//Task 실행 전 UI 작업
}
@Override
protected Boolean doInBackground(String[] params) {
try {
//Background 작업
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
//Main UI 작업
}
AsyncTask가 deprecated 된 이유는 다음과 같다.
1. 순차 실행 및 예외 처리 기능성 부족으로 인한 유연성 저하
2. 실행 순서의 보장 불가
3. 메모리 누수 가능성
이제 빈자리를 메꿔 줄 Coroutine은 비동기 프로그래밍을 위한 Kotlin 특징 중 하나로서, 여러 개의 비동기 작업을 순차 또는 병렬로 실행 가능하다. 또한 코드 가독성 유지 보수성도 향상되는 측면을 가지고 있다.
장점을 몇 가지 더 정리해 보자면..
1. 가독성과 간결성
2. 비동기 코드의 순차적 구조 확립 가능
3. 자원 효율성 (경량 스레드 방식 동작)
4. 취소 및 예외 처리 기능 지원
5. 다양한 스케쥴러를 활용한 할당 기능
Coroutine을 활용한 새로운 AsyncTask는 아래와 같이 구성했다.
abstract class AsyncTaskBase<T> {
abstract suspend fun execute() : T
fun executeAsync(onResult : (T) -> Unit, onFailed: (Throwable) -> Unit = {}) {
CoroutineScope(Dispatchers.IO).launch {
try {
val result = execute()
withContext(Dispatchers.Main) {
onResult(result)
}
} catch (e: Throwable) {
withContext(Dispatchers.Main) {
onFailed(e)
}
}
}
}
}
추상 클래스를 활용하여 실제 doInBackground()의 역할을 suspend function인 execute() 내부에서 구현하도록 처리했다.
그리고 반환 타입을 제네릭으로 명시하여 Task 마다 각기 다른 타입으로 반환될 수 있도록 했다.
executeAsync 함수는 비동기 처리를 시작하는 주요 함수로서 onResult와 onFailed라는 두 개의 callback을 받고 있다.
먼저 IO Dispatcher Coroutine으로 크게 묶여있고, 내부에서 Main Dispatcher Coroutine이 사용되는데,
Coroutine Dispatcher는 Coroutine이 실행될 스레드 풀을 지정하는 역할을 한다.
이것에 따라 네트워크 작업이 가능한지, 대용량 처리 작업이 가능한지, UI 작업이 가능한지 구분된다.
1. Main : UI와 상호작용 시에 사용한다.
2. IO : 네트워크 상호작용 및 파일 입출력과 같은 I/O 작업에 사용된다.
3. Default : CPU 밀집적인 작업에 사용된다.
4. Unconfined : 현재 스레드에서 즉시 실행된다. 혼동하지 않도록 유의해야 한다.
5. newSingeThreadContext : 새로운 스레드를 생성하여 실행한다. 특정 작업을 위해 새로운 스레드를 할당해야 할 때 사용한다. 사용이 완료 이후에는 반드시 close() 호출하여 자원을 해제하도록 한다.
val myThread = newSingleThreadContext("MyThread")
GlobalScope.launch(myThread) {
// 새로운 스레드에서 실행
}
// 사용 후에는 반드시 close 호출
myThread.close()
이에 따라 execute() 내부에서는 DB 작업 또는 네트워크 통신 작업을 수행하고, 그 결과 값을 제네릭으로 반환하여 특별한 예외가 발생하지 않는다면 onResult를 통해 Main thread로 결과 값을 반환하게 되고, 중간에 예외가 발생했다면 해당 예외를 기반으로 로그를 남기거나 오류 Toast를 팝업 할 수 있도록 Main thread로 Throwable을 반환하게 된다.
class TestTask: AsyncTaskBase<Boolean>() {
override suspend fun execute(): Boolean {
val isSuccessFirst = TaskFirst()
if (!isSuccessFirst) return false
val isSuccessSecond = TaskSecond()
if (!isSuccessSecond) return false
return LastTask()
}
}
위와 같이 execute()를 구현하고,
TestTask().executeAsync({
//TODO: 성공 시
}, {
//TODO: 실패 시
})
Activity 등에서는 위와 같이 호출하여 사용할 수 있다.