티스토리 뷰

Android

8. 브로드캐스트 수신자 : Broadcast Receiver

알 수 없는 사용자 2018. 10. 6. 21:12


이번 포스팅에서는

안드로이드 4가지 구성요소 중

브로드캐스트 수신자에 대해 

알아보도록 하겠습니다.

4가지 구성요소인 만큼 액티비티나 서비스만큼

중요하기 때문에 꼭 알고 있어야 할 내용입니다.


================================================================


브로드캐스트 수신자 : Broadcast Receiver




1. Broadcast Receiver가 무엇일까?


직역으로 하면 방송 수신자라고 해석할 수 있습니다. 안드로이드에서 뜻하는 의미도 방송 수신자라고 봐도 무방합니다. 실제로 Broadcast Receiver는 시스템이나 앱 등에서 이벤트 발생시 방송을 해주는 개념입니다. 


여기서 Broadcast를 좀 더 깊게 생각해보겠습니다. 한국어로 번역하면 방송이라는 뜻입니다. 방송은 한 곳에서 여러 사람한테 동시에 메시지를 알려주는 것으로 생각할 수 있습니다.

이러한 방송이라는 의미가 안드로이드에서는 메시지를 여러 객체에 전달하는 것을 의미합니다. 

(여기서 사용한 메시지 단어는 sms와 같은 문자메시지를 의미하는 것이 아니라 누군가에 알리는 어떤 알림 자체를 의미합니다.) 

예를 들어 다른 사람으로부터 sms 문자메시지를 수신했다면 기본 앱으로 장착된 메시지 어플을 통해 알림이 올 것입니다. 하지만 만약 우리가 기본 앱을 제외한 다른 른 앱에게 sms 알림 메시지를 알려줄 필요가 있다면 이때 사용되는 것이 Broadcast Receiver입니다.



쉽게 말해 하나의 알림을 여러 곳에 뿌려주는 셈이죠.






2. Broadcast Receiver에서 어떤 것을 받을 수 있을까?


 ACTION_BOOT_COMPLETED

부팅이 끝났을 때 (RECEIVE_BOOT_COMPLETED 권한등록 필요)


ACTION_CAMERA_BUTTON

카메라 버튼이 눌렸을 때


ACTION_DATE_CHANGED

ACTION_TIME_CHANGED

폰의 날짜, 시간이 수동으로 변했을때 (설정에서 수정했을때)


ACTION_SCREEN_OFF

ACTION_SCREEN_ON

화면 on, off


ACTION_AIRPLANE_MODE_CHANGED

비행기 모드


ACTION_BATTERY_CHANGED

ACTION_BATTERY_LOW

ACTION_BATTERY_OKAY

배터리 상태변화


ACTION_PACKAGE_ADDED

ACTION_PACKAGE_CHANGED

ACTION_PACKAGE_DATA_CLEARED

ACTION_PACKAGE_INSTALL

ACTION_PACKAGE_REMOVED

ACTION_PACKAGE_REPLACED

ACTION_PACKAGE_RESTARTED

어플 설치/제거


ACTION_POWER_CONNECTED

ACTION_POWER_DISCONNECTED

충전 관련


ACTION_REBOOT

ACTION_SHUTDOWN

재부팅/종료


ACTION_TIME_TICK

매분마다 수신


android.provider.Telephony.SMS_RECEIVED

sms 수신 (RECEIVE_SMS 권한 필요)


출처: http://itmir.tistory.com/424 [미르의 IT 정복기]


이러한 액션 말고도 엄청나게 많은 액션이 있다고 합니다.







3. Broadcast Receiver 어떻게 사용해야 하지?


먼저 리시버를 사용하기 전에 리시버의 종류를 알아야 합니다.

리시버는 동적 리시버와 정적 리시버로 구분됩니다.


간단히 설명하자면 리시버를 사용하려면 AndroidManifest.xml 파일에 등록해주어야 하는데 이때 사용자가 직접  AndroidManifest.xml 파일에 등록해주냐 아니면 클래스 파일에서 제어하냐의 차이입니다.

정적 리시버가 전자의 경우로 사용자가 직접  AndroidManifest.xml 파일에 등록하여 리시버를 구현하는 형태인데 이 형태는 한 번 등록하면 해제할 수 없는 방식입니다.

동적 리시버는 후자의 경우로 클래스 파일에서 리시버를 등록하고 해제할 수 있는 형태이기 때문에 시스템이나 앱에 부하를 줄일 수 있습니다. 하지만 해제를 적절히 해주지 않는다면 메모리 릭이 발생할 수 있습니다.


등록된 리시버를 구현하면 원하는 브로드캐스트 메시지를 구별해야합니다. 

메시지는 인텐트 안에 넣어 전달되기 때문에 정적리시버의 경우 리시버를 등록할 때 intent filter를 이용하여 등록하면 됩니다.

만약 브로드캐스트 메시지가 인텐트에 넣어 전달되었을 때 브로드캐스트리시버를 상속하는 클래스에서는 자동으로 호출되는 onReceive() 메소드를 오버라이드 할 수 있기 때문에 onReceive() 안에 브로드캐스트 메시지가 전달되었을 때 작동할 기능을 정의하면 됩니다.


매니페스트 파일 or registerReceiver() 를 통한 리시버 등록 → 등록한 리시버에서 인텐트 필터 → 

onReceive() 메소드 정의 → (동적리시버) unregisterReceiver() 를 통한 리시버 해제







4. 리시버 테스트를 위한 SMS 리시버를 구현해보자.


SMS를 수신하여 받을 액티비티를 구현합니다. (SmsActivity, activity_sms.xml)



public class SmsActivity extends AppCompatActivity {

EditText editText;
EditText editText2;
EditText editText3;

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

editText = (EditText) findViewById(R.id.editText);
editText2 = (EditText) findViewById(R.id.editText2);
editText3 = (EditText) findViewById(R.id.editText3);

Intent paresIntent = getIntent();
processIntent(paresIntent);

}

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

super.onNewIntent(intent);
}

private void processIntent(Intent intent){
editText.setText(intent.getStringExtra("sender"));
editText2.setText(intent.getStringExtra("contents"));
editText3.setText(intent.getStringExtra("receivedDate"));
}
}


글쓴이와 내용 그리고 날짜까지 표현하는 액티비티로 구상하였습니다. 

리시버를 통해서 전달되는 것은 Intent이기 때문에 getIntent() 를 통해 전달되는 Intent를 받을 수 있습니다.

onNewIntent() 메소드는 이전 포스팅에서 설명한 것과 같이 이미 액티비티가 만들어져 있는 상황에서 처리하도록 정의해두었습니다.




② 브로드캐스트 리시버를 상속하는 클래스를 만들어 onReceive() 정의를 해줍니다.


public class SmsReceiver extends BroadcastReceiver {

private 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,"onReceived() 호출됨");

Bundle bundle = intent.getExtras();
SmsMessage[] messages = parseSmsMessage(bundle);

if(messages != null){
String sender = messages[0].getDisplayOriginatingAddress();
Log.d(TAG,sender);

String contents = messages[0].getMessageBody();
Log.d(TAG, contents);

Date receivedDate = new Date(messages[0].getTimestampMillis());
Log.d(TAG,receivedDate.toString());

sendToActivity(context, contents, sender, receivedDate);
}


}

private void sendToActivity(Context context, String contents, String sender, Date receivedDate){
Intent intent1 = new Intent(context,SmsActivity.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent1.putExtra("sender", sender);
intent1.putExtra("contents",contents);
intent1.putExtra("receivedDate",format.format(receivedDate));

context.startActivity(intent1);
}

private SmsMessage[] parseSmsMessage(Bundle bundle){
Object[] objs = (Object[])bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[objs.length];
for(int i=0; i<objs.length;i++) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String format = bundle.getString("format");
messages[i] = SmsMessage.createFromPdu((byte[]) objs[i], format);
}else {
messages[i] = SmsMessage.createFromPdu((byte[]) objs[i]);
}
}

return messages;
}
}


- parseSmsMessage()

 :  전달받은 문자 내용을 파싱하고 메시지를 복원하는 코드입니다.


- sendToActivity()

 : 리시버로 전달된 인텐트의 내용을 다시 인텐트에 담아 띄우고자 하는 액티비티로 넘기는 메소드입니다. 플래그 설정은 앞 포스팅에서 설명한 것과 같이 브로드캐스트 리시버는 액티비티가 없는 형태이기 때문에 TASK 설정을 해주어야 하고 이미 메모리에 만든 SmsActivity가 있다면 추가로 만들지 않도록 TOP 설정도 해줍니다.




③-1 정적 리시버의 경우 AndroidManifest.xml 에 등록을 합니다.


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


위에서 말한 것과 같이 원하고자 하는 메시지를 걸러내기 위해 intent filter를 이용하여 SMS문자메시지를 특정하였습니다.

또한 intent filter의 우선순위도 priority 속성으로 결정할 수 있습니다.


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

SMS는 권한이 필요하기 때문에 권한도 부여해야 합니다.



③-2 동적 리시버의 경우 MainActivity에서 등록, 해제 작업을 해줍니다. 


public class MainActivity extends AppCompatActivity {

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

myReceiver = new SmsReceiver();
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(myReceiver,intentFilter);

    }

@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
}


코드를 해석하면 정적리시버와 똑같습니다.

다만 클래스에서 등록하는 과정이기 때문에 BroadcastReceiver 객체와 IntentFilter 객체를 직접 생성하여 등록한 것일 뿐입니다.

여기에서는 onDestroy()에 해제 메소드를 호출하였는데 이렇게 하면 액티비티가 사라지는 경우와 같이 원치 않는 경우에 리시버가 해제되는 상황이 발생할 수 있기 때문에 서비스 단에서 처리해주는 것이 좋습니다.




④ 위험권한 처리


위험권한의 경우 다음 포스팅에서 다루겠습니다. 여기에서는 일단 테스트를 위해 코드를 사용해주시면 됩니다.

onCreate() 내부에 다음과 같은 내용을 넣어주세요.

int permissionChceked = ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS);
if(permissionChceked == PackageManager.PERMISSION_GRANTED){
Toast.makeText(getApplicationContext(), "SMS 수신권한 있음.",Toast.LENGTH_LONG).show();
}else {
Toast.makeText(getApplicationContext(), "SMS 수신권한 없음.",Toast.LENGTH_LONG).show();
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.RECEIVE_SMS},1);

}


그다음 onRequestPermissionsResult() 메소드를 오버라이드하여 아래와 같이 정의해 주세요.

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

if(requestCode ==1 ){
if (grantResults.length>0) {
grantResults[0] = PackageManager.PERMISSION_GRANTED;
Toast.makeText(getApplicationContext(), "SMS 권한을 사용자가 승인함.",Toast.LENGTH_LONG).show();
} else{
Toast.makeText(getApplicationContext(), "SMS 권한을 사용자가 거부함.",Toast.LENGTH_LONG).show();
}
}

}




⑤ 테스트 결과 출력


 안드로이드 에뮬레이터에는 메시지를 보내서 출력해볼 수 있는 기능도 가지고 있습니다.


                                                         


안드로이드 에뮬레이터 오른쪽에 보면 왼쪽과 같은 점 3개의 아이콘을 클릭할 수 있습니다.

클릭하게 되면 창이 하나 출력되는데 Phone 목록을 클릭하시면 오른쪽과 같은 문자메시지를 보낼 수 있는 기능이 나옵니다.



메시지를 전송하면 다음과 같이 보낸 사람 번호와 내용, 보낸 날짜와 시간까지 출력되는 것을 확인할 수 있습니다.





5. SMS를 수신할 때의 브로드캐스트 수신자의 동작 방식 정리




[출처] Do it! 안드로이드 앱 프로그래밍


책에 나온 이 그림이 진짜 한눈에 이해할 수 있도록 잘 그려진 것 같습니다.

SMS를 단말에서 받으면 텔레포니 모듈이라는 것이 처리합니다. 그 후 처리된 정보는 인텐트에 담겨 브로드캐스팅 방식으로 다른 앱들에게도 전달되는 것입니다.

그 후 앱들에 정의된 onReceive() 메소드에 따라 처리하게 되는 것입니다. 

물론 텔레포니 모듈이 정보를 뿌려줄 앱을 정하는 것은 우리가 매니페스트나 클래스에서 리시버를 시스템 상에 등록해 두었기 때문에 처리가 가능한 것입니다.


========================================================================================================


문자 SMS는 메시지 수신했을 때 단말의 기본 메시지 앱이 가장 먼저 받아 처리하고 다른 앱으로 넘겨준다고 합니다. 이러한 특징은 API 19 버전부터 적용되었다고 하는데 기본 앱이 아닌 직접 만든 앱으로 사용자가 단말의 기본 메시지 앱으로 지정하면 단말의 기본 SMS 앱으로 직접 만든 SMS 수신 앱을 띄울 수 있다고 합니다. 반대로 다른 앱들이 문자를 처리하지 못하도록 설정할 수도 있다고 하는데 둘다 단말의 사용자가 직접 설정해야하는 것이기 때문에 참고만 하면 좋을 것 같습니다.

'Android' 카테고리의 다른 글

10. 프래그먼트 : Fragment  (4) 2018.10.10
9. 위험 권한 : Dangerous Permission  (0) 2018.10.07
7 . 서비스 : Service  (1) 2018.10.03
6. 안드로이드의 4가지 구성 요소 & Intent  (0) 2018.09.30
5. Page Sliding  (0) 2018.09.30
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함