안드로이드/정리

[Android] Thread에 관해...

쓰레드란?

=> 프로세스 내에서 순차적으로 실행되는 실행 흐름의 최소 단위이다.

 

프로세스?

=> 운영체제에 의해 메모리에 적재되어 실행 중인 프로그램.

=> 스레드로 구성

=> 모든 프로세스엔 1개 이상의 스레드가 존재하여 작업 수행.

=> 하나의 프로세스 내에 2개 이상의 스레드가 동작하도록 하는 것을 멀티스레드 프로그래밍

 

멀티스레드

=> 하나의 프로세스 내 둘 이상의 쓰레드가 동시에 작업 수행하는 것

=> 각 쓰레드가 자신이 속한 프로세스의 메모리 공유

=> CPU의 코어 수보다 더 많은 쓰레드가 실행되면, 각 코어가 정해진 시간 동안 여러 작업 번갈아가며 수행

=> 각 쓰레드가 서로 교체될 때 쓰레드 간의 문맥 교환(context switching)이 발생

=> Context Switching은 현 작업 상태나 다음 작업에 필요한 각종 데이터를 저장하고 읽어오는 작업

 

메인쓰레드

=> 쓰레드는 프로세스 실행 중 언제든 필요에 따라 만들어지고 실행된다. (기존에 이미 실행 중인 쓰레드에 의해 수행될 수 있음)

=> 프로세스가 시작될 때 최초의 실행 시작점부터 순차적으로 진행되는 실행 흐름을 메인 쓰레드.

=> 새로운 쓰레드가 오직 메인 쓰레드에 의해서만 실행될 수 있는 것은 아님.

 

Android UI Thread

=> 안드로이드 메인 쓰레드가 UI Thread

=> 화면에 그려지는 UI 관련 작업들은 UI Thread에서만 실행되어야 함

=> 메인 쓰레드는 메시지 큐 수신을 대기하는 Looper를 가지며, 사용자 입력과 시스템 이벤트, 화면 그리기 등의 메시지가 수신되면 각 메시지에 매핑된 핸들러의 메서드 실행

=> 메인이 아닌 Thread에서 UI관련 작업을 수행하려면, Handler를 통해 Main Thread로 메시지를 전송하고 해당 작업을 메인 쓰레드에서 처리해야 함

  • 일반 쓰레드는 루퍼를 갖지 않고,
    HandlerThread를 생성하면 Looper를 갖는 Thread 생성 가능
  • Thread는 Thread 외부에서 전달된 message등이 인입되는 Queue를 가진다.
  • Looper는 Queue에 message 혹은 Runnable 객체가 있다면 Handler로 전달한다.
  • Handler는 Looper가 보낸 message 혹은 Runnable(쓰레드를 전달) 객체를 처리한다.

Handler

=> 안드로이드 OS에 메시지를 던질 수 있는 객체

=> 핸들러를 통해 쓰레드 간 메시지를 주고 받을 수 있음

 

runOnUiThread

=> 수행하고자 하는 코드를 Main Thread가 처리하도록 하는 메서드

=> Handler와 마찬가지로 별도 생성한 Thread에서 UI 작업이 필요한 경우 사용

=> 개발자가 만든 Runnable 객체를 메인 스레드에서 실행되도록 만드는 메서드

=> 내부동작은, 현 스레드가 메인 스레드인지 검사해

메인 스레드가 아니면 post()

메인 스레드면 Runnable의 run() 실행

=> Runnable 객체에 구현된 코드를 반드시 메인 스레드에서 실행해야 할 때 사용하는 메서드

 

AsyncTask

=> 비동기작업 수행 위해 사용

=> 크기가 큰 파일 복사, DB 쿼리, HTTP 서버 API 요청, FTP 파일 다운롣 ㅡ등 비동기 작업 필요한 경우 사용

=> Thread, Handler, Message, Runnable 등 직접 다루지 않아도 메인 쓰레드와 별개로 비동기 실행이 필요한 작업에 사용

=> AsyncTask는 API Level 30에서 deprecated

 

AsyncTask 총 4단계

  • 실행(execute) : 비동기작업 준비 & 시작
  • 백그라운드 작업(doInBackground) : 백그라운드 쓰레드에서 비동기 작업 실행
  • 진행 상황 업데이트(onProgressUpdate) : 백그라운드 쓰레드 진행 상황을 메인 쓰레드로 전달
  • 비동기 실행 완료 후 처리(onPOstExecute) : 백그라운드 쓰레드 완료 후 메인 쓰레드에 완료 상태 전달0

 

쓰레드 오류상황

inner class ThreadClass : Thread() {
        override fun run() {
            while(isRunning) {
                sleep(100)
                Log.d(TAG, System.currentTimeMillis().toString())
                binding.helloTextview.text = System.currentTimeMillis().toString()
            }
        }
    }

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

뷰 계층을 생성한 원래 쓰레드만이 해당 뷰를 조작할 수 있다.

메인 쓰레드가 아닌 다른 쓰레드에서 화면을 그리는 기능이 실행되었기 때문에 발생한 에러

 

해결방법

UI Thread와 Handler를 사용.

inner class ThreadClass : Thread() {
        override fun run() {
            val handler = Handler(Looper.getMainLooper())
            while(isRunning) {
                sleep(100)
                handler.post{
                    binding.helloTextview.text = System.currentTimeMillis().toString()
                }
            }
        }
    }

핸들러를 사용 (메인에게 요청.)