프로세스 및 스레드 개요

애플리케이션 구성요소가 시작되고 애플리케이션에 실행 중인 다른 구성요소가 없으면 Android 시스템은 단일 실행 스레드로 애플리케이션의 새 Linux 프로세스를 시작합니다. 기본적으로 같은 애플리케이션의 모든 구성요소는 기본 스레드라고 하는 동일한 프로세스와 스레드에서 실행됩니다.

애플리케이션의 다른 구성요소가 이미 시작되었기 때문에 애플리케이션 구성요소가 시작되고 해당 애플리케이션의 프로세스가 이미 있는 경우, 구성요소는 해당 프로세스 내에서 시작되고 동일한 실행 스레드를 사용합니다. 그러나 애플리케이션의 여러 구성요소가 별도의 프로세스에서 실행되도록 할 수 있으며 모든 프로세스에 추가 스레드를 만들 수 있습니다.

이 문서는 프로세스와 스레드가 Android 애플리케이션에서 작동하는 방식을 설명합니다.

프로세스

기본적으로 애플리케이션의 모든 구성요소는 동일한 프로세스에서 실행되며 대부분의 애플리케이션은 이를 변경하지 않습니다. 그러나 특정 구성요소가 속하는 프로세스를 제어해야 하는 경우 매니페스트 파일에서 제어할 수 있습니다.

각 구성요소 유형의 매니페스트 항목(<activity>, <service>, <receiver>, <provider>)은 구성요소가 실행되는 프로세스를 지정할 수 있는 android:process 속성을 지원합니다. 각 구성요소가 자체 프로세스에서 실행되도록 하거나 일부 구성요소는 프로세스를 공유하고 다른 구성요소는 공유하지 않도록 이 속성을 설정할 수 있습니다.

애플리케이션이 동일한 Linux 사용자 ID를 공유하고 동일한 인증서로 서명되었다면 서로 다른 애플리케이션의 구성요소가 동일한 프로세스에서 실행되도록 android:process를 설정할 수도 있습니다.

<application> 요소는 모든 구성요소에 적용되는 기본값을 설정하는 데 사용할 수 있는 android:process 속성도 지원합니다.

Android는 사용자에게 더 즉각적으로 서비스를 제공하는 다른 프로세스에서 리소스가 필요할 때 특정 시점에 프로세스를 종료하기로 결정할 수 있습니다. 따라서 종료된 프로세스에서 실행 중인 애플리케이션 구성요소도 함께 폐기됩니다. 해야 할 작업이 있으면 이러한 구성요소의 프로세스가 다시 시작됩니다.

종료할 프로세스를 결정할 때 Android 시스템은 사용자와 관련한 프로세스의 상대적 중요도를 평가합니다. 예를 들어 눈에 보이는 활동을 호스팅하는 프로세스보다 화면에 더 이상 표시되지 않는 활동을 호스팅하는 프로세스를 더 쉽게 종료합니다. 따라서 프로세스를 종료할지 여부는 해당 프로세스에서 실행되는 구성요소의 상태에 따라 달라집니다.

프로세스 수명 주기의 세부정보와 애플리케이션 상태와의 관계에 관한 내용은 프로세스 및 앱 수명 주기에서 설명합니다.

Threads

애플리케이션이 시작되면 시스템은 기본 스레드라고 하는 애플리케이션 실행 스레드를 만듭니다. 이 스레드는 그리기 이벤트를 포함하여 적절한 사용자 인터페이스 위젯에 이벤트를 전달하는 역할을 하므로 매우 중요합니다. 또한 거의 항상 애플리케이션이 Android UI 도구 키트의 android.widgetandroid.view 패키지에 있는 구성요소와 상호작용하는 스레드입니다. 이러한 이유로 기본 스레드를 UI 스레드라고도 합니다. 그러나 특별한 상황에서는 앱의 기본 스레드가 UI 스레드가 아닐 수 있습니다. 자세한 내용은 스레드 주석을 참고하세요.

시스템은 구성요소의 각 인스턴스에 대해 별도의 스레드를 생성하지 않습니다. 같은 프로세스에서 실행되는 모든 구성요소는 UI 스레드에서 인스턴스화되고 각 구성요소의 시스템 호출이 해당 스레드에서 전달됩니다. 따라서 시스템 콜백에 응답하는 메서드(예: 사용자 작업을 보고하는 onKeyDown() 또는 수명 주기 콜백 메서드)는 항상 프로세스의 UI 스레드에서 실행됩니다.

예를 들어 사용자가 화면의 버튼을 터치하면 앱의 UI 스레드가 위젯에 터치 이벤트를 전달하고 위젯은 눌린 상태를 설정하고 이벤트 큐에 무효화 요청을 게시합니다. UI 스레드가 이 요청을 큐에서 제거하고 위젯에 자체적으로 다시 그리라고 알립니다.

애플리케이션을 올바르게 구현하지 않으면 앱이 사용자 상호작용에 응답하여 집중적인 작업을 실행할 때 이 단일 스레드 모델은 성능이 저하될 수 있습니다. 네트워크 액세스나 데이터베이스 쿼리와 같은 UI 스레드에서 긴 작업을 실행하면 전체 UI가 차단됩니다. 스레드가 차단되면 그리기 이벤트를 비롯한 모든 이벤트가 전달될 수 없습니다.

사용자의 관점에서는 애플리케이션이 중단된 것처럼 보입니다. 심지어 UI 스레드가 몇 초 이상 차단되면 사용자에게 '애플리케이션 응답 없음' (ANR) 대화상자가 표시됩니다. 그러면 사용자는 애플리케이션을 종료하거나 제거할 수도 있습니다.

