브로드캐스트 개요

Android 앱은 게시-구독 디자인 패턴과 유사하게 Android 시스템 및 기타 Android 앱에서 브로드캐스트 메시지를 보내고 받습니다. 시스템과 앱은 일반적으로 특정 이벤트가 발생할 때 브로드캐스트를 전송합니다. 예를 들어 Android 시스템은 시스템 부팅이나 기기 충전과 같은 다양한 시스템 이벤트가 발생할 때 브로드캐스트를 전송합니다. 앱은 다른 앱이 관심을 가질 만한 사항 (예: 새 데이터 다운로드)을 알리기 위해 맞춤 브로드캐스트를 전송하기도 합니다.

앱은 특정 브로드캐스트를 수신하도록 등록할 수 있습니다. 브로드캐스트가 전송되면 시스템은 해당 유형의 브로드캐스트를 수신하도록 구독한 앱으로 브로드캐스트를 자동으로 라우팅합니다.

일반적으로 브로드캐스트는 앱 간에 그리고 일반적인 사용자 흐름 외부에서 메시지 시스템으로 사용할 수 있습니다. 하지만 브로드캐스트에 응답하고 시스템 성능 저하의 원인이 될 수 있는 작업을 백그라운드에서 실행하는 기회를 남용하지 않도록 주의해야 합니다.

시스템 브로드캐스트 정보

시스템은 시스템이 비행기 모드로 전환되거나 비행기 모드에서 전환되는 등 다양한 시스템 이벤트가 발생할 때 브로드캐스트를 자동으로 전송합니다. 구독된 모든 앱이 이러한 브로드캐스트를 수신합니다.

Intent 객체는 방송 메시지를 래핑합니다. action 문자열은 발생한 이벤트(예: android.intent.action.AIRPLANE_MODE)를 식별합니다. 인텐트에는 extra 필드에 번들로 묶인 추가 정보가 포함될 수도 있습니다. 예를 들어 비행기 모드 인텐트에는 비행기 모드가 사용 설정되어 있는지 여부를 나타내는 불리언 추가 항목이 포함됩니다.

인텐트를 읽고 인텐트에서 작업 문자열을 가져오는 방법에 관한 자세한 내용은 인텐트 및 인텐트 필터를 참고하세요.

시스템 브로드캐스트 작업

시스템 브로드캐스트 작업의 전체 목록은 Android SDK의 BROADCAST_ACTIONS.TXT 파일을 참고하세요. 각 브로드캐스트 작업에는 연결된 상수 필드가 있습니다. 예를 들어 상수 ACTION_AIRPLANE_MODE_CHANGED의 값은 android.intent.action.AIRPLANE_MODE입니다. 각 브로드캐스트 작업의 문서는 연결된 상수 필드에서 확인할 수 있습니다.

시스템 브로드캐스트 변경사항

Android 플랫폼이 발전함에 따라 시스템 브로드캐스트의 동작 방식이 주기적으로 변경됩니다. 모든 Android 버전을 지원하려면 다음 변경사항을 참고하세요.

Android 16

Android 16에서는 android:priority 속성이나 IntentFilter.setPriority()를 사용하여 서로 다른 프로세스 간에 브로드캐스트 전송 순서가 보장되지 않습니다. 브로드캐스트 우선순위는 모든 프로세스가 아닌 동일한 애플리케이션 프로세스 내에서만 적용됩니다.

또한 브로드캐스트 우선순위는 (SYSTEM_LOW_PRIORITY + 1, SYSTEM_HIGH_PRIORITY - 1) 범위로 자동 제한됩니다. 시스템 구성요소만 SYSTEM_LOW_PRIORITY, SYSTEM_HIGH_PRIORITY을 브로드캐스트 우선순위로 설정할 수 있습니다.

Android 14

앱이 캐시된 상태인 동안 시스템은 시스템 상태를 위해 브로드캐스트 전송을 최적화합니다. 예를 들어 앱이 캐시된 상태에 있는 동안 시스템은 ACTION_SCREEN_ON과 같은 덜 중요한 시스템 브로드캐스트를 지연시킵니다. 앱이 캐시된 상태에서 활성 프로세스 수명 주기로 전환되면 시스템은 지연된 브로드캐스트를 전송합니다.

매니페스트에 선언된 중요한 브로드캐스트는 전송을 위해 캐시된 상태에서 앱을 일시적으로 삭제합니다.

