브로드캐스트(broadcast) 는 널리 알리다는 뜻이다.
예를들어, 핸드폰의 설정을 비행기모드로 바꿨을때 android.intent.action.AIRPLANE_MODE 라는 브로드캐스트 메시지가 발생된다. 이 브로드캐스트 메시지를 앱에서 받아서 여러가지 작업을 할 수 있다.
브로드캐스트를 직접 발생시킬 수도 있는데, 그 방법은 아래와 같다.
Intent().also { intent ->
intent.setAction("com.example.broadcast.MY_NOTIFICATION")
intent.putExtra("data", "Notice me senpai!")
sendBroadcast(intent)
}
여기서 action은 브로드캐스트 메시지를 식별하는 식별자다.
상수를 모아둔 클래스를 만들고 거기에 선언해 놓는 것이 좋다.
만약 로컬에서만 브로드캐스트 하려면 아래와 같이 하면 된다.
LocalBroadcastManager.getInstance(context).sendBroadcast(
Intent().apply { action = Constants.ACTION_NEED_REFRESH })
}
공식문서에 따르면, LocalBroadcastManager를 사용하면 다른 앱이 브로드캐스트를 수신하거나 전송할 수 있는 측면과 관련된 보안 문제에 관해 신경 쓰지 않아도 된다.
여기서 보안문제란,
1. 민감한 데이터를 브로드캐스트 하지 않아야한다.
2. 악성 브로드캐스트를 받지 않기 위해 앱이 수신하는 브로드캐스트를 제한해야한다.
이를 위한 방법들은 공식홈페이지에 자세히 나와있다.
지금은 브로드캐스트에 대해 좀더 알아보자.
브로드캐스트를 받는 방법은 receiver를 메니페스트에 선언하는 것과 컨텍스트에 등록하는 방법이 있다.
1. 메니페스트에 선언
<receiver android:name=".MyBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>
참고:앱이 API 레벨 26 이상을 타겟팅하면 manifest를 사용하여암시적브로드캐스트(앱을 구체적으로 타겟팅하지 않는 브로드캐스트)의 수신자를 선언할 수 없습니다.
이후에 BroadcastReceiver 를 상속받는 클래스를 만들면 된다.
private const val TAG = "MyBroadcastReceiver"
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
StringBuilder().apply {
append("Action: ${intent.action}\n")
append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
toString().also { log ->
Log.d(TAG, log)
Toast.makeText(context, log, Toast.LENGTH_LONG).show()
}
}
}
}
이러면 시스템이 이 브로드캐스트를 받을 때 클래스의 객체를 생성해서 onReceive를 호출한다고 한다.
또한 앱이 설치될때 시스템 패키지 관리자가 리시버를 등록한다는데, 이는 앱의 진입점이 별도로 생긴다는 것을 의미한다. 즉, 앱이 꺼져있어도 시스템이 앱을 실행하고 브로드캐스트를 받을 수 있다는 것이다.
2. 컨텍스트에 등록
val br: BroadcastReceiver = MyBroadcastReceiver()
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).apply {
addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
}
registerReceiver(br, filter)
위 코드를 보면 브로드캐스트와 인텐트 필터를 만들어 registerReceiver 함수를 통해 리시버를 컨텍스트에 등록하는 것을 볼 수 있다.
만약 LocalBroadcast의 경우에는 아래와 같이 한다.
LocalBroadcastManager.getInstance(requireContext())
.registerReceiver(mRefreshReceiver, IntentFilter(Constants.ACTION_NEED_REFRESH))
등록한 리시버는 더이상 필요하지 않을때는 해제해줘야한다. 그래야 쓸데없는 브로드캐스트를 받지 않는다.
unregisterReceiver(android.content.BroadcastReceiver)
LocalBroadcast의 경우,
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mRefreshReceiver);
이렇게 해주면 된다.
참고로 onReceive 함수는 메인스레드에서 실행되기 때문에 오래걸리는 작업은 백그라운드 스레드에서 수행해야한다.
private const val TAG = "MyBroadcastReceiver"
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val pendingResult: PendingResult = goAsync()
val asyncTask = Task(pendingResult, intent)
asyncTask.execute()
}
private class Task(
private val pendingResult: PendingResult,
private val intent: Intent
) : AsyncTask<String, Int, String>() {
override fun doInBackground(vararg params: String?): String {
val sb = StringBuilder()
sb.append("Action: ${intent.action}\n")
sb.append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
return toString().also { log ->
Log.d(TAG, log)
}
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
// Must call finish() so the BroadcastReceiver can be recycled.
pendingResult.finish()
}
}
}
(굳이 AsyncTask를 쓸 필요는 없다)
모든 브로드캐스트는 본래 10초이내에 완료되어야하고, 시간이 지나면 ANR을 발생시킨다고 한다.
시간이 오래걸리는 작업을 할 경우 goAsync() 함수를 이용해 타임아웃을 연장해야한다.
@Override
public void onReceive(final Context context, final Intent intent) {
PendingResult asyncResult = goAsync()
//시간이 오래걸리는 작업
asyncResult.finish();
}
PendingResult를 꼭 finish 해주는 것을 잊지말자.
이런 브로드캐스트는 그럼 어떨때 쓸까?
나의 경우, 앱을만들다보면 ViewPager와 TabLayout으로 탭마다 프래그먼트들을 배치해놓는 경우가 많이 생기는데
이때 한 프래그먼트나 액티비티에서 어떤 컨텐츠가 수정되거나 삭제되었을때 나머지 프래그먼트를 갱신하기 위해서 쓴다.
'안드로이드 앱개발' 카테고리의 다른 글
동영상 스플래시 스크린 띄우기 (동영상에 애니메이션 넣기) (0) | 2021.04.03 |
---|---|
Firebase를 이용해 FCM 구현하기(웹서버 이용) (0) | 2021.01.08 |
Architecture Components(MVVM 패턴) (0) | 2020.11.20 |
안드로이드 서비스 (0) | 2020.11.20 |
카메라로 사진찍기 (0) | 2020.10.27 |