안드로이드 앱 개발 프로젝트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

 

 

'안드로이드 앱 개발 프로젝트1' 카테고리의 다른 글

12일차 - AsyncTask가 맞나?  (0) 2021.01.22
11일차 - 스레드 공부 노트 1  (0) 2021.01.21
10일차  (0) 2021.01.20
9일차 - 데이터 삽입 알고리즘 최적화  (0) 2021.01.19
8일차  (0) 2021.01.18