Android 9

Android 9 (API 수준 28)부터 NETWORK_STATE_CHANGED_ACTION 브로드캐스트는 사용자의 위치 또는 개인 식별 정보에 관한 정보를 수신하지 않습니다.

Android 9.0 (API 수준 28) 이상을 실행하는 기기에 앱이 설치된 경우 시스템은 Wi-Fi 브로드캐스트에 SSID, BSSID, 연결 정보 또는 검색 결과를 포함하지 않습니다. 이 정보를 가져오려면 대신 getConnectionInfo()를 호출하세요.

Android 8.0

Android 8.0 (API 수준 26)부터 시스템은 매니페스트 선언 수신기에 추가 제한을 적용합니다.

앱이 Android 8.0 이상을 타겟팅하는 경우 매니페스트를 사용하여 대부분의 암시적 브로드캐스트 (앱을 구체적으로 타겟팅하지 않는 브로드캐스트)의 리시버를 선언할 수 없습니다. 사용자가 앱을 활발하게 사용하는 경우 컨텍스트 등록 수신기를 계속 사용할 수 있습니다.

Android 7.0

Android 7.0 (API 수준 24) 이상에서는 다음 시스템 브로드캐스트를 전송하지 않습니다.

또한 Android 7.0 이상을 타겟팅하는 앱은 registerReceiver(BroadcastReceiver, IntentFilter)를 사용하여 CONNECTIVITY_ACTION 브로드캐스트를 등록해야 합니다. 매니페스트에서 리시버를 선언해도 작동하지 않습니다.

방송 수신

앱은 컨텍스트 등록 수신기와 매니페스트 선언 수신기의 두 가지 방법으로 브로드캐스트를 수신할 수 있습니다.

컨텍스트에 등록된 수신자

컨텍스트 등록 수신기는 등록 컨텍스트가 유효한 한 브로드캐스트를 수신합니다. 일반적으로 registerReceiver 및 unregisterReceiver 호출 사이에 있습니다. 등록 컨텍스트는 시스템이 해당 컨텍스트를 소멸시킬 때도 무효화됩니다. 예를 들어 Activity 컨텍스트 내에서 등록하면 활동이 활성 상태로 유지되는 한 브로드캐스트를 수신합니다. 애플리케이션 컨텍스트로 등록하면 앱이 실행되는 동안 브로드캐스트를 수신합니다.

수신자를 컨텍스트에 등록하려면 다음 단계를 따르세요.

  1. 앱의 모듈 수준 빌드 파일에 AndroidX 핵심 라이브러리 버전 1.9.0 이상을 포함합니다.

    Groovy

    dependencies {
        def core_version = "1.19.1"
    
        // Java language implementation
        implementation "androidx.core:core:$core_version"
        // Kotlin
        implementation "androidx.core:core-ktx:$core_version"
    
        // To use RoleManagerCompat
        implementation "androidx.core:core-role:1.1.0"
    
        // To use the Animator APIs
        implementation "androidx.core:core-animation:1.0.0"
        // To test the Animator APIs
        androidTestImplementation "androidx.core:core-animation-testing:1.0.0"
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation "androidx.core:core-performance:1.0.0"
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation "androidx.core:core-google-shortcuts:1.1.0"
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation "androidx.core:core-remoteviews:1.1.0"
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation "androidx.core:core-splashscreen:1.2.0"
    }

    Kotlin

    dependencies {
        val core_version = "1.19.1"
    
        // Java language implementation
        implementation("androidx.core:core:$core_version")
        // Kotlin
        implementation("androidx.core:core-ktx:$core_version")
    
        // To use RoleManagerCompat
        implementation("androidx.core:core-role:1.1.0")
    
        // To use the Animator APIs
        implementation("androidx.core:core-animation:1.0.0")
        // To test the Animator APIs
        androidTestImplementation("androidx.core:core-animation-testing:1.0.0")
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation("androidx.core:core-performance:1.0.0")
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation("androidx.core:core-google-shortcuts:1.1.0")
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation("androidx.core:core-remoteviews:1.1.0")
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation("androidx.core:core-splashscreen:1.2.0")
    }
  2. BroadcastReceiver의 인스턴스를 만듭니다.

    Kotlin

    val myBroadcastReceiver = MyBroadcastReceiver()
    

    자바

    MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
    
  3. IntentFilter의 인스턴스를 만듭니다.

    Kotlin

    val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
    

    자바

    IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");
    
  4. broadcast receiver를 내보내기하여 기기의 다른 앱이 볼 수 있게 해야 하는지 선택합니다. 이 수신기가 시스템이나 다른 앱(내가 소유한 다른 앱 포함)에서 전송된 브로드캐스트를 수신하는 경우 RECEIVER_EXPORTED 플래그를 사용합니다. 이 수신기가 앱에서 전송한 브로드캐스트만 수신 대기하는 경우 RECEIVER_NOT_EXPORTED 플래그를 사용하세요.

    Kotlin

    val listenToBroadcastsFromOtherApps = false
    val receiverFlags = if (listenToBroadcastsFromOtherApps) {
        ContextCompat.RECEIVER_EXPORTED
    } else {
        ContextCompat.RECEIVER_NOT_EXPORTED
    }
    

    자바

    boolean listenToBroadcastsFromOtherApps = false;
    int receiverFlags = listenToBroadcastsFromOtherApps
            ? ContextCompat.RECEIVER_EXPORTED
            : ContextCompat.RECEIVER_NOT_EXPORTED;
    
  5. registerReceiver()를 호출하여 리시버를 등록합니다.

    Kotlin

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags)
    

    자바

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);
    
  6. 브로드캐스트 수신을 중지하려면 unregisterReceiver(android.content.BroadcastReceiver)을 호출합니다. 더 이상 필요하지 않거나 컨텍스트가 더 이상 유효하지 않은 경우 리시버를 등록 취소해야 합니다.