Android UI 도구 키트는 스레드로부터 안전하지 않습니다. 따라서 작업자 스레드에서 UI를 조작하지 마세요. UI 스레드에서 사용자 인터페이스를 모두 조작합니다. Android의 단일 스레드 모델에는 두 가지 규칙이 있습니다.

  1. UI 스레드를 차단하지 않습니다.
  2. UI 스레드 외부에서 Android UI 도구 키트에 액세스하면 안 됩니다.

작업자 스레드

이 단일 스레드 모델로 인해 애플리케이션 UI가 반응하도록 하려면 UI 스레드를 차단하지 않는 것이 중요합니다. 실행할 작업이 있지만 즉각적인 조치가 없으면 별도의 백그라운드 또는 작업자 스레드에서 실행해야 합니다. UI 또는 기본 스레드가 아닌 다른 스레드에서 UI를 업데이트할 수 없다는 점에 유의하세요.

이러한 규칙을 따를 수 있도록 Android는 다른 스레드에서 UI 스레드에 액세스하는 여러 가지 방법을 제공합니다. 다음은 몇 가지 유용한 메서드 목록입니다.

다음 예에서는 View.post(Runnable)를 사용합니다.

Kotlin

fun onClick(v: View) {
    Thread(Runnable {
        // A potentially time consuming task.
        val bitmap = processBitMap("image.png")
        imageView.post {
            imageView.setImageBitmap(bitmap)
        }
    }).start()
}

Java

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            // A potentially time consuming task.
            final Bitmap bitmap =
                    processBitMap("image.png");
            imageView.post(new Runnable() {
                public void run() {
                    imageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

ImageView는 항상 UI 스레드에서 조작되는 반면 백그라운드 작업은 별도의 스레드에서 실행되므로 이 구현은 스레드로부터 안전합니다.

그러나 작업이 복잡해질수록 이러한 종류의 코드가 더 복잡해지고 유지 관리하기가 어려워질 수 있습니다. 작업자 스레드와의 더 복잡한 상호작용을 처리하려면 작업자 스레드에서 Handler를 사용하여 UI 스레드에서 전달된 메시지를 처리하는 것이 좋습니다. 백그라운드 스레드에서 작업을 예약하고 UI 스레드에 다시 통신하는 방법에 관한 자세한 설명은 백그라운드 작업 개요를 참고하세요.

스레드로부터 안전한 메서드

구현하는 메서드가 두 개 이상의 스레드에서 호출되는 경우가 있으므로 스레드로부터 안전하게 작성되어야 합니다.

이는 주로 바인드된 서비스의 메서드와 같이 원격으로 호출할 수 있는 메서드에 해당합니다. IBinder에 구현된 메서드의 호출이 IBinder가 실행 중인 동일한 프로세스에서 시작되면 메서드는 호출자의 스레드에서 실행됩니다. 그러나 호출이 다른 프로세스에서 발생하면 메서드는 시스템이 IBinder와 동일한 프로세스에서 유지하는 스레드 풀에서 선택된 스레드에서 실행됩니다. 프로세스의 UI 스레드에서 실행되지 않습니다.

예를 들어 서비스의 onBind() 메서드는 서비스 프로세스의 UI 스레드에서 호출되지만 onBind()가 반환하는 객체에 구현된 메서드(예: 리모트 프로시져 콜(RPC) 메서드를 구현하는 서브클래스)는 풀의 스레드에서 호출됩니다. 한 서비스에 클라이언트가 둘 이상 있을 수 있으므로 둘 이상의 풀 스레드가 동시에 같은 IBinder 메서드에 참여할 수 있으므로 IBinder 메서드는 스레드로부터 안전하게 구현되어야 합니다.

마찬가지로 콘텐츠 제공자는 다른 프로세스에서 발생한 데이터 요청을 수신할 수 있습니다. ContentResolverContentProvider 클래스는 프로세스 간 통신(IPC) 관리 방법의 세부정보를 숨기지만 이러한 요청에 응답하는 ContentProvider 메서드(query(), insert(), delete(), update()getType() 메서드)는 프로세스의 UI 스레드가 아니라 콘텐츠 제공자 프로세스의 스레드 풀에서 호출됩니다. 이러한 메서드는 동시에 여러 스레드에서 호출될 수 있으므로 스레드로부터 안전하게 구현되어야 합니다.

프로세스 간 통신

Android는 RPC를 사용하는 IPC 메커니즘을 제공합니다. 이 메커니즘에서는 메서드가 활동이나 다른 애플리케이션 구성요소에 의해 호출되지만 다른 프로세스에서 원격으로 실행되고 모든 결과가 호출자에 반환됩니다. 여기에는 메서드 호출과 그 데이터를 운영체제가 이해할 수 있는 수준으로 분해하여 로컬 프로세스와 주소 공간에서 원격 프로세스와 주소 공간으로 전송한 다음 여기서 호출을 다시 조합하고 다시 실행합니다.

그러면 반환 값이 반대 방향으로 전송됩니다. Android는 이러한 IPC 트랜잭션을 실행하는 데 필요한 모든 코드를 제공하므로 개발자는 RPC 프로그래밍 인터페이스를 정의하고 구현하는 데에만 집중할 수 있습니다.

IPC를 실행하려면 bindService()를 사용하여 애플리케이션을 서비스에 바인딩해야 합니다. 자세한 내용은 서비스 개요를 참고하세요.