본문 바로가기
Kotlin/Kotlin 문법

Android AsyncTask의 구조와 제작 사항

by MonoSoft 2020. 10. 13.
728x90
반응형

 

 

 

package com.monosoft.asynctasksample

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.*
import java.lang.Exception
import java.net.URL

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

//AsyncTask?
//쓰레드와 핸들러를 하나로 합쳐서 사용하기 쉽게 만들어놓은 클래스

buttonDownload.setOnClickListener {
downloadAndSetImage()
}
}

fun downloadAndSetImage() {
var text =""
var str = editUrl.text.toString()

if(!str.startsWith("http")) {
text = "http://$str"
}
val url = text

Log.d("텍스트","${text}")

val asyncTask = object : AsyncTask<String,Void,Bitmap?>() {
override fun doInBackground(vararg p0: String?): Bitmap? {
//백그라운드에서 실행되는 영역
val urlString = p0[0] ?: ""
var bitmap:Bitmap? = null
try {

Log.d("텍스트","${url}")
val url = URL(url)
Log.d("텍스트1","${url}")
val stream = url.openStream() //일종의 파이프식으로 연결
Log.d("텍스트2","${url}")
val bitmap = BitmapFactory.decodeStream(stream)
} catch(e:Exception){
Log.e("에러","${e.localizedMessage}")
}


return bitmap
}

override fun onProgressUpdate(vararg values: Void?) {
//백그라운드에서 특정함수를 호출하면 이 함수가 호출됨
//메인스레드 / 포어그라운드 영역

}

override fun onPostExecute(result: Bitmap?) {
result?.let {bitmap ->
imagePreView.setImageBitmap(bitmap)
}
}
}
Log.d("텍스트","${url}")
asyncTask.execute(url) //doInBackground 넘김
//ex) asyncTask.execute(url,url,url,url,url) //doInBackground 넘김
}
}

 

 

--------------------------------------------------------추가 설명

 

서론

안드로이드에서 UI를 조작할 수 있는 방법에 대해서 두 가지를 배웠습니다.

  1.  Handler와 Looper 사용하기
  2.  runOnUiThread( ) 사용하기

 두 가지 사용법을 자세히 읽어보시면 어렵지 않게 따라할 수 있겠지만 이번에는 안드로이드에서 스레드나 메시지 루프 등의 원리를 이해하지 않아도 하나의 클래스에서 UI 작업을 쉽게 할 수 있게 해주는 AsyncTask 클래스에 대해서 배워보도록 하겠습니다. 이해하시면 너무 간단하고 쓰기 편하기 때문에 이것만 사용하겠다 하실 수도 있지만 저처럼 큰 코 다칠 수 있는 AsyncTask의 단점도 알려드리도록 하겠습니다.

 

 

AsyncTask 의 개념

 

 

 

AsyncTaskd의 동작 순서를 먼저 설명하고, 개념을 말씀드리도록 하겠습니다.

 

  1.  execute( ) 명령어를 통해 AsyncTask을 실행합니다.

  2.  AsyncTask로 백그라운드 작업을 실행하기 전에 onPreExcuted( )실행됩니다. 이 부분에는 이미지 로딩 작업이라면 로딩 중 이미지를 띄워 놓기 등, 스레드 작업 이전에 수행할 동작을 구현합니다. 

  3.  새로 만든 스레드에서 백그라운드 작업을 수행합니다. execute( ) 메소드를 호출할 때 사용된 파라미터를  전달 받습니다.

  4.  doInBackground( ) 에서 중간 중간 진행 상태를 UI에 업데이트 하도록 하려면 publishProgress( ) 메소드를 호출 합니다.

  5.  onProgressUpdate( ) 메소드는 publishProgress( )가 호출 될 때  마다 자동으로 호출됩니다.

  6.  doInBackground( ) 메소드에서 작업이 끝나면 onPostExcuted( ) 로 결과 파라미터를 리턴하면서 그 리턴값을 통해 스레드 작업이 끝났을 때의 동작을 구현합니다. 

 