broadcast receiver 등록 취소

브로드캐스트 리시버가 등록되어 있는 동안 등록된 컨텍스트에 대한 참조를 보유합니다. 수신자의 등록된 범위가 컨텍스트 수명 주기 범위를 초과하면 누수가 발생할 수 있습니다. 예를 들어 활동 범위에 리시버를 등록했지만 시스템이 활동을 소멸할 때 등록 취소하는 것을 잊은 경우에 발생할 수 있습니다. 따라서 항상 브로드캐스트 리시버를 등록 해제하세요.

Kotlin

class MyActivity : ComponentActivity() {
    private val myBroadcastReceiver = MyBroadcastReceiver()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags)
        setContent { MyApp() }
    }

    override fun onDestroy() {
        super.onDestroy()
        // When you forget to unregister your receiver here, you're causing a leak!
        this.unregisterReceiver(myBroadcastReceiver)
    }
}

자바

class MyActivity extends ComponentActivity {
    MyBroadcastReceiver myBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ...
        ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags);
        // Set content
    }
}

최소 범위에 리시버 등록

브로드캐스트 수신기는 결과에 실제로 관심이 있을 때만 등록해야 합니다. 가능한 가장 작은 수신기 범위를 선택합니다.

  • LifecycleResumeEffect 또는 활동 onResume/onPause 수명 주기 메서드: 앱이 재개된 상태일 때만 브로드캐스트 수신기가 업데이트를 수신합니다.
  • LifecycleStartEffect 또는 활동 onStart/onStop 수명 주기 메서드: 앱이 재개된 상태일 때만 브로드캐스트 수신기가 업데이트를 수신합니다.
  • DisposableEffect: 컴포저블이 컴포지션 트리에 있는 동안에만 브로드캐스트 리시버가 업데이트를 수신합니다. 이 범위는 활동 수명 주기 범위에 연결되지 않습니다. 애플리케이션 컨텍스트에 리시버를 등록하는 것이 좋습니다. 이는 컴포저블이 이론적으로 활동 수명 주기 범위를 초과하여 활동을 누수할 수 있기 때문입니다.
  • 활동 onCreate/onDestroy: 활동이 생성된 상태에 있는 동안 브로드캐스트 수신기가 업데이트를 수신합니다. onSaveInstanceState(Bundle)이 호출되지 않을 수 있으므로 onDestroy()에서 등록을 취소해야 합니다.
  • 맞춤 범위: 예를 들어 ViewModel 범위에 리시버를 등록하여 활동 재생성 후에도 유지할 수 있습니다. 수신기가 활동 수명 주기 범위를 벗어나 활동을 누출할 수 있으므로 애플리케이션 컨텍스트를 사용하여 수신기를 등록해야 합니다.

