본문 바로가기

Programming/Android

Sticky broadcast intent

원문 : http://newbiehc.blogspot.kr/2011/03/sticky-intent-sticky-broadcast-intent.html


Sticky intent (또는 Sticky broadcast intent)

Sticky intent 는 말 그대로 끈적끈적한 intent 를 말합니다. ^^;
끈적끈적해서 자신의 역할을 수행한 뒤에도 메모리에 딱 달라붙어서 사라지지 않고 남아있는 것이죠.
보통의 broadcast 된 intent는 자신과 관련된 모든 Broadcast receiver 를 거치고 나면 임무 완료되어 ‘즐거운 퇴근길에 올라’ 메모리 상에서 제거됩니다. 하지만 Sticky intent는 임무 완료 후에도 메모리에 남아 있는다는 사실! (정확하진 않지만 해당 event에 대한 다음 broadcast 가 있을 때까지 남아 있는 것 같습니다)

그럼 이 sticky intent를 어디에 사용할까요?

안드로이드에서 Broadcast receiver 는 두 가지 방법을 통하여 등록할 수 있습니다. 정적으로 AndroidManifest.xml 에 태그를 이용하여 등록하는 방법 과 동적으로 Context.registerReceiver() method를 이용하는 방법.

정적으로 등록하는 경우에는 Broadcast receiver 를 등록한 app의 실행 여부와 관계없이 관련 Broadcast intent 가 있으면 등록한 Broadcast receiver 의 onReceive() method가 호출됩니다. 무의미한 상황에서 CPU와 배터리를 사용하는 셈이 될 수도 있는 것인데요. 만약 우리가 작성한 app 이 실행 중인 상황에서만 Broadcast receiver 가 동작하기를 원한다면 정적인 등록 방법은 알맞지 않겠죠.

그래서 아래와 같이 동적으로 등록하는 방법을 사용할 수 있겠습니다.
registerReceiver(new BroadcastReceiver(){
@Override
public void onReceive(Context arg0, Intent arg1){
//TODO
}
}, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 

짐작하시겠지만, 동적으로 등록하는 경우엔 당연히 Broadcast receiver 를 등록한 이후에 발생하는 intent 만을 수신하게 됩니다. 그런데 여기서 문제가 발생합니다.
예를 들어, Status Bar 에 배터리의 상태를 표현하려고 동적으로 Broadcast receiver 를 등록했는데, 이미 해당 intent (Intent.ACTION_BATTERY_CHANGED 를 action으로 갖는 intent)가 broadcast 된 후라면 그 Broadcast receiver 는 동작하지 않게 됩니다. 다음 broadcast 가 있을 때까지 말이죠. 따라서 intent 가 아닌 다른 방법으로 배터리 상태를 확인하는 코드를 추가해야 할지도 모릅니다.
하지만 편리하게도 안드로이드에서는 위와 같은 상황이 일어나지 않습니다. 배터리의 상태를 알리는 intent 는 Sticky intent 이기 때문이죠. 다시 말해 이 intent는 자신의 임무를 완료한 후에도 메모리에 남아 있다가 자신과 관련된 Broadcast receiver 가 등록되면 해당 receiver에 바로 전달되는 것입니다.

참고로 안드로이드 시스템에서 broadcast 하는 Sticky intent는 아래와 같습니다.
1) 배터리 관련 : Intent.ACTION_BATTERY_CHANGED
2) 저장소 관련 : Intent. ACTION_DEVICE_STORAGE_LOW
3) Docking 관련 : Intent. ACTION_DOCK_EVENT
4) Network 관련 : ConnectivityManager.CONNECTIVITY_ACTION

참고) http://blog.naver.com/PostView.nhn?blogId=huewu&logNo=110090854068


원문 : http://blog.naver.com/PostView.nhn?blogId=huewu&logNo=110090854068

About Android Sticky Broadcast Intent


 안드로이드에서 사용되는 Intent 중에는 몹시 끈적 끈적 거려서, 한번 전달되고 나면 깔끔하게 정리되지 못하고, 지나간 흔적이 그대로 남아 약속시간을 한참 지난 후에 파티에 참가한 사람도, 그 파티에서 무슨일이 일어났는지 전부 알 수 있는 그런 Intent 가 있다는 사실을 알고 계신가요?



Context.sendStickyBroadcast() 메서드를 통해 송신 되는 특별한 Intent 들이 바로 그렇습니다. 안드로이드 Intent 는 어플리케이션 컴포넌트간에 데이터를 주고 받을 때 사용되는 간단한 메세지라고 할 수 있습니다. 그리고 Intent 에는 두 가지 종류가 있지요.특정 사람에게만 지정해서 발송하는 일반 Intent, 그리고 스팸 문자 처럼 등록되어있는 모든 사용자들에게 전달되는 브로드캐스트 Intent 입니다. 브로드캐스트 Intent 는 안드로이드 시스템 혹은 개발자가 작성한 특정 어플리케이션 구성요소가 안드로이드 시스템 전체에 전달하고자 하는 공지 사항이 있을 경우에 사용하곤 합니다.


 안드로이드 개발 문서에도 명시되어 있듯이 브로드캐스트 리시버를 사용하는데는 두 가지 방법이 있습니다. 정적으로 메니페스트파일내에 선언하는 방법과, 동적으로 Context.registerReceiver() 메서드를 이용하는 방법입니다. 다음과 같이 말이지요.

registerReceiver(
		new BroadcastReceiver(){
			@Override
			public void onReceive(Context arg0, Intent arg1) {
				//do something.
			}
			
		}, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));


 정적인 경우에는 해당 어플리케이션의 실행 여부와 관계없이 작성한 인텐트 필터와 부합하는 브로드캐스트 Intent 가 전송된 경우 해당 브로드캐스트 리시버의 onReceive() 메서드가 호출됩니다. 위의 예처럼, 동적으로 추가된 경우에는 당연히 브로드캐스트리시버를 등록한 이후에 발생하는 Intent 만을 수신하게 됩니다. 그리고 한번 송신된 Intent 는 정적 혹은 동적으로 등록된 브로드캐스트 리시버를 전부 거치고 나면 임무 완료. 즐거운 퇴근길에 올라 메모리 상에서 제거됩니다. 일반적인 경우라면 그렇습니다. 


  특정 Intent 가 sendStickyBroadcast() 메서드를 통해 전달된 경우라면 조금 다른 일이 벌어집니다. 간단히 말해, Sticky Intent 는 해당 Intent 가 전달되고 관련된 모든 브로드캐스트 리시버가 약속된 일을 수행된 이 후에도 퇴근하지 못하고, 죽치고 앉아서 메모리를 차지하고 있게됩니다. 끈적 끈적한 만큼, 쉽게 사라지지 않고 버티고 있다고 생각하시면 됩니다. 그리고 해당 Intent 에 부합되는 필터를 갖는 브로드캐스트 리시버가 새롭게 시스템에 추가되는 경우 (Context.registerReceiver() 메서드를 통해...), 메모리 상에 남아있던 Sticky Intent 가 바로 전달됩니다. 


 왜 이런 기능이 필요한 걸까요?


 대답은 단순합니다. 편리한 경우가 많기 때문입니다. 브로드캐스트 Intent 는 시스템 상에 발생한 어떤 이벤트를 전달하는 수단으로 사용된다는 점을 생각해 본다면, 이는 시스템이 새롭게 추가된 리시버에게 자신의 최신 상태를 바로 알려주는 효과가 있습니다.


 네트워크 연결을 체크하기 위한 코드를 하나의 예로 생각해 볼 수 있습니다. 우리는 안드로이드 시스템이 무선 네트워크의 연결 상태에 따라서 특정한 브로드캐스트 Intent 를 송신한다는 사실을 알고 있습니다. 그리고, 네트워크 상태를 체크하기 위해 그 Intent 를 사용하고자 마음 먹었습니다.


 간편하게 메니페스트 상에 정적인 브로드캐스트 리시버를 등록하여 사용할 수도 있습니다. 하지만 이런 경우, 우리가 작성한 어플리케이션이 동작 중인 상황이 아닌 경우에도 무선 네트워크 상황이 변경될 때 마다 우리가 등록한 브로드캐스트 리시버가 동작하는 문제가 발생합니다. 무의미하게 CPU 와 배터리를 사용하는 셈입니다.


 따라서 당연히 Context.registerReceiver() 메서드를 이용하여, 동적으로 브로드캐스트 리시버를 등록해야 합니다. 어라 그런데 한 가지 귀찮은 문제가 발생하게됩니다. 브로드캐스트 리시버를 등록한 시점에는 당장 네트워크 상황이 어떤지 확인할 길이 없기 때문에, 현재 네트워크 상태 정보를 가져오는 구문을 추가해야합니다. 그리고 혹시나 브로드캐스트 리시버를 통한 처리 내용과 직접 네트워크 정보를 가져오는 부분이 서로 충돌을 일으키지 않을까 신경써서 처리해 주어야 하지요. 만일 네트워크 관련된 Intent 가 끈적 끈적 하지 않다면 말입니다.


 하지만, 편리하게도 안드로이드 플랫폼 단에서 전달되는 CONNECTIVITY_ACTION Intent 는 sendStickyBroadcast() 메서드를 이용해 전달되는 끈적 끈적한 Intent 이며, 이와 관련된 브로드캐스트 리시버를 등록하는 순간, 시스템이 가장 최근에 전송한 Intent 를 바로 전달해 줍니다. 즉, 리시버를 등록하는 순간, Intent 가 전달되고 그 Intent 정보를 통해 현재 네트워크 상황을 바로 알 수 있는 셈이지요.


registerReceiver(
		new BroadcastReceiver(){

			@Override
			public void onReceive(Context arg0, Intent intent) {
				NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
				if( info.getType() == ConnectivityManager.TYPE_WIFI 
						&& info.getState() == NetworkInfo.State.CONNECTED){
					//Wi-Fi is connected.
					//do something useful.
					//...
				}
			}
			
		}, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));


 따라서 결론은 이렇습니다. 만일, 무선 네트워크가 켜져 있는지를 확인하고자 하는 코드를 작성한다고 한다면, 위와 같이 간단한 브로드캐스트 리시버를 하나 생성해주는 것만으로 충분합니다. (물론 필요한 각종 Permission 설정은 해주어야 합니다.) 그리고 그것은 별 신경을 기울이지 않으면 그냥 지나치기 쉽상인 브로드캐스트 Intent 의 숨겨진 기능, 끈적 끈적한 Intent 덕분에 가능한 일 입니다.


  참고로, 안드로이드 플랫폼 상에서는 아래와 같이 통신 모뎀과 관견된 설정 값이 변경되거나 네트워크 연결 정보가 변경되는 경우에 한해서만 Sticky 브로드캐스트 Intent 가 사용되고 있습니다. 보다 간결하고 쉬운 안드로이드 어플리케이션을 작성하는데 도움이 되기를 바랍니다^^;