안드로이드 앱 개발 프로젝트1

12일차 - 스레드 공부 노트2

원준킹 2021. 1. 22. 18:01
  1. 스레드와 스레드 통신

파일 복사 스레드의 진행 상태를 메인 스레드로 메시지형태로 전달하고, 메인 스레드에서는 그 값을 사용하여 현재 진행 상태를 화면에 표시하는 과정

 

  1. 안드로이드 스레드 통신. 핸들러
  • 핸들러 : 안드로이드에서 사용할 수 있는 스레드 통신 방법 중 가장 일반적인 핸들러

 

4.1 메시지

  • 메시지 : 전달할 데이터를 한 곳에 저장하는 역할을 하는 클래스
  • 메시지 -> 핸들러 -> 메시지 큐

4.2 메시지 큐

  • 메시지 큐 : 메시지 객체를 큐 형태로 관리하는 자료 구조, 앱의 메인스레드에서 기본적으로 사용되고 있음
  • 메시지 전달은 핸들러를 통해서, 메시지를 꺼내고 처리하는 역할은 루퍼가 수행

4.3 루퍼

  • 루퍼 : 메시지 큐로부터 메시지를 꺼내와 해당 메시지와 연결된 핸들러를 호출하는 역할 담당
  • 메시지 처리를 위한 메시지 루프 실행

4.4 핸들러

  • 핸들러 : 스레드의 루퍼와 연결된 메시지 큐로 메시지를 보내고 처리할 수 있게 함
  • 핸들러 클래스 인스터스 생성시 자동으로 해당 스레드와 메시지 큐에 연결됨
  1. 안드로이드 핸들러 사용 방법

5.1 메시지 수신 스레드

  • Handler 객체 생성 및 handleMessage() 메서드 오버라이드
     가장 먼저 할 일은 메인 스레드에서 수신 메시지를 처리하기 위해 핸들러 객체를 생성하는 것
     그 다음 수신된 메시지를 처리하기 위해 handleMessage 메서드를 오버라이드

5.2 메시지 송신 스레드

  • 수신 스레드의 핸들러 객체 참조를 통해 메시지 객체 획득
     메인 스레드인 경우 액티비티의 클래스 변수로 핸들러 객체를 선언하고 액티비티 참조를 통해 핸들러 객체 참조
     액티비티 내에서 스레드를 생성했으면 핸들러 객체 바로 참조

5.3 핸들러 관련 참고 사항

  • 스레드 통신의 대상은 자기 자신도 포함
     외부 스레드에서 전달되는 메시지 처리를 위해 구현한 기능을 그대로 사용하는 상황
     순차적으로 실행되어야 하는 코드 사이에 시스템 이벤트가 고려되어야 하는 상황
  • 핸들러는 하나의 스레드에 여러 개 만들 수 있음
     오히려 하나의 핸들러에서 모든 메시지를 처리하는 것 보다 메시지 종류 및 기능에 따라 여러 개 핸들러로 나누어 처리하는게 더 낫다
  1. 핸들러 사용 목적과 Runnable
  • 메시지 객체 사용시에 번거로운 절차
     메시지 데이터 종류마다 다른 상수값 정의, 그에 맞는 조건문 작성
  • 핸들러 사용의 주 목적이 대상 스레드의 코드를 실행하는 것이면 그냥 실행 코드를 보내면 되지 않을 까
  • Runnable 객체 : 실행 코드가 담긴 객체

6.1 핸들러로 Runnable 객체 보내기

6.2 Runnable 인터페이스

  • Runnable은 새로운 스레드에서 실행될 run()메서드를 가지는 인터페이스
  • 새로운 스레드를 실행하는 역할은 Thread 클래스의 역할임

 

  1. 핸들러와 Runnable을 사용한 스레드 통신 예제

public class MainActivity extends AppCompatActivity {

TextView clockTextView ;
private static Handler mHandler ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ... 코드 계속.

    mHandler = new Handler() ;