스테이트풀(Stateful) 및 스테이트리스(Stateless) 컴포저블 만들기

Compose에는 스테이트풀(Stateful) 및 스테이트리스(Stateless) 컴포저블이 있습니다. 컴포저블 내에서 브로드캐스트 리시버를 등록하거나 등록 취소하면 스테이트풀이 됩니다. 컴포저블은 동일한 매개변수가 전달될 때 동일한 콘텐츠를 렌더링하는 결정적 함수가 아닙니다. 내부 상태는 등록된 브로드캐스트 수신기에 대한 호출에 따라 변경될 수 있습니다.

Compose의 권장사항에 따라 컴포저블을 상태가 있는 버전과 상태가 없는 버전으로 분할하는 것이 좋습니다. 따라서 브로드캐스트 리시버 생성을 컴포저블 외부로 호이스팅하여 스테이트리스로 만드는 것이 좋습니다.

@Composable
fun MyStatefulScreen() {
    val myBroadcastReceiver = remember { MyBroadcastReceiver() }
    val context = LocalContext.current
    LifecycleStartEffect(true) {
        // ...
        ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, flags)
        onStopOrDispose { context.unregisterReceiver(myBroadcastReceiver) }
    }
    MyStatelessScreen()
}

@Composable
fun MyStatelessScreen() {
    // Implement your screen
}

manifest에 선언된 수신자

매니페스트에서 브로드캐스트 수신기를 선언하면 브로드캐스트가 전송될 때 시스템에서 앱을 실행합니다. 앱이 아직 실행되고 있지 않으면 시스템에서 앱을 실행합니다.

manifest에서 broadcast receiver를 선언하려면 다음 단계를 따르세요.

  1. 앱의 매니페스트에서 <receiver> 요소를 지정합니다.

    <!-- If this receiver listens for broadcasts sent from the system or from
         other apps, even other apps that you own, set android:exported to "true". -->
    <receiver android:name=".MyBroadcastReceiver" android:exported="false">
        <intent-filter>
            <action android:name="com.example.snippets.ACTION_UPDATE_DATA" />
        </intent-filter>
    </receiver>
    

    인텐트 필터는 수신기가 구독하는 브로드캐스트 작업을 지정합니다.

  2. BroadcastReceiver을 서브클래스화하고 onReceive(Context, Intent)을 구현합니다. 다음 예의 broadcast receiver는 브로드캐스트의 콘텐츠를 로깅하고 표시합니다.

    Kotlin

    class MyBroadcastReceiver : BroadcastReceiver() {
    
        @Inject
        lateinit var dataRepository: DataRepository
    
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == "com.example.snippets.ACTION_UPDATE_DATA") {
                val data = intent.getStringExtra("com.example.snippets.DATA") ?: "No data"
                // Do something with the data, for example send it to a data repository:
                dataRepository.updateData(data)
            }
        }
    }
    

    자바

    public static class MyBroadcastReceiver extends BroadcastReceiver {
    
        @Inject
        DataRepository dataRepository;
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Objects.equals(intent.getAction(), "com.example.snippets.ACTION_UPDATE_DATA")) {
                String data = intent.getStringExtra("com.example.snippets.DATA");
                // Do something with the data, for example send it to a data repository:
                if (data != null) { dataRepository.updateData(data); }
            }
        }
    }
    

앱이 설치될 때 시스템 패키지 관리자가 수신자를 등록합니다. 그러면 리시버가 앱의 별도 진입점이 되므로 앱이 실행되고 있지 않으면 시스템이 앱을 시작하고 브로드캐스트를 전송할 수 있습니다.

시스템은 수신하는 각 브로드캐스트를 처리하기 위해 새 BroadcastReceiver 구성요소 객체를 만듭니다. 이 객체는 onReceive(Context, Intent) 호출 기간 동안만 유효합니다. 코드가 이 메서드에서 반환되면 시스템은 구성요소가 더 이상 활성 상태가 아닌 것으로 간주합니다.

프로세스 상태에 미치는 영향

BroadcastReceiver의 작동 여부는 포함된 프로세스에 영향을 미치며, 이는 시스템 종료 가능성을 변경할 수 있습니다. 포그라운드 프로세스가 수신자의 onReceive() 메서드를 실행합니다. 시스템은 심각한 메모리 압력 상태를 제외하고 프로세스를 실행합니다.

