[Android] 브로드캐스트 리시버 (Broadcast Receiver)를 이용해 특정 행동에 대하여 명령 실행하기

 

브로드캐스트(Broadcast) 와 브로드캐스트 리시버(Broadcast Receiver)

안드로이드 앱에 있어서 중요한 기능 중 하나인 브로드캐스트 (Broadcast)는 이름 그대로 방송을 한다는 의미이다. 전화를 받거나 화면을 껐다 켜거나 스마트폰을 종료했다가 다시 전원을 켜는 등 특정 행동을 했을 때 Intent를 통해 이런 행동이 발송되어지며 브로드캐스트 리시버 (Broadcast Receiver)가 수신하여 앱에 행동을 전달하게 된다.

쉽게 말해서, "전원을 켜면 이 앱을 실행하라" 라던가 "화면을 켜면 앱에서 실행 중인 음악을 종료하라" 등의 명령을 Broadcast를 이용해 내릴 수 있다는 것이다. 

앱이 Broadcast를 받기 위해서는 위에서 언급했던 Broadcast Receiver를 등록해야 하는데, Receiver를 등록하는 방법에는 AndroidManifest.xml에 선언하여 앱을 설치했을 때 Receiver를 등록하는 정적 Broadcast Receiver와 앱이 실행 중일 때 Receiver를 실행하는 동적 Broadcast Receiver가 있다. 



정적 Broadcast Receiver와 동적 Broadcast Receiver의 차이

정적 리시버는 앱이 종료가 된 후에도 브로드캐스트를 받으면 해당 방송에 대하여 사전에 세팅했던 명령을 실행하게 되지만, 동적 리시버는 앱이 실행되고 있는 중에만 방송을 받아 명령을 실행할 수 있다. 가령 스마트폰이 꺼진다면 실행 중인 모든 앱이 종료될텐데, 이 때 전원을 켜는 행동에 대하여 앱을 실행시키는 정적 리시버를 등록해 놓으면 앱이 종료된 후에도 스마트폰이 켜지고 앱이 자동으로 실행되게 된다.

동적 리시버는 앱이 실행되는 중에만 브로드캐스트를 받을 수 있으므로 당연히 전원을 껐다 켜면 리시버가 동작을 하지 않으며 굳이 스마트폰을 끌 필요도 없이 앱을 강제로 종료하기만 해도 리시버가 동작을 하지 않는다.

그렇다면 정적 리시버를 등록하는 것이 훨씬 유리하지 않은가? 굳이 동적 리시버를 이용하는 이유가 무엇인가? (물론 정적 리시버를 사용하면 원할 때 리시버를 해제할 수 없으며, 동적 리시버를 사용하면 원할 때 리시버를 해제할 수 있으니 이럴 경우에는 동적 리시버가 유리하기는 하다.) 라고 묻는다면, 안드로이드 8.0 (Oreo) 부터 몇몇을 제외하고 나머지 브로드캐스트 리시버를 AndroidManifrest.xml에 등록할 수 없게 되었다. 리소스 제한 등이 이유라고 한다. 아래의 링크에서 예외 리시버를 확인할 수 있다.

https://developer.android.com/guide/components/broadcast-exceptions



정적 Broadcast Receiver 등록

아래와 같이 Broadcast Receiver를 상속받는 클래스를 하나 생성하자.


package com.example.brawlkat;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class kat_BroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

    }
}


AndroidManifest.xml에 <receiver>를 등록하자. 이 때 <intent-filter> 안에 수신 받을 브로드캐스트를 넣어주어야 한다. 브로드캐스트를 여 <intent-filter>를 여러 개 생성하여 브로드캐스트를 각각 넣어줘도 되고, 하나의 <intent-filter>에 여러 개의 브로드캐스트를 넣어줘도 좋다.

<receiver android:name=".kat_BroadcastReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>



동적 Broadcast Receiver 등록

아래의 코드를 보자.