    // 핸들러로 전달할 runnable 객체. 수신 스레드 실행.
    final Runnable runnable = new Runnable() {
        @Override
        public void run() {
            Calendar cal = Calendar.getInstance();

            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
            String strTime = sdf.format(cal.getTime());

            clockTextView = findViewById(R.id.clock);
            clockTextView.setText(strTime);
        }
    } ;

    // 새로운 스레드 실행 코드. 1초 단위로 현재 시각 표시 요청.
    class NewRunnable implements Runnable {
        @Override
        public void run() {
            while (true) {

                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace() ;
                }

                mHandler.post(runnable) ;
            }
        }
    }

    NewRunnable nr = new NewRunnable() ;
    Thread t = new Thread(nr) ;
    t.start() ;
}

}

  1. runOnUiThread()
  • 핸들러를 통한 스레드 통신이 가장 많이 사용되는 경우는 새로운 스레드에서 실행된 결과를 메인 스레드를 통해 화면에 표시하고자 할 때

  • 이전에 다뤘던 Runnable 객체를 보내는 과정
     수신 측 스레드에서 핸들러 객체 생성
     송신 측 스레드에서 Runnable 객체 생성 및 run() 메서드 구현
     Handler.post() 메서드를 통해 Runnable 객체 전달

  • runOnUiThread() : 액티비티에서 Runnable 수신하는 경우 핸들러 객체를 매번 새로 만들지 않고 액티비티가 기본적으로 가지고 있는 핸들러 사용하게 하는 메서드

  • Runnable 객체를 보낸 스레드가 메인 스레드일 경우 그냥 run() 메서드를 바로 실행

  • 아니면 Handler.post() 로 Runnable 객체 전달

public class MainActivity extends AppCompatActivity {


TextView clockTextView ;


@Override

protected void onCreate(Bundle savedInstanceState) {

// ... 코드 계속.


// 핸들러로 전달할 runnable 객체. 수신 스레드 실행.

final Runnable runnable = new Runnable() {

@Override

public void run() {

Calendar cal = Calendar.getInstance();


SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");

String strTime = sdf.format(cal.getTime());


clockTextView = findViewById(R.id.clock);

clockTextView.setText(strTime);

}

} ;


// 새로운 스레드 실행 코드. 1초 단위로 현재 시각 표시 요청.

class NewRunnable implements Runnable {

@Override

public void run() {

while (true) {


try {

Thread.sleep(1000);

} catch (Exception e) {

e.printStackTrace() ;

}


// 메인 스레드에 runnable 전달.

runOnUiThread(runnable) ;

}

}

}


NewRunnable nr = new NewRunnable() ;

Thread t = new Thread(nr) ;

t.start() ;

}

  • 별도의 Handler 선언과 post() 메소드 수행 작업이 사라짐

 

  1. 안드로이드 AsyncTask

9.1 동기 실행 VS 비동기 실행

  • 동기 실행 : 프로그램 실행 중 하나의 기능 또는 함수를 실행할 때 실행한 측(Caller)에서 기능이나 함수의 모든 동작이 완료될 때까지 대기하는 방식
  • 비동기 실행 : 기능 또는 함수의 동작이 끝나길 기다리지 않고 바로 그 다음 코드를 실행하는 방식

 사용 사례 : 크기 큰 파일 복사, 데이터베이스 쿼리, HTTP 서버 API 요청, FTP 파일 다운로드 등
 단발성 실행
 스레드 실행 중간 상태 또는 최종 결과를 메인 스레드로 전달

 

  • 비동기 실행의 정형화된 패턴
     execute -> doInBackground -> onProgressUpdate -> onPostExecute
     실행(execute) : 비동기 작업 준비 및 시작
     백그라운드 작업(doInBackground) : 백그라운드 스레드에서 비동기 작업 실행
     진행 상황 업데이트(doProgressUpdate) : 백그라운드 스레드 진행 상황을 메인스레드로 전달
     비동기 실행 완료 후 처리(onPostExecute) : 백그라운드 스레드 완료 후 메인스레드에 완료 상태 전달

9.2 AsyncTask