시스템은 onReceive() 후에 BroadcastReceiver를 비활성화합니다. 수신자 호스트 프로세스의 중요도는 앱 구성요소에 따라 다릅니다. 해당 프로세스가 매니페스트 선언 리시버만 호스팅하는 경우 시스템은 onReceive() 후에 이를 종료하여 다른 더 중요한 프로세스의 리소스를 확보할 수 있습니다. 이는 사용자가 상호작용한 적이 없거나 최근에 상호작용하지 않은 앱에서 흔히 발생합니다.

따라서 broadcast receiver는 장기 실행 백그라운드 스레드를 시작해서는 안 됩니다. 시스템은 onReceive() 이후 언제든지 프로세스를 중지하여 메모리를 회수하고 생성된 스레드를 종료할 수 있습니다. 프로세스를 활성 상태로 유지하려면 시스템이 프로세스가 계속 작동 중임을 알 수 있도록 JobScheduler를 사용하여 수신기에서 JobService를 예약하세요. 자세한 내용은 백그라운드 작업 개요를 참고하세요.

브로드캐스트 전송

Android에서는 앱이 브로드캐스트를 전송하는 두 가지 방법을 제공합니다.

  • sendOrderedBroadcast(Intent, String) 메서드는 한 번에 하나의 수신기에 브로드캐스트를 전송합니다. 각 수신기는 차례로 실행되므로 다음 수신기에 결과를 전파할 수 있습니다. 또한 다른 수신기에 도달하지 않도록 브로드캐스트를 완전히 중단할 수 있습니다. 동일한 앱 프로세스 내에서 리시버가 실행되는 순서를 제어할 수 있습니다. 이렇게 하려면 일치하는 인텐트 필터의 android:priority 속성을 사용하세요. 우선순위가 동일한 수신기는 임의의 순서로 실행됩니다.
  • sendBroadcast(Intent) 메서드는 정의되지 않은 순서로 모든 수신자에게 브로드캐스트를 전송합니다. 이를 일반 브로드캐스트라고 합니다. 이는 더 효율적이지만 수신기가 다른 수신기의 결과를 읽거나 브로드캐스트에서 수신한 데이터를 전파하거나 브로드캐스트를 중단할 수 없음을 의미합니다.

다음 코드 스니펫은 인텐트를 만들고 sendBroadcast(Intent)를 호출하여 브로드캐스트를 전송하는 방법을 보여줍니다.

Kotlin

val intent = Intent("com.example.snippets.ACTION_UPDATE_DATA").apply {
    putExtra("com.example.snippets.DATA", newData)
    setPackage("com.example.snippets")
}
context.sendBroadcast(intent)

자바

Intent intent = new Intent("com.example.snippets.ACTION_UPDATE_DATA");
intent.putExtra("com.example.snippets.DATA", newData);
intent.setPackage("com.example.snippets");
context.sendBroadcast(intent);

방송 메시지는 Intent 객체로 래핑됩니다. 인텐트의 action 문자열은 앱의 Java 패키지 이름 구문을 제공하고 브로드캐스트 이벤트를 고유하게 식별해야 합니다. putExtra(String, Bundle)를 사용하여 인텐트에 추가 정보를 첨부할 수 있습니다. 인텐트에서 setPackage(String)를 호출하여 동일한 조직의 앱 집합으로 브로드캐스트를 제한할 수도 있습니다.

권한으로 브로드캐스트 제한

권한을 사용하면 특정 권한을 보유한 앱 집합으로 브로드캐스트를 제한할 수 있습니다. 브로드캐스트의 발신자 또는 수신자에 제한을 적용할 수 있습니다.

권한이 있는 브로드캐스트 보내기

sendBroadcast(Intent, String) 또는 sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)를 호출할 때 권한 매개변수를 지정할 수 있습니다. 매니페스트에서 <uses-permission> 태그를 사용하여 이 권한을 요청한 수신기만 브로드캐스트를 수신할 수 있습니다. 권한이 위험한 경우 수신기가 브로드캐스트를 수신하기 전에 권한을 부여해야 합니다. 예를 들어 다음 코드는 권한이 있는 브로드캐스트를 전송합니다.

Kotlin

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)

자바

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);

브로드캐스트를 수신하려면 수신 앱이 다음과 같이 권한을 요청해야 합니다.

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