private void RegisterBroadcastReceiver(){

    if(broadcastReceiver != null) return;

    final IntentFilter ScreenOnIntentFilter = new IntentFilter();
    final IntentFilter ScreenOffIntentFilter = new IntentFilter();

    ScreenOnIntentFilter.addAction(BROADCAST_MASSAGE_SCREEN_ON);
    ScreenOffIntentFilter.addAction(BROADCAST_MASSAGE_SCREEN_OFF);

    broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if(BROADCAST_MASSAGE_SCREEN_ON.equals(intent.getAction())){
                isBroadcastThreadStop = false;
                checkThread.run();
            }
            else if(BROADCAST_MASSAGE_SCREEN_OFF.equals(intent.getAction())){
                isBroadcastThreadStop = true;
            }
        }
    };

    registerReceiver(broadcastReceiver, ScreenOnIntentFilter);
}

private void UnregisterBroadcastReceiver(){

    if(broadcastReceiver != null){
        unregisterReceiver(broadcastReceiver);
        broadcastReceiver = null;
    }
}

정적으로 브로드캐스트를 등록하는 방법을 코딩으로 바꾼 것이라고 볼 수 있는데, AndroidManifest.xml 파일에서 설정한 <Intent-filter>를 직접 생성하여 필터에 액션을 추가해주고, BroadcastReceiver를 생성해주고 registerReceiver를 이용해 브로드캐스트를 등록해주고 RegisterBroadcastReceiver 메서드를 원하는 곳에 넣어주면 되겠다. 참고로 여기서 BROADCAST_MASSAGE_SCREEN_ON과 OFF는 각각 Intent.ACTION_SCREEN_ON과 OFF다.

UnregisterBroadcastReceiver는 생성한 브로드캐스트 리시버를 unregisterReceiver로 연결 해제한 후 null로 제거하면 된다. 보통 onDestroy()에 해당 메서드를 넣게 된다.

Intent.ACTION_SCREEN_ON과 같이 기본 제공되는 action 외에도 사용자가 직접 action을 만들어 넣어줄 수도 있다. 이 action을 이용하여 브로드캐스트를 송, 수신할 수 있게 만들 수 있는데, 아래의 코드를 보자. 


package com.example.test;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setAction("com.example.test.MY_ACTION");
                sendBroadcast(intent);
            }
        });

        myBroadcastReceiver receiver = new myBroadcastReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.example.test.MY_ACTION");
        registerReceiver(receiver, filter);
    }

    private class myBroadcastReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction().equals("com.example.test.MY_ACTION")){
                Toast.makeText(getApplicationContext(), intent.getAction(), Toast.LENGTH_SHORT).show();
            }
        }
    }
}

여기서 IntentFilter에 "com.example.test.MY_ACTION"이라는 액션을 넣어줬다. 액션의 명명은 보통 com.example.패키지명.액션명으로 짓는다고 하는데, 그냥 "MY_ACTION"으로 이름을 설정하고 돌려봐도 잘 되는 것을 보면 아마 다른 앱에서 해당 브로드캐스트를 받을 때를 생각해서 프로젝트에 연관이 있는 이름으로 짓는 것이 아닌가 싶다.





마치며..

브로드캐스트는 매우 유용한 기능이다. 자신의 앱이 스마트폰이 켜졌을 때 같이 실행되게 만들고 싶을 때 정적 리시버를 이용해 구현할 수 있고, Foreground Service에서 어떤 작업을 진행하고 있을 때 스마트폰의 화면이 꺼졌을 때 작업을 중지하고, 화면이 켜졌을 때 작업을 재시작하게 만들고 싶다면 동적 리시버를 이용해 구현할 수 있다. (이 때는 Service를 상속 받은 클래스에 리시버를 등록하면 된다.) 안드로이드에서 많이 사용하는 기능인 만큼 잘 공부해 능숙하게 사용할 수 있도록 하면 좋을 것 같다.


댓글

댓글 쓰기