  • 비동기적으로 실행될 필요가 있는 작업을 위해 사용하는 클래스
     추상 클래스 : abstract class AsyncTask
     제네릭 타입 : AsyncTask<Params, Progress, Result>
     가변 인자 : (Params …), (Progress …)
     실행 단계 : onPreExecute, doInBackground, onProgressUpdate, onPostExecute

9.3 추상 클래스 (abstrack class AsyncTask)

  • AsyncTask는 추상 클래스 == AsyncTask를 사용하기 위해서는 반드시 AsyncTask를 상속한 클래스를 생성해야 한다

9.4 제네릭 타입(AsyncTask<Params, Progress, Result>)

  • Params : AsyncTask 실행에 필요한 파라미터

  • Progress : 현재 작업 진행 정보르 나타내는 상태 값

  • Result : 작업의 실행이 완료된 후의 최종 결과

  • 만약 두 가지 이상의 인자를 전달하고 싶은데 Progress 인자가 하나 뿐인데 어떻게 할까 -> 가변 인자

9.5 가변 인자 (Varargs)

  • 파라미터 타입에 … 을 추가하여 메서드에 전달되는 파라미터가 가변 인자라는 것을 명시 가능
public void Func(String... vals) {
if (vals.length == 1) System.out.println(vals[0]) ;
else if (vals.length == 2) System.out.println(vals[1]) ;
else if (vals.length == 3) System.out.println(vals[2]) ;
}

9.6 실행 단계

  • 작업 시작 onPreExecute -> 작업 수행 doInBackground -> 상태 갱신 onProgressUpdate -> 결과 확인 onPostExecute

 

 

  • onPreExecute() : 작업이 실행되기 직전 UI 스레드에 의해 호출됨, 비동기 실행 작업에 대한 초기화 과정을 수행
  • doInBackground() : onPreExecute() 호출 뒤 바로 백그라운드 스레드에 호출됨, 실질적 작업 실행 코드가 작성되는 메서드
  • onProgressUpdate() : doInBackground()에서 publishProgress(Pregress…)를 호출하면 UI스레드에서 호출되는 메서드, 작업 진행 상태 화면 갱신 역할
  • onPostExecute(Result) : 백그라운드 스레드의 모든 실행이 완료되면 UI 스레드에서 호출되는 메서드, Result 는 doInBackground()에서 리턴되는 값

9.7 AsyncTask 예제

  1. AsyncTask Deprecated (????)
  • 공부 마치자마자 발견한 deprecated 소식

  • API 30부터deprecated
    https://developer.android.com/reference/android/os/AsyncTask

  • UI 스레드의 올바르고 쉬운 사용을 위해 고안되었음

  • context leak, missed callbacks, crashes on configuration changes, incosistent behavior on different versions of the platform, swallows exceptions from doInBackground 와 같은 문제점으로 deprecated

  • AsyncTask 는 Thread와 Handler의 사용을 도울 뿐 스레드 프레임워크를 구성하는 클래스가 아니다

  • AsyncTask는 짧은 시간의 동작(몇 초 정도)에서만 사용하는 것이 이상적이며 긴 시간동안 스레드를 유지하는 작업에는 java.util.concurrent package 에서 제공하는 Executor, ThreadPoolExecutor, FutureTask 와 같은 클래스를 사용 권장

  • 그냥 지금 당장은 AsyncTask 쓰고, 나중에 RxJAVA로 이전해보자.

www.androidpub.com/devfree/2072034

 

Android: Deprecated 에 대한 고찰... - 게시판 - 안드로이드 개발자 모임 게시판

요새OS가 업그레이드가 되면서 구글사마가 이전버전에 지원하던 API 를 deprecated 라는 이름을 걸고사용하지말것을 당부하는데요사실 deprecated 되는 API중에 정말로 쓸모 없는것은 deprecated되는게 맞

www.androidpub.com

 

medium.com/@prixe87/asynctask-deprecated-%EB%8C%80%EB%B9%84%ED%95%98%EA%B8%B0-392f3be5a712

 

AsyncTask Deprecated 대비하기

AsyncTask was intended to enable proper and easy use of the UI thread. However, the most common use case was for integrating into UI, and…

medium.com