애플리케이션 구성요소가 시작되고 애플리케이션에 실행 중인 다른 구성요소가 없는 경우 Android 시스템은 실행 스레드가 하나인 애플리케이션의 새 Linux 프로세스를 시작합니다. 기본적으로 동일한 애플리케이션의 모든 구성요소는 기본 스레드라고 하는 동일한 프로세스 및 스레드에서 실행됩니다.
애플리케이션 구성요소가 시작되고 애플리케이션의 다른 구성요소가 이미 시작되었기 때문에 해당 애플리케이션의 프로세스가 이미 있는 경우 구성요소는 해당 프로세스 내에서 시작되고 동일한 실행 스레드를 사용합니다. 그러나 애플리케이션의 여러 구성요소가 별도의 프로세스에서 실행되도록 정렬하고 모든 프로세스에 추가 스레드를 만들 수 있습니다.
이 문서에서는 Android 애플리케이션에서 프로세스와 스레드가 작동하는 방법을 설명합니다.
프로세스
기본적으로 애플리케이션의 모든 구성요소는 동일한 프로세스에서 실행되며 대부분의 애플리케이션은 이를 변경하지 않습니다. 그러나 특정 구성요소가 속한 프로세스를 제어해야 하는 경우 매니페스트 파일에서 이 작업을 실행할 수 있습니다.
각 구성요소 요소 유형(<activity>, <service>, <receiver>, <provider>)의 매니페스트 항목은 구성요소가 실행되는 프로세스를 지정할 수 있는 android:process 속성을 지원합니다. 각 구성요소가 자체 프로세스에서 실행되도록 또는 일부 구성요소는 프로세스를 공유하지만 다른 구성요소는 공유하지 않도록 이 속성을 설정할 수 있습니다.
애플리케이션이 동일한 Linux 사용자 ID를 공유하고 동일한 인증서로 서명된 경우 서로 다른 애플리케이션의 구성요소가 동일한 프로세스에서 실행되도록 android:process를 설정할 수도 있습니다.
<application>
요소는 모든 구성요소에 적용되는 기본값을 설정하는 데 사용할 수 있는 android:process 속성도 지원합니다.
Android는 사용자를 더 즉시 지원하는 다른 프로세스에 리소스가 필요한 경우 어느 시점에서 프로세스를 종료할 수 있습니다. 종료된 프로세스에서 실행되는 애플리케이션 구성요소는 결과적으로 소멸됩니다. 이러한 구성요소에 실행할 작업이 있으면 프로세스가 다시 시작됩니다.
종료할 프로세스를 결정할 때 Android 시스템은 사용자에게 미치는 상대적 중요성을 고려합니다. 예를 들어 화면에 더 이상 표시되지 않는 활동을 호스팅하는 프로세스는 표시되는 활동을 호스팅하는 프로세스에 비해 더 쉽게 종료됩니다. 따라서 프로세스를 종료할지 여부는 해당 프로세스에서 실행되는 구성요소의 상태에 따라 달라집니다.
프로세스 수명 주기 및 애플리케이션 상태와의 관계에 관한 자세한 내용은 프로세스 및 앱 수명 주기를 참고하세요.
스레드
애플리케이션이 실행되면 시스템은 기본 스레드 라고 하는 애플리케이션의 실행 스레드를 만듭니다. 이 스레드는 그리기 이벤트를 비롯한 이벤트를 적절한 사용자 인터페이스 위젯에 전달하는 역할을 하므로 매우 중요합니다. 또한 애플리케이션이 Android UI 툴킷의 android.widget 및 android.view 패키지의 구성요소와 상호작용하는 스레드이기도 합니다.
이러한 이유로 기본 스레드를 UI 스레드 라고도 합니다. 그러나 특별한 상황에서는 앱의 기본 스레드가 UI 스레드가 아닐 수 있습니다. 자세한 내용은 스레드
주석을 참고하세요.
시스템은 구성요소의 각 인스턴스에 별도의 스레드를 만들지 않습니다. 동일한 프로세스에서 실행되는 모든 구성요소는 UI 스레드에서 인스턴스화되며 각 구성요소에 대한 시스템 호출은 해당 스레드에서 전달됩니다. 따라서 사용자 작업을 보고하는 onKeyDown() 또는 수명 주기 콜백 메서드와 같은 시스템 콜백에 응답하는 메서드는 항상 프로세스의 UI 스레드에서 실행됩니다.
예를 들어 사용자가 화면의 버튼을 터치하면 앱의 UI 스레드는 터치 이벤트를 위젯에 전달하고 위젯은 눌린 상태를 설정하고 무효화 요청을 이벤트 대기열에 게시합니다. UI 스레드는 요청을 대기열에서 삭제하고 위젯에 자체적으로 다시 그리도록 알립니다.
애플리케이션을 올바르게 구현하지 않으면 이 단일 스레드 모델은 앱이 사용자 상호작용에 응답하여 집약적인 작업을 실행할 때 성능이 저하될 수 있습니다. 네트워크 액세스 또는 데이터베이스 쿼리와 같은 긴 작업을 UI 스레드에서 실행하면 전체 UI가 차단됩니다. 스레드가 차단되면 그리기 이벤트를 비롯한 이벤트를 전달할 수 없습니다.
사용자 관점에서 애플리케이션이 응답을 중지한 것으로 보입니다. 더욱이 UI 스레드가 몇 초 이상 차단되면 사용자에게 "애플리케이션 응답 없음" (ANR) 대화상자가 표시됩니다. 그러면 사용자는 애플리케이션을 종료하거나 제거할 수도 있습니다.
Android UI 툴킷은 스레드 안전이 아닙니다. 따라서 작업자 스레드에서 UI를 조작하지 마세요. UI 스레드에서 사용자 인터페이스를 모두 조작하세요. Android의 단일 스레드 모델에는 두 가지 규칙이 있습니다.
- UI 스레드를 차단하지 마세요.
- UI 스레드 외부에서 Android UI 툴킷에 액세스하지 마세요.
작업자 스레드
이 단일 스레드 모델로 인해 UI 스레드를 차단하지 않는 것이 애플리케이션 UI의 응답성에 매우 중요합니다. 즉각적이지 않은 작업을 실행해야 하는 경우 별도의 백그라운드 또는 작업자 스레드에서 실행해야 합니다. UI 또는 기본 스레드 이외의 스레드에서는 UI를 업데이트할 수 없다는 점을 기억하세요.
이러한 규칙을 준수할 수 있도록 Android는 다른 스레드에서 UI 스레드에 액세스하는 여러 방법을 제공합니다. 다음은 도움이 될 수 있는 메서드 목록입니다.
다음 예에서는 작업을 백그라운드 스레드로 오프로드하고 작업이 완료되면 UI 스레드를 업데이트하는 방법을 보여줍니다.
Kotlin
// Kotlin coroutines implementation. fun onClick(v: View) { // Launch a coroutine in the lifecycle scope (e.g., in an Activity or Fragment). lifecycleScope.launch { // Run the blocking task on the IO dispatcher. val bitmap = withContext(Dispatchers.IO) { BitmapFactory.decodeFile("image.png") } // Back on the main thread, update the UI. imageView.setImageBitmap(bitmap) } }
자바
// Java Executor implementation. // (executorService is assumed to be defined elsewhere). public void onClick(View v) { executorService.execute(() -> { // Run the heavy task on a background thread. Bitmap bitmap = BitmapFactory.decodeFile("image.png"); // Update the View on the UI thread. imageView.post(() -> imageView.setImageBitmap(bitmap)); }); }
이 구현은 스레드 안전입니다. 백그라운드 작업은 별도의 스레드에서 실행되는 반면 ImageView는 항상 UI 스레드에서 조작되기 때문입니다.
그러나 작업의 복잡성이 증가함에 따라 이러한 종류의 코드는 복잡해지고 유지보수하기 어려워질 수 있습니다. 작업자 스레드와의 더 복잡한 상호작용을 처리하려면 작업자 스레드에서 Handler를 사용하여 UI 스레드에서 전달된 메시지를 처리하는 것이 좋습니다. 백그라운드 스레드에서 작업을 예약하고 UI 스레드와 다시 통신하는 방법에 관한 자세한 설명은
백그라운드 작업 개요를 참고하세요.
스레드 안전 메서드
경우에 따라 구현하는 메서드가 둘 이상의 스레드에서 호출되므로 스레드 안전하도록 작성해야 합니다.
이는 주로 바인딩된 서비스의 메서드와 같이 원격으로 호출할 수 있는 메서드에 해당합니다.
에서 구현된 메서드에 대한 호출이 IBinder가 실행 중인 동일한 프로세스에서 시작되면 메서드는 호출자의 스레드에서 실행됩니다.IBinder
그러나 호출이 다른 프로세스에서 시작되면 메서드는 시스템이 IBinder와 동일한 프로세스에서 유지하는 스레드 풀에서 선택한 스레드에서 실행됩니다.
프로세스의 UI 스레드에서는 실행되지 않습니다.
예를 들어 서비스의
onBind() 메서드는 서비스 프로세스의 UI 스레드에서 호출되는 반면 리모트 프러시저 콜 (RPC) 메서드를 구현하는 서브클래스와 같이 onBind()가 반환하는 객체에서 구현된 메서드는 풀의 스레드에서 호출됩니다. 서비스에는 클라이언트가 두 개 이상 있을 수 있으므로 두 개 이상의 풀 스레드가 동시에 동일한 IBinder 메서드를 사용할 수 있으므로 IBinder 메서드는 스레드 안전하도록 구현해야 합니다.
마찬가지로 콘텐츠 제공자는 다른 프로세스에서 시작된 데이터 요청을 수신할 수 있습니다.
ContentResolver 및 ContentProvider
클래스는 프로세스 간 통신(IPC)이 관리되는 방식의 세부정보를 숨기지만
이러한 요청에 응답하는 ContentProvider 메서드(
query(),
insert(),
delete(),
update(),
및 getType())는
프로세스의 UI
스레드가 아닌 콘텐츠 제공자의 프로세스에 있는 스레드 풀에서 호출됩니다. 이러한 메서드는 동시에 여러 스레드에서 호출될 수 있으므로 스레드 안전하도록 구현해야 합니다.
프로세스 간 통신
Android는 RPC를 사용하는 IPC 메커니즘을 제공합니다. 여기서 메서드는 활동 또는 기타 애플리케이션 구성요소에 의해 호출되지만 다른 프로세스에서 원격으로 실행되며 결과는 호출자에게 다시 반환됩니다. 여기에는 메서드 호출과 데이터를 운영체제가 이해할 수 있는 수준으로 분해하고, 로컬 프로세스 및 주소 공간에서 원격 프로세스 및 주소 공간으로 전송한 다음, 호출을 다시 어셈블하고 다시 실행하는 작업이 포함됩니다.
그런 다음 반환 값은 반대 방향으로 전송됩니다. Android는 이러한 IPC 트랜잭션을 실행하는 모든 코드를 제공하므로 RPC 프로그래밍 인터페이스를 정의하고 구현하는 데 집중할 수 있습니다.
IPC를 실행하려면 애플리케이션이 bindService()를 사용하여 서비스에 바인딩해야 합니다. 자세한 내용은 서비스 개요를 참고하세요.