BLUETOOTH_CONNECT과 같은 기존 시스템 권한을 지정하거나 <permission> 요소를 사용하여 맞춤 권한을 정의할 수 있습니다. 일반적인 권한 및 보안에 관한 자세한 내용은 시스템 권한을 참고하세요.

권한으로 브로드캐스트 수신

브로드캐스트 수신기를 등록할 때 권한 매개변수를 지정하면(매니페스트에서 registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) 또는 <receiver> 태그 사용) 매니페스트에서 <uses-permission> 태그를 사용하여 권한을 요청한 브로드캐스터만 수신기에 인텐트를 전송할 수 있습니다. 권한이 위험한 경우 브로드캐스터에게도 권한이 부여되어야 합니다.

예를 들어 수신 앱에 다음과 같이 매니페스트 선언 수신기가 있다고 가정해 보겠습니다.

<!-- If this receiver listens for broadcasts sent from the system or from
     other apps, even other apps that you own, set android:exported to "true". -->
<receiver
    android:name=".MyBroadcastReceiverWithPermission"
    android:permission="android.permission.ACCESS_COARSE_LOCATION"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.snippets.ACTION_UPDATE_DATA" />
    </intent-filter>
</receiver>

또는 수신 앱에 다음과 같은 컨텍스트 등록 수신기가 있습니다.

Kotlin

ContextCompat.registerReceiver(
    context, myBroadcastReceiver, filter,
    android.Manifest.permission.ACCESS_COARSE_LOCATION,
    null, // scheduler that defines thread, null means run on main thread
    receiverFlags
)

자바

ContextCompat.registerReceiver(
        context, myBroadcastReceiver, filter,
        android.Manifest.permission.ACCESS_COARSE_LOCATION,
        null, // scheduler that defines thread, null means run on main thread
        receiverFlags
);

그런 다음 이러한 수신기에 브로드캐스트를 전송하려면 전송 앱이 다음과 같이 권한을 요청해야 합니다.

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

동일한 프로세스 내에서 브로드캐스트 방지

브로드캐스트는 서로 다른 앱 간 또는 시스템과 앱 간에 메시지를 전송하기 위한 프로세스 간 통신 (IPC) 메커니즘으로 설계되었습니다. 모든 수신기가 브로드캐스트를 전송한 것과 동일한 프로세스에서 실행되는 브로드캐스트인 자체 브로드캐스트를 전송하는 것은 매우 비효율적이고 불필요한 시스템 오버헤드를 생성하므로 적극 권장되지 않습니다.

앱이 자체적으로 브로드캐스트를 전송하는 두 가지 일반적인 시나리오는 다음과 같습니다.

  • 동일한 프로세스의 구성요소 간 통신: 예를 들어 활동, 프래그먼트, 서비스 또는 백그라운드 스레드 간에 이벤트나 데이터를 전달합니다. 브로드캐스트를 전송하는 대신 관찰자 패턴 또는 반응형 스트림과 같은 표준 인프로세스 통신 메커니즘을 사용하세요.

    • Kotlin Flow (SharedFlow 및 StateFlow): 앱의 코루틴과 구성요소 전반에서 이벤트 스트림이나 상태 업데이트를 방출하고 관찰하는 Kotlin의 최신 관용적 솔루션입니다.
    • 공유 ViewModel: 동일한 활동 내에서 다양한 UI 구성요소 (예: 프래그먼트 또는 컴포저블) 간의 데이터 및 이벤트 공유를 지원합니다.
    • 콜백 및 리스너: 표준 인터페이스 콜백 또는 함수 참조가 구성요소 간에 직접 전달되거나 중앙 저장소 또는 컨트롤러에 등록됩니다.
  • 시스템 이벤트, 작업 또는 알람 처리: 예를 들어 예약된 작업, 알람 또는 시스템 콜백을 수신한 다음 브로드캐스트를 전송하여 실제 작업을 트리거합니다. 브로드캐스트를 전송하는 대신 해당 작업 (예: JobService 또는 WorkManager 작업자), 알람 핸들러 또는 시스템 콜백 구성요소 내에서 직접 작업을 완료하거나 앱의 비즈니스 로직 클래스에 직접 위임하세요.