여기서 핵심은 onPreExecute( ), onProgressUpadate( ), onPostExecute( ) 메소드는 메인 스레드에서 실행되므로 UI 객체에 자유롭게 접근할 수 있다는 것입니다.

 

 

 

 

 

필자가 AsyncTask 를 처음 봤을 때 헷갈렸던 부분은 바로 AsyncTask Generic 타입이었습니다. AsyncTask <Params, Progress, Result> 에서 인자1, 인자2, 인자3에 들어갈 값이 무엇이고 어디에 쓰인다는 것인지 알 수 없었던 것입니다.

 

  • Params : doInBackground 파라미터 타입이 되며, execute 메소드 인자 값이 됩니다.

  • Progress : doInBackground 작업 시 진행 단위의 타입으로 onProgressUpdate 파라미터 타입입니다.

  • Result : doInBackground 리턴값으로 onPostExecute 파라미터 타입입니다.

 

AsyncTask 의 사용법

사용법은 아래 코드를 보면 쉽게 파악할 수 있을 것이라고 생각합니다.

 

public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 버튼을 클릭하면 파일 다운로드 경로를 파라미터로 AsyncTask 실행 public void OnClick(View view) { switch (view.getId()) { case R.id.button: try { new DownloadFilesTask().execute(new URL("파일 다운로드 경로1")); } catch (MalformedURLException e) { e.printStackTrace(); } break; } } private class DownloadFilesTask extends AsyncTask { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Long doInBackground(URL... urls) { // 전달된 URL 사용 작업 return total; } @Override protected void onProgressUpdate(Integer... progress) { // 파일 다운로드 퍼센티지 표시 작업 } @Override protected void onPostExecute(Long result) { // doInBackground 에서 받아온 total 값 사용 장소 } } }

 

 

AsyncTask 의 제약조건 및 단점

 AsyncTask는 비교적 오래 걸리지 않은 작업에 유용하고, Task 캔슬이 용이하며 로직과 UI 조작이 동시에 일어나야 할 때 매우 유용하게 사용되지만 다음의 제약조건과 단점을 알고 사용해야합니다.

 

[제약조건]

  1.  API16(젤리빈) 미만 버전에서는 AsyncTask 선언을 UI Thread에서 해주지 않으면 오류가 발생한다. (API 16 이상(JELLY BEAN)의 버전에서는 자유롭게 사용해도 된다고합니다.)
  2.  excutes(Params)는 UI 스레드에서 직접호출해야합니다
  3.  수동으로 onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) 호출하면 안됩니다.
  4.  Task는 오직 한번만 실행될 수 있습니다.

 

[단점]

  1.  하나의 객체이므로 재사용이 불가능합니다. (객체를 새롭게 생성하면 되지만 메모리 효율 나빠짐)
  2.  구현한 액티비티 종료 시 별도의 지시가 없다면 종료되지 않습니다. 
  3.  Activity 종료 후 재시작 시 AsyncTask의 Reference는 invalid 해지며 onPostExecute( ) 메소드는 새로운 Activit에 어떠한 영향도 끼치지 못합니다.
  4.  AsyncTask의 기본 처리 작업 개수는 1개입니다. Async 하지 않은 AsyncTask의 변화를 아래에 적어 놓겠습니다.

 

 

Order of execution

When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.

If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) withTHREAD_POOL_EXECUTOR.

 

해석해보자면

 

맨 처음 소개될 때, AsyncTask 는 순차적으로 하나의 스레드에서 실행되었으나, 도넛의 시작과 함께 병렬 처리 가능한 스레드 풀로 바뀌었다. 허니콤의 시작과 함께 병렬 처리의 에러를 피하기 위해 다시 싱글 스레드로 실행되게 되었다. 만약 당신이 병렬 처리를 원한다면 myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 처럼 사용할 수 있다.

 

 

 

참고사이트

  1.  https://gist.github.com/benelog/5954649
  2.  http://corej21.tistory.com/38



출처: https://itmining.tistory.com/7 [IT 마이닝]

728x90
반응형

댓글