ANDROID/Android 앱 프로그래밍

[Android] 브로드캐스트(Broadcast)

주 녕 2021. 6. 11. 04:34
728x90

모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다. 

 

브로드캐스트 수신자(Broadcast Receiver)

브로드캐스팅(Broadcasting) : 메시지를 여러 객체에 전달하는 것

→ 안드로이드는 여러 앱 구성 요소에 메시지를 전달할 때 브로드캐스팅을 사용함 (앱과 앱 사이, 앱과 시스템 사이의 이벤트 전달)

  • 안드로이드 4대 앱 구성 요소 중 하나
  • AndroidManifest 파일에 등록해야 시스템이 알 수 있고 화면도 없게 됨
  • 매니페스트 등록 방식이 아닌 소스코드에서 registerReceiver() 메서드를 사용해서 시스템에 등록할 수 있음

 

브로드캐스트 수신자 등록하고 사용하기

<manifest...>

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

    <application
        ...>
        <activity android:name=".SmsActivity"/>

        <receiver
            android:name=".SmsReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
  1. 브로드캐스트 수신자는 <receiver> 태그 안에 <intent-filter> 태그를 넣어 어떤 인텐트를 받을 것인지 지정
    • <intent-filter> 태그 안에 <action> 태그의 name 속성 값으로 android.provider.Telephony.SMS_RECEIVED
    • SMS 메시지가 들어간 인텐트를 구분하기 위한 액션 정보임
  2. SMS을 수신하기 위해서는 RECEIVE_SMS 권한이 필요함
    • <uses-permission> 태그를 추가하여 위험 권한을 추가함
    • build.gradle(Module:app)의 dependencies에 간단하게 위험 권한을 추가할 수 있는 외부 라이브러리 (com.github.gedroSG94:AutoPermissions:1.0.3)와 maven url (https://jitpack.io)를 싱크함

 

MainActivity.java

  • AutoPermissionsListener 인터페이스를 상속 받아 구현함
    • AndroidManifest에서 필요한 권한들을 추가하면 최초 실행 시 자동으로 권한 요청을 띄우고 승인 결과를 알 수 있음 
    • onDenied → 거절, onGranted → 승인
public class MainActivity extends AppCompatActivity implements AutoPermissionsListener {

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

        AutoPermissions.Companion.loadAllPermissions(this, 101);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        AutoPermissions.Companion.parsePermissions(this, requestCode, permissions, this);
    }

    @Override
    public void onDenied(int i, String[] permissions) {
        Toast.makeText(this, "permission denied : "+permissions.length, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onGranted(int i, String[] permissions) {
        Toast.makeText(this, "permission granted : "+permissions.length, Toast.LENGTH_SHORT).show();
    }
}

 

위험권한설정.gif

 

SmsActivity.java

전달받은 메세지를 띄우는 액티비티

public class SmsActivity extends AppCompatActivity {

    TextView textView1, textView2, textView3;

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

        textView1 = findViewById(R.id.edittext_sender);
        textView2 = findViewById(R.id.edittext_content);
        textView3 = findViewById(R.id.edittext_time);

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });

        Intent passedIntent = getIntent();
        processIntent(passedIntent);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        processIntent(intent);
        super.onNewIntent(intent);
    }

    private void processIntent(Intent intent) {
        if (intent != null) {
            String sender = intent.getStringExtra("sender");
            String contents = intent.getStringExtra("contents");
            String receivedDate = intent.getStringExtra("receivedDate");

            textView1.setText(sender);
            textView2.setText(contents);
            textView3.setText(receivedDate);
        }
    }
}

 

SmsReceiver.java

  • SMS를 받으면 onReceive() 메서드가 자동 호출됨
  • 파라미터로 전달되는 인텐트(Intent) 객체 안에 SMS 데이터가 들어 있음
  • 인텐트 객체 안에 번들(Bundle) 객체 안에 부가 데이터가 들어 있음
public class SmsReceiver extends BroadcastReceiver {
    public static final String TAG = "SmsReceiver";

    public SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive() 메서드 호출됨.");

        // 인텐트에서 Bundle 객체를 가져옴
        Bundle bundle = intent.getExtras();
        SmsMessage[] messages = parseSmsMessage(bundle);

        if (messages != null && messages.length > 0) {
            String sender = messages[0].getOriginatingAddress();
            Log.i(TAG, "SMS sender : "+sender);

            String contents = messages[0].getMessageBody();
            Log.i(TAG, "SMS contents : "+contents);

            Date receivedDate = new Date(messages[0].getTimestampMillis());
            Log.i(TAG, "SMS received date : "+receivedDate.toString());

            // 새로운 화면을 띄우기 위한 메서드 호출
            sendToActivity(context, sender, contents, receivedDate);
        }
    }

    private SmsMessage[] parseSmsMessage(Bundle bundle) {
        // Bundle 객체의 부가 데이터 중에서 pdus 가져옴
        Object[] objects = (Object[]) bundle.get("pdus");
        SmsMessage[] messages = new SmsMessage[objects.length];

        int smsCount = objects.length;
        for(int i = 0; i < smsCount; i++) {
            // OS 버전에 따라 다른 방식으로 호출
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                String format = bundle.getString("format");
                messages[i] = SmsMessage.createFromPdu((byte[]) objects[i], format);
            } else {
                messages[i] = SmsMessage.createFromPdu((byte[]) objects[i]);
            }
        }

        return messages;
    }

    private void sendToActivity(Context context, String sender, String contents, Date receivedDate) {
        Intent intent = new Intent(context, SmsActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

        intent.putExtra("sender", sender);
        intent.putExtra("contents", contents);
        intent.putExtra("receivedDate", format.format(receivedDate));

        context.startActivity(intent);
    }
}
  • 받은 SMS 내용을 인텐트로 전달함 → sendToActivity()
    • 브로드캐스트 수신자는 화면이 없기 때문에 인텐트의 플래그FLAG_ACTIVITY_NEW_TASK를 추가해야 함
    • 이미 메모리에 만든 SmsActivity가 있을 때 액티비티를 중복 생성하지 않도록 FLAG_ACTIVITY_SINGLE_TOP을 추가함
  • 매니페스트 파일에 <receiver> 태그로 등록하지 않고 registerReceiver() 메서드를 사용해 등록할 수 있음
    • 소스 파일에서 등록하면 화면이 사용자에게 보일 때만 브로드캐스트 수신자에서 메시지를 받도록 할 수 있음

메시지수신.gif

 

 

브로드캐스트 수신자 정리

  • 단말에서 SMS 문자를 받았을 때, 텔레포니(Telephony) 모듈이 처리함
    • 이렇게 처리된 정보는 인텐트에 담겨 브로드캐스팅 방식으로 다른 앱에 전달
    • 인텐트를 전달 받았을 때 onReceive() 메서드가 자동 호출
    • 위의 예제에서는 SmsReceiver 객체에서 인텐트 안의 데이터를 확인하고 SmsActivity로 인텐트를 전달
  • 앱 A가 실행되어 있지 않아도 앱 A가 원하는 브로드캐스트 메세지가 도착하면 앱 B를 실행하고 있는 도중에 A가 실행될 수 있음
    • 동일한 SMS 수신 앱 여러 개를 수정하여 만들어 설치하면 어느 앱에서 생긴 오류인지 찾기 어려움
    • 새 개발 버전의 앱을 만들었을 경우에는 구 개발 버전의 앱을 삭제하는 것이 좋음

 

 

*인텐트(Intent)와 플래그(flag), 부가 데이터(Extra data)에 관한 내용은 아래 포스팅 참고!

 

[Android] 인텐트(Intent)

모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다. 인텐트(Intent) 앞에서 본 인텐트는 작업을 수행하기 위해 사용되는 명령 or 데이터를 전달하는 기능 → 인텐트를 만든

junyoung-developer.tistory.com

 

[Android] 플래그(flag)와 부가 데이터(Extra Data)

모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다. 액티비티로 만든 화면이 한 번 메모리에 만들어졌는데도 계속 startActivity()나 startActivityForResult() 메서드를 여러 번 호

junyoung-developer.tistory.com

728x90