요즘 일하면서 스레드를 정말 많이 사용하는데, 평소에 잘 사용한다고 생각하고 있었는데 Rx에서 제공하는 스레드(AndroidSchedulers.MainThread, Computation.Io 등등 너무 편해요..)를 사용하다보니 예전 기억이 긴가민가해 이번을 기회로 다시 정리 할까 한다.
기본적으로 안드로이드에서의 프로세스와 스레드 개념은 리눅스, 윈도우 등과 같은 운영체제에 배운 것과 크게 차이점이 없다.
하지만 단순히 프로세스와 스레드뿐만 아니라 안드로이드 컴퍼넌트와의 관계까지 함께 이해해야 앱이 어떤식으로 동작하는지 정확히 이해 할 수 있다.
예전에 케이티 하이텔 면접에서 이런 질문을 받았던 것을 기억한다.
혹시 쓰레드와 프로세스의 차이점에 대해 아냐고-
그래서 나는 프로세스는 하나의 쓰레드 또는 여러개의 쓰레드를 가질 수 있으며 하나의 프로세스 안에 존재하는 쓰레드들은 메모리를 공유(shared memory) 할 수 있으며, 다른 프로세스끼리 통신 하기 위해선 RPC(remote procedure call) 방식을 이용해서 메모리를 공유 할 수 있다 정도로 말했던거 같은데 사실인지는 모르겠지만 잘 넘어 갔던 기억이 난다.
운영체제 책에서는 프로세스를 실행 중인 프로그램 // 스레드를 실행 중인 프로그램 내에서 동작하는 작업의 단위라 설명하고 있으니 언뜻 보면 내가 대답했던 답과 어느정도 일맥 상통 하는 것 같다.
우선은 프로세스에 대해 알아보겠다.
사진을 보며 이해하도록 해보자.
A앱, B앱이 설치되고 실행이 되면, 각각 하나의 프로세스가 생기게 되고 프로세스 내에는 하나의 메인 쓰레드(우리는 Ui Thread라고도 한다)가 할당된다. 액티비티, 서비스와 같은 컴포넌트들이 메인 스레드 내에서 동작을 하며, 메인 스레드에서 동작하는 컴포넌트들은 스레드를 만들어 작업을 진행 할 수도 있다. 사진의 B 앱에서는 Woker Thread 하나를 만들어서 어떤 동작을 하고 있다. A 앱과 B앱이 가진 MainThread는 각각의 다른 User id , Process id를 가지고 있다. 앱을 삭제하고 다시 설치하면 UID, PID가 바뀔수도 있다.
또한 동일한 스레드를 사용할 수 있도록AndroidManifest.xml에서 정적으로 코딩 해 줄수 있는데, 이 부분에 대해 궁금하다면 android:process 쪽으로 구글링 하면 될것이다.
액티비티에서도 생명주기가 있던거 처럼 안드로이드 프로세스에도 생명주기가 존재한다. 프로세스 생명주기는 안드로이드가 제한적인 메모리를 가졌기 때문에, 메모리를 효율적으로 사용하기 위해 프로세스의 중요도 별로 구분시켜 놓은 것을 말한다.
즉, 안드로이드 기기에서 메모리가 부족하면 새로운 앱을 실행시키지 못하기 때문에 오랫동안 사용하지 않은 프로세스나 중요하지 않은 프로세스를 시스템에서 강제 종료하여 메모리를 확보한 후에 새로운 프로세스를 실행 시키는 것이다. 이때 프로세스 라이클을 참조하여 우선 순위를 정하고 메모리를 확보를 위해 프로세스를 종료하게 된다.
안드로이드 프로세스 라이플 사이클에는 어떤 것들이 있을까?
1. Foreground Process
* 프로세스 내에 동작 중인 액티비티가 Resume상태로 사용자가 앱과 상호작용하는 상태.
* 프로세스 내의 서비스가 Foreground Service로 동작 중인 상태(status bar).
* 프로세스 내의 리시버에서 onReceive 가 동작 중인 상태 (2번과 3번에 대한 내용은 안드로이드 컴포넌트 중에 서비스에 대한 내용으로 서비스도 생명주기를 가진다. 나중에 꼭 포스팅 하려 한다)
2. Visible Process
* 프로세스 내에서 관리하는 액티비티가 Pause 상태로 사용자가 볼 수 만 있는 상태.
3. Service Process
* 프로세스 내에서 동작하는 서비스가 있는 상태.
4. Background Process
* 프로세스에서 관리하고 있는 액티비티들이 사용자에게 보여주지 않고 존재하는 상태.
* 프로세스 내의 서비스들 중에 동작 중인 서비스가 없는 상태.
5. Empty Process
* 프로세스 내에 동작하는 액티비티 또는 서비스가 업슨 경우에 Background Process와 마찬가지로 안드로이드 시스템에서 프로세스를 강제 종료하여 부족한 메모리를 회수한다.
=> 4 ~5 번에 있는 프로세스 상태는 안드로이드 시스템에서 프로세스를 강제 종료하여 부족한 메모리를 회수하게 된다.
언제든지 프로세스가 종료 될 수 있기 때문에 사용중이던 액티비티는 상태를 저장할 수 있어야 한다.(Oncreate의 Bundle을 생각해라!)
이런 안드로이드 시스템에서 프로세스들의 우선순위를 관리하고 강제 종료를 담당하는 일은 LMK(Low Memory Killer)라는 커널 모듈에서 합니다. 리눅스의 메모리를 관리하는 OOM(Out Of Memory Killer)를 사용하지 않고 안드로이드에 특화된 메모리 관리 모듈을 사용합니다. 이는 안드로이드와 같은 모바일 환경에서는 리눅스의 OOM 커널이 맞지 않기 때문인데.
OOM에서는 메모리의 사용이 많으면 강제종료의 대상이 되지만, 안드로이드는 현재 사용 중인 프로세스를 제일 높은 우선순위로 여기고 프로세스가 죽지 않게 다른 프로세스를 대상으로 메모리를 회수 하기 때문입니다.
그렇다면 스레드에 대해 알아보겠습니다.
스레드는 프로세스 내에서 실질적인 작업을 수행 단위를 말합니다. 안드로이드에서 앱이 실행되면 프로세스가 생성되고 프로세스 내에는 메인 스레드가 생성되고, 메인 스레드 내에서 안드로이드 컴포넌트들이 동작하는 형태입니다. 그리고 메인 스레드에서 동작중인 작업들을 woker thread를 만들어서 사용할 수 있습니다.(시간이 오래 걸리는 작업을 메인 스레드에서 진행하면 사용자 입장에서는 앱이 중지되어 있는 것처럼 느낄수 있기 때문에 ANR을 통해 앱을 종료할지 대기할지 선택할수 있는 팝업을 제공하기도 합니다. 가령 Mainthread에서 while문을 통해 반복적인 작업을 해야한다면 어쩔 것인가? -> 쓰레드를 생성한다 ^^)
기본적으로 스레드의 사용법은 안다고 가정하고, 많이들 헷갈려 하는 부분만 말을 하자면 쓰레드 클래스를 만들때 Thread를 상속받아 만들어도 되고, Runnable 클래스를 implement 하여 만들어도 됩니다. 이들의 차이점은 Thread를 상속받아 만들게 된다면 기본적으로 제공하는 wait, join, yield 등의 메소드를 사용할 수 있다는 장점이 있습니다.
또한 worker thread에서는 ui갱신이 되지 않기 때문에 runOnUiThread를 이용하여 업데이트 시켜줘도 되며, handler를 통해 업데이트 시킬수도 있습니다.
'Android > Development Tips' 카테고리의 다른 글
[펌] Activity 스택 intent flag (0) | 2018.07.12 |
---|---|
Android NDK (0) | 2018.04.12 |
Measuring of String in android (0) | 2018.02.12 |
액티비티와 생명주기 (0) | 2017.12.03 |
안드로이드 컴포넌트 (0) | 2017.12.03 |