Android 17 QPR2 이상을 실행하는 기기에서 시스템은 전송 프로세스에 반환하여 더 효율적으로 자체 브로드캐스트를 전송합니다. 전송 프로세스는 기본 스레드에서 자체 수신기에 브로드캐스트를 전송합니다. 자체 브로드캐스트를 전송해도 프로세스의 중요도가 높아지지 않으며 캐시된 프로세스가 정지되지 않으므로 앱을 계속 실행하거나 백그라운드에서 작업을 실행하기 위해 자체 브로드캐스트에 의존하지 마세요. 이 최적화가 적용되더라도 자체 브로드캐스트는 진행 중인 통신 메커니즘보다 효율성이 떨어지므로 대신 이러한 대안을 사용하세요.

앱 구성요소 간 통신 설계에 관한 자세한 내용은 앱 아키텍처 가이드를 참고하세요.

보안 고려사항

브로드캐스트를 전송하고 수신할 때 고려해야 할 몇 가지 보안 사항은 다음과 같습니다.

  • 매니페스트에 동일한 브로드캐스트를 수신하도록 등록된 앱이 많으면 시스템에서 많은 앱을 실행하여 기기 성능과 사용자 환경에 모두 상당한 영향을 미칠 수 있습니다. 이를 방지하려면 매니페스트 선언보다 컨텍스트 등록을 사용하는 것이 좋습니다. 경우에 따라 Android 시스템 자체에서 컨텍스트 등록 수신기의 사용을 강제합니다. 예를 들어 CONNECTIVITY_ACTION 브로드캐스트는 컨텍스트 등록 수신자에게만 전달됩니다.

  • 암시적 인텐트를 사용하여 민감한 정보를 브로드캐스트하지 마세요. 브로드캐스트를 수신하도록 등록하면 모든 앱이 정보를 읽을 수 있습니다. 브로드캐스트를 수신할 수 있는 대상을 제어할 수 있는 방법은 다음과 같이 세 가지가 있습니다.

    • 브로드캐스트를 전송할 때 권한을 지정할 수 있습니다.
    • Android 4.0 (API 수준 14) 이상에서는 브로드캐스트를 전송할 때 setPackage(String)를 사용하여 패키지를 지정할 수 있습니다. 시스템은 패키지와 일치하는 앱 집합으로 브로드캐스트를 제한합니다.
  • 수신기를 등록하면 모든 앱이 잠재적으로 악의적인 브로드캐스트를 앱의 수신기로 보낼 수 있습니다. 앱이 수신하는 브로드캐스트를 제한하는 방법에는 여러 가지가 있습니다.

    • broadcast receiver를 등록할 때 권한을 지정할 수 있습니다.
    • 매니페스트 선언 수신기의 경우 매니페스트에서 android:exported 속성을 'false'로 설정할 수 있습니다. 리시버가 앱 외부 소스의 브로드캐스트를 수신하지 않습니다.
  • 브로드캐스트 작업의 네임스페이스는 전역입니다. 작업 이름과 기타 문자열이 소유한 네임스페이스에 작성되어 있는지 확인합니다. 그렇지 않으면 다른 앱과 의도치 않게 충돌할 수 있습니다.

  • 수신기의 onReceive(Context, Intent) 메서드는 기본 스레드에서 실행되므로 빠르게 실행되고 반환되어야 합니다. 장기 실행 작업을 실행해야 하는 경우 스레드를 생성하거나 백그라운드 서비스를 시작할 때 주의해야 합니다. 시스템이 onReceive()가 반환된 후 전체 프로세스를 종료할 수 있기 때문입니다. 자세한 내용은 프로세스 상태에 미치는 영향을 참고하세요. 장기 실행 작업을 실행하려면 다음을 권장합니다.

    • 수신기의 onReceive() 메서드에서 goAsync()를 호출하고 BroadcastReceiver.PendingResult를 백그라운드 스레드에 전달합니다. 이렇게 하면 onReceive()에서 반환된 후에도 브로드캐스트가 활성 상태로 유지됩니다. 하지만 이 방법을 사용하더라도 시스템에서는 브로드캐스트를 매우 빠르게 (10초 미만) 종료할 것으로 예상합니다. 기본 스레드의 글리치 현상을 방지하기 위해 작업을 다른 스레드로 이동할 수 있습니다.
    • JobScheduler로 작업 예약 자세한 내용은 지능형 작업 예약을 참고하세요.
  • 사용자 환경이 거슬리므로 특히 수신기가 두 개 이상인 경우 브로드캐스트 수신기에서 활동을 시작하지 마세요. 대신 알림을 표시하는 것이 좋습니다.