안드로이드 Broadcast 를 테스트 해보고 정리해둔다.
개념도 제대로 모른채 구글링해서 나온 예제를 실행해보고 실행이 되는구나 하는 정도만으로는 응용이 불가능하다는 걸 테스트 해보면서 몸소 느끼고 있다. Android Studio 기반에서 테스트한 것을 적어둔다.
안드로이드 6.0 에서 SMS 권한이 없는데 허용할 것인지 문의하는 창도 뜨고 잘 동작된다.
BroadCast.zip
Android Studio 는 main 폴더에 핵심적인 내용이 들어가 있으므로 이것만 파일 첨부하면 이걸 이용해서 다시 작성하기가 쉽다.
BroadcastReceiver는 모든 어플에게 다 전해지는 것이다. (시스템 매니저가 알아서 보내는 것)
// IntentFilter 전달받는 여러 인텐트중 특정 인텐트만 고르겠다는 의도
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED);
registerReceiver(mUsbAttachReceiver , filter); // 브로드캐스트리시버 등록
또는
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USB_DEVICE_ATTACHED);
registerReceiver(mUsbAttachReceiver, filter); // 브로드캐스트리시버 등록
Broadcast Receiver(방송수신자)는 BroadcastReceiver 클래스를 상속받아 작성한다.
Broadcast 메시지가 수신되면 onReceive 메서드가 자동 호출된다.
public class Broadcastreceiver extends BroadcastReceiver {
private Bundle bundle; private SmsMessage currentSMS; private String message;
@Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { Log.d("onReceive()","부팅완료"); //Intent i = new Intent(context, ScreenService.class); //context.startService(i); }
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) { Log.d("SMSBroadcastReceiver", "SMS 메시지가 수신되었습니다."); //Toast.makeText(context, "문자가 수신되었습니다", Toast.LENGTH_SHORT).show(); bundle = intent.getExtras(); if (bundle != null) { Object[] pdu_Objects = (Object[]) bundle.get("pdus"); if (pdu_Objects != null) { for (Object aObject : pdu_Objects) { currentSMS = getIncomingMessage(aObject, bundle); String senderNo = currentSMS.getDisplayOriginatingAddress(); message = currentSMS.getDisplayMessageBody(); Toast.makeText(context, "senderNum: " + senderNo + " :\n message: " + message, Toast.LENGTH_LONG).show(); } this.abortBroadcast(); // End of loop } } } // bundle null
|
AndroidManifest.xml 을 사용하여 Broadcast Receiver 를 등록
- 앱이 실행중이 아니더라도 지정한 Broadcast 메시지를 수신(지정한 Action 발생)되면 Broadcast Receiver 가 자동 실행된다.
동적으로 Broadcast Receiver 를 등록하면, 앱이 실행중일 때만 Broadcast Receiver 가 수행되며, 동적으로 Broadcast Receiver 를 추가/제거가 가능하다.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tistory.link2me.sms_receive">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.RECEIVE_SMS" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name ="com.tistory.link2me.sms_receive.Broadcastreceiver" android:enabled="true"> <intent-filter android:priority="10000"> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver>
</application>
</manifest> |
AndroidManifest.xml에 해당 리시버를 등록하고 intent-filter로 원하는 브로드캐스팅 받을 Action을 등록한다.
즉, intent-filter는 어떤 intent를 필터링해서 들을것인지 설정하는 것이다.
안드로이드 앱에서 알고 싶은 이벤트들이 있을수 있다. 가령
- 배터리 부족
- 네트워크 연결 / 중단
- SD카드 삽집 / 제거
- SMS 수신
- 카메라 버튼이 눌렸을때
- 폰의 날짜, 시간이 변동되었을때
- 비행기 모드 전환시
- 어플 설치/제거
- 재부팅/종료
- 매 분마다 수신
android:enabled="true" : 시스템이 알아서 Broadcastreceiver를 실행한다는 의미다. default 가 true 다.
android:exported="false" : 외부 어플리케이션에서는 사용할 수 없으며 같은 앱 또는 같은 UserId를 가진 놈만 호출할 수 있다는 뜻이다. (이걸 적어두면 메시지 수신이 제대로 안됨)
android:priority : 숫자가 높을수록 우선순위가 높으며, 우선순위가 높은 리시버부터 순차적으로 메세지가 전달된다.
=== Broadcastreceiver.java === |
package com.tistory.link2me.sms_receive;
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.telephony.SmsMessage; import android.util.Log; import android.widget.Toast;
public class Broadcastreceiver extends BroadcastReceiver { private Bundle bundle; private SmsMessage currentSMS; private String message;
@Override public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { Log.d("onReceive()","부팅완료"); //Intent i = new Intent(context, ScreenService.class); //context.startService(i); }
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { Log.d("onReceive()","스크린 ON"); }
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { Log.d("onReceive()","스크린 OFF"); }
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) { Log.d("SMSBroadcastReceiver", "SMS 메시지가 수신되었습니다."); Toast.makeText(context, "문자가 수신되었습니다", Toast.LENGTH_SHORT).show(); bundle = intent.getExtras(); if (bundle != null) { Object[] pdu_Objects = (Object[]) bundle.get("pdus"); if (pdu_Objects != null) { for (Object aObject : pdu_Objects) { currentSMS = getIncomingMessage(aObject, bundle); String senderNo = currentSMS.getDisplayOriginatingAddress(); message = currentSMS.getDisplayMessageBody(); Toast.makeText(context, "senderNum: " + senderNo + " :\n message: " + message, Toast.LENGTH_LONG).show(); } this.abortBroadcast(); // End of loop } } } // bundle null }
private SmsMessage getIncomingMessage(Object aObject, Bundle bundle) { SmsMessage currentSMS; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { String format = bundle.getString("format"); currentSMS = SmsMessage.createFromPdu((byte[]) aObject, format); } else { currentSMS = SmsMessage.createFromPdu((byte[]) aObject); } return currentSMS; } }
|
문자수신시각 정보 추가시
Date curDate = new Date(currentSMS.getTimestampMillis());
SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
String receivedDate = sdf.format( curDate );
=== MainActivity.java ===
|
package com.tistory.link2me.sms_receive;
import android.Manifest; import android.content.pm.PackageManager; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS); if(permissionCheck == PackageManager.PERMISSION_GRANTED){ Toast.makeText(this, "SMS 수신권한 있음", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "SMS 수신권한 없음", Toast.LENGTH_SHORT).show(); if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.RECEIVE_SMS)){ Toast.makeText(this, "SMS 권한 설정이 필요함", Toast.LENGTH_SHORT).show(); } else { // 권한이 할당되지 않았으면 해당 권한을 요청 ActivityCompat.requestPermissions(this,new String[] {Manifest.permission.RECEIVE_SMS},1); } } }
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == 1) { for (int i = 0; i < permissions.length; i++) { if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, permissions[i] + " 권한이 승인됨.", Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, permissions[i] + " 권한이 거부됨.", Toast.LENGTH_LONG).show(); } } } }
}
|