프로그래밍(programming)/안드로이드(android)

[210524] 안드로이드 방송 수신자 / 브로드 캐스트 리시버(Brodcast Receiver) - SMS 문자 받아서 출력

하빌리즘 2021. 5. 24. 17:45
반응형

안드로이드 앱의 4대 구성 요소를 컴포넌트라고 부르는데, '컴포넌트(Component)' 중 하나인

'서비스(Service)'는 이전 시간에 알아보았고, 이번에는 또 다른 4대 컴포넌트인

'방송수신자/브로드 캐스트 리시버(Brodcast Receiver)'를 알아보겠다.

 

안드로이드에서 '방송(Brodcasting)' 이란 메시지를 여러 객체에 전달하는 것을 말한다.

즉, 이런 방송을 받고 싶다면 '방송 수신자(Brodcast Receiver)' 를 만들어서 앱에 등록해주면 된다. 

 

 

 

앱을 사용하다보면 사건이 발생하거나 상태가 변경되는 등 많은 이벤트들이 발생하는데, 

방송 수신자는 이것들을 시스템에게 통지받기를 원한다.

 

예를 들어, 다른 사람으로부터 문자 메시지를 받았을 때 이 문자 메시지를 SMS 앱에 알려줘야 한다면

브로드 캐스팅 방식으로 전달하면 된다. 이렇게 전달하게 되면 단말 전체에 적용이 되기 때문에

이런 방식을 '글로벌 이벤트(Global Event)' 라고 부른다.

 

즉, '문자 메시지가 도착했습니다.", "전화가 왔습니다." 와 같은 것이다.

 

위에서 언급했다시피, 방송 수신자도 인텐트나 서비스와 마찬가지로 앱의 구성 요소이기 때문에

매니페스트에 반드시 등록을 해줘서 시스템이 알게 해줘야한다.

그리고 원하는 메시지만 받으려면 인텐트 필터를 사용해서 시스템에 등록해주면 된다.

(모든 메시지는 인텐트 안에 넣어서 전달되므로)

 

이제 직접 방송 수신자를 만들고 SMS 문자 메시지를 받아서 발신자 번호, 메시지 내용, 수신 시간을

출력하는 앱을 만들어 보겠다.

 

우선 매니페스트에 등록부터 하도록 하겠다.

 

1
2
3
4
5
6
7
8
 <receiver android:name=".SmsReceiver"
            android:enabled="true"
            android:exported="true" >
 
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
cs

그리고 SMS를 수신하려면 권한도 필요하다.

 

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

 

그 다음으로는 BrodcastReceiver 클래스를 상속 받는 새로운 클래스를 생성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class SMSReceiver extends BroadcastReceiver {
 
    private static final String TAG = "SMSReceiver"// logt 를 입력하면 자동으로 태그 생성
 
    public SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 한국식 날짜 표기 형식으로 변경
 
    @Override // 메시지를 받으면 onReceive()가 자동 호출됨
    public void onReceive(Context context, Intent intent) { // 인텐트 안에는 문자 메시지 데이터가 들어가있음 -> 시스템이 Telephony 모듈에서 넣어줌
        Log.i(TAG, "문자 메시지를 받았습니다. " );
 
        Bundle bundle = intent.getExtras(); // 인텐트에서 번들 객체 가져오기
        SmsMessage[] smsMessage = mySmsMessage(bundle); // mySmsMessage() 메소드로 SMS 메시지 객체 생성
 
        if(smsMessage != null && smsMessage.length > 0) { // 메시지가 널 값이 아니고, 메시지의 길이가 0보다 크면
 
            String sender = smsMessage[0].getOriginatingAddress(); // 발신자 번호 가져오기
            Log.i(TAG, "발신자 번호 : " + sender);
 
            String content = smsMessage[0].getMessageBody(); // 문자 내용 가져오기
            Log.i(TAG, "문자 내용 : " + content);
 
            Date date = new Date(smsMessage[0].getTimestampMillis()); // 수신 시간 가져오기
            Log.i(TAG, "수신 시간 : " + date.toString());
            sendToMain(context, sender, content, date); // 메인 액티비티로 인텐트를 보내기 위한 메소드
        }
    }
 
    private SmsMessage[] mySmsMessage(Bundle bundle) { // SmsMessage 라는 자료형으로 된 배열 객체 리턴
        Object[] objects = (Object[]) bundle.get("pdus"); // Bundle 객체에 들어가 있는 부가 데이터 중에서 pdus 가져오기 (pdus에는 SMS데이터와 관련된 내용들이 들어가있음)
        SmsMessage[] smsMessages = new SmsMessage[objects.length];
 
        int smsCount = objects.length;
 
        for(int i = 0; i < smsCount; i++) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 개인 단말의 안드로이드 버전이 버전 6보다 크면
                String format = bundle.getString("format");
                // 인텐트 객체 안에 부가 데이터로 들어가있는 SMS 데이터를 확인하려면 SmsMessage 클래스의 createFromPdu 메소드로 Smsmessage 객체로 리턴하면 SMS 데이터를 확인할 수 있음
                smsMessages[i] = SmsMessage.createFromPdu((byte[]) objects[i], format);
            } else { // 6이전 버전이면
                smsMessages[i] = SmsMessage.createFromPdu((byte[]) objects[i]);
            }
        }
        return smsMessages; // Bundle 객체의 인텐트 안에 부가 데이터로 들어가있던 데이터를 이용하여 SmsMessage 객체로 변환해서 던져줌
   }
 
   private void sendToMain(Context context, String sender, String content, Date date) {
        Intent intent = new Intent(context, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        
        intent.putExtra("sender", sender);
        intent.putExtra("content", content);
        intent.putExtra("date", dateformat.format(date));
        
        context.startActivity(intent);
   }
}
 
cs
 

조금 복잡해보일 수 있으나 mySmsMessage() 메소드는 SMS 데이터를 확인할 수 있도록 안드로이드 API에 정해둔 

코드를 사용하므로 수정될 일이 거의 없기 때문에 다른 앱을 만들 때에도 재사용을 할 수가 있다.

 

이렇게 코드를 작성하고 단말로 문자 메시지를 보내게 되면

문자 메시지를 받은 후 로그창

로그창에 다음과 같이 받은 내용들이 출력된다.

 

이것을 로그창이 아닌 액티비티 화면에 출력하고 싶으면 인텐트를 이용해서 데이터를 전달하는 방식으로 하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MainActivity extends AppCompatActivity {
 
    TextView senderTv, contentTv, dateTv;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        senderTv = (TextView)findViewById(R.id.senderTv);
        contentTv = (TextView)findViewById(R.id.contentTv);
        dateTv = (TextView)findViewById(R.id.dateTv);
 
        Intent passedIntent = getIntent(); // 전달받은 인텐트를 처리하도록
        processIntent(passedIntent); // 인텐트 처리 메소드 호출
    }
 
    @Override
    protected void onNewIntent(Intent intent) { // 액티비티가 이미 만들어져 있는 상태에서 전달 받은 인텐트를 처리하도록 설정
        processIntent(intent);
 
        super.onNewIntent(intent);
    }
 
    private void processIntent(Intent intent) { // 인텐트 처리 메소드, 인텐트 객체 안에 들어 있는 데이터를 꺼내서 TextView 에 설정
        if (intent != null) { // 인텐트 널 체크, 인텐트가 널이 아니면
 
            // 전달 받은 인텐트에서 발신자 번호, 메시지 내용, 수신 시간 값들을 가져오기
            String sender = intent.getStringExtra("sender");
            String content = intent.getStringExtra("content");
            String date = intent.getStringExtra("date");
 
            // 가져온 값들을 TextView에 출력
            senderTv.setText("발신자 번호 : " + sender);
            contentTv.setText(content);
            dateTv.setText("수신 시간 : " + date);
        }
    }
}
cs

초기 화면

 

로그창 때와 마찬가지로 단말로 문자 메시지를 보내게 되면 관련 내용이 로그창과 함께 액티비티 화면에 출력된다.

문자 받은 후 최종 화면

 

피드백은 언제나 환영입니다. 

반응형