Programing/Android

BroadcastReceiver

안중환 2016. 5. 6. 20:23

출처: http://cafe.daum.net/_c21_/bbs_search_read?grpid=1MWA2&fldid=aAfL&datanum=102&docid=1MWA2aAfL10220110915161750

1. Broadcast Receiver에 대해서

 

Android의 4가지 Component 중 가장 쉬운 것중 하나가 바로

Broadcast Receiver이다.

Broadcast Receiver는 각각의 Component들 간에 메시지를

전달할 수 있는 방법을 제공한다.

 

 

우선 여러 패키지에서는 귀를 기울일만한 방송에 대해 Receiver라는 녀석을 등록해 둔다.

이 Receiver는 자신이 관심을 가지는 방송을 듣게되면 동작하게 되는 것이다.

 

그림으로 보자면

아래에 1번과 같이 특정 패키지 Component에서 sendBroadcast(메시지)를 통해서

전역 방송을 날린다. 이때 방송은 "Test1"이라고 가정하자.

그후 이미 등록된 Receiver 중 3번에 해당하는 receiver가 그 메시지에 관심을 가지고 있으므로 동작하게 될 것이다.

 

 

여기서 방송이라는 표현을 썼는데 이 것이 바로 Intent이다.

앞서 Activity를 설명하면서 Intent에 대해서 충분히 설명을 하였다.

Andriod를 하면서 Intent는 아주 중요하다. (조금이라도 이해가 안되었다면 앞의 강좌를 다시보고 또 보자.)

앞으로 계속 배우게 될 BroadcastReceiver, Service에서 계속 활용된다.

Activity에서 설명한 Intent에 대해 이해하면 모두 같은 방식으로 사용되니 한번만 잘 이해하면 된다.

(주변에 Intent에 대해 정확히 이해하는 사람이 많이 없어서 계속 말하는 것이다. ^^;)

 

어쨌든 참 글로 쓰면 왜 쉬운 내용도 어렵게 전달이 될까? 짜증이 난다. ^^;

카페에 누군가 동영상 강좌로 해 주면 안되겠냐는 글을 보았다.

어쩔때는 정말 그러고 싶다. ㅋㅋㅋ

 

자 계속해서 Receiver에는  두가지 종류가 있다.

바로 정적 Receiver와 동적 Receiver이다.

별로 어렵지 않으니 계속 강좌를 지켜보길 바란다.

 

 

1.1 정적 Receiver

 

정적 Receiver란 말 그대로 Receiver를 고정해서 등록해 놓고 원하는 방송에

반응하는 Receiver를 말한다.

 

역시 개발자는 소스를 보고 이해하는 것이 빠를 것이다.

아래의 패키지로 이해해 보자.

두 가지 패키지를 만들 것이다.

 

우선 첫번째 패키지는 방송을 하는 패키지이다.

 

아래에 1번과 같이 Intent를 하나 생성하고

Intent에 방송할 내용을 담는다.

방송할 내용 역시 Action이 사용된다. Activity와 똑같다. ㅎㅎㅎ

setData()를 통해 Intent Filter에 매칭될 Scheme을 하나 넣어 보았다.  왜?  그냥.... -  _-;

(BroadcastReceiver는 모든 패키지가 받을 수 있다. 그러므로 꼭 원하는 패키지 Receiver에게 전달하게 하려면

 적절한 Intent Filter 를 사용하는 것이 좋다.)

1번에서 방송하게 될 Action을 기억해 두자. "android.intent.action.SUPERSK"

 

 

자 이제 두번째 패키지 이다.

 

당연히 이 패키지는 방송을 듣고 반응하는 리시버 이다.

 

아래는 AndroidManifest.xml에 리시버를 하나 등록하고 있다.

아래는 AndroidManifest.xml에 리시버를 하나 등록하고 있다.

아래는 AndroidManifest.xml에 리시버를 하나 등록하고 있다.

3번 반복한 것은 ^^ 오타가 아니다.

왜냐하면 AndriodManifest.xml에 고정해서 리시버를 등록하기 때문에 정적 Receiver라고 말하는 것이다.

이해가 되는가 안되면 다음에 나올 동적 Receiver까지 보면 이해가 될 것이다.

일단 ^^/ 계속...

위의 Intent Filter는 이전 Activity에서 Intent를 이해했다면 쉽게 볼 수 있을 것이다.

2번을 보면 Action으로 첫번째 패키지가 보내는 방송에 귀를 기울이는 Action 일 것이다.

 

계속해서 그 방송을 듣게 되면 처리되는 코드는 아래와 같다.

해당 방송이 오면 Toast를 하나 띄울 것이다.

위의 onReceive( Context context, Intent intent) 의 인자인

intent로 방송을 보낼때 전달되었던 Intent가 그대로 전달받게 된다.

바로 이 Intent로 원하는 데이터를 Intent Extra에 담아서 보내면 된다.

 

자 이제 실행해 보자.

첫번째 패키지에 버튼을 누르면

2번과 같이 두번째 패키지가 방송을 듣고 Toast를 하나 보여 준다.

쉽지 않은가?

 

테스트 패키지 소스는 아래에 첨부한다.

 

첨부파일 Broadcast1.zip

첨부파일 Receiver1.zip

 

 

1.2 동적 Receiver

 

동적 Receiver는 AndroidManifest.xml에 Receiver를 등록하지 않는다.

위에서 만든 패키지 중 방송을 보내는 패키지를 그대로 활용하고,

아래의 패키지를 추가로 만들어 보자.

위에서 왜 Activity를 하나 만들었을까?

궁금증을 가지고 계속 아래를 보자.

 

Activity의 내용이다.

위에서 Activity의 생명 주기중 onCreate에서 Receiver를 등록하고 있다.

차근차근 보자.

1번에서 IntentFilter를 하나 생성하여 방송에 매칭될 Action과 Data의 Scheme을 등록하였다.

2번에서 BroadcastReceiver 객체를 하나 생성한다. 객체 생성과 동시에 onReceiver() 함수를 구현해 주면 된다.

            차후 방송이 오게 되면 바로 onReceiver()함수가 호출될 것이다.

3번에서 해당 BroadcastReceiver 객체를 등록한다. registerReceiver()함수는 바로 receiver 객체를 등록하게 해 준다.

           당연히 registerReceiver() 인자로 등록될 리시버 객체와 intentFilter를 넣어주면 된다.

 

자 특정 Component에서 BroadcastReceiver 객체를 동적으로 생성하여 리시버를 등록 하였다.

이 것이 바로 동적 Receiver라고 하는 이유다.

 

자 잘 동작되는지 보자.

1번과 같이 지금 만든 동적 리시버 패키지를 꼭 실행해 둬야 한다. (이유가 있다.)

2번과 같이 꼭 Home Key를 눌러서 다시 홈으로 가자.

3번에서 방송을 날릴 패키지를 실행하자.

4번의 버튼을 눌러보면 동적 Receiver가 동작하고 Toast를 하나 띄울 것이다.

 

자 다시 하나면 더 테스트를 해 보자.

아래와 같은 시나리오로 해야한다.

1번에서 동적 리시버 패키지를 실행하자

2번과 같이 Back Key를 눌러 해당 패키지를 종료해 보자.

3번과 같이 홈에서 방송을 보낼 패키지를 실행하고

4번과 같이 방송 버튼을 눌러 Broadcast를 날려 보자.

!!! Toast가 안뜨지 않는가?

이것이 바로 동적 리시버의 특징이다.

동적 리시버는 자신을 등록한 Component의 생명 주기가 끝나면 사라진다.

위에서는 Activity에서 리시버를 등록 했으므로 Activity를 Back Key를 이용해서 종료하게 되면

리시버도 사라지는 것이다.

 

Activity의 생명 주기는 아래와 같다.

즉 onDestroy()까지 타면 Receiver는 없어 진다.

꼭 이해하도록 하자.

 

동적 리시버에서 꼭 하나더 알아야 할 것이 있다.

리시버를 등록할때 registerReceiver()라는 함수를 썼다.

그렇다면 분면 unregisterReceiver()라는 함수도 있지 않겠는가 그렇다.

registerReceiver()로 등록된 Receiver는 unregisterReceiver() 함수를 이용해서 등록해제 할 수 있다.

즉 리시버를 더 이상 사용할 필요가 없을 때는 unregisterReceiver() 함수로 해제하면 된다.

또한 한번 등록된 리시버를 중복해서 다시 등록하는 일이 없도록 하자.

만일 두번 등록이 되면 중복된 리시버 모두 동작하기 때문이다.

뿐만아니라 unregisterReceiver()를 통해 해제치 않으면

메모리에 계속 잡고 있는 메모리 누수가 발생한다.

꼭 해제해 주도록하자.

 

적절한 위치에 등록과 해제 소스를 넣어 두자.

예를 들어 onCreate()에 등록하고 onDestroy()에 해제 한다던가...

 

테스트 패키지 소스는 아래에 첨부한다.

 

첨부파일 Receiver2.zip

 

 

1.3 동적 Receiver와 정적 Receiver는 어떤 차이가 있을까?

 

android를 처음 접할때 이 점이 참 궁금했다.

그 이유는 참 간단했다. 하지만 글로 설명해야 하는데 잠시 두렵기도 하다...  =_  =;;

일단 정적 리시버는 한번 등록하고 계속 유지할 수 있다는 장점이 있지만

해제가 어렵다는 단점이 있다.

다른 단점을 이해하기 위해서는 동적 리시버의 장점을 설명해야 한다.

 

동적 리시버는 등록한 component 생명 주기가 끝나면

더이상 동작하지 않는다는 단점이 있다고 했다. 물론 계속 유지 하고 싶다면 정적 리시버를 쓰면 된다.

동적 리시버는 내가 생각하는 아주 편리한 장점 두가지가 있다.

 

첫번째는 바로 시스템에 큰 부하를 주지 않는 점이다.

 

매분마다 발송되는 시스템 Action "android.intent.action.TIME_TICK" 이 있다

이 것은 AlarmManagerService란 서비스가 매 분마다 발송한다.

만일 정적 리시버로 해당 Action을 등록했다면 매분마다 계속 이벤트를 받게 되고,

매 분마다 해당 이벤트에 대한 처리를 할 것이다.

이는 시스템 측면에서 전체적이 성능을 저하시킬 것이다.

하지만 동적리시버는 한 Component의 생명주기 내에서만 해당

이벤트를 받으므로 큰 부하를 주지 않는 것이다.

 

참고로  "android.intent.action.TIME_TICK" 이벤트는 정적 리시버로는 받을 수 없다.

위에서 말한 이유 때문이다. 즉 Android 시스템에서 시스템에 부하를 주는

경우를 당연히 두지 않는다.

이렇게 동적 리시버에서만 Action을 받을 수 있도록 하는 Intent Flag는

"FLAG_RECEIVER_REGISTERED_ONLY" 이다.

( FLAG_RECEIVER_REGISTERED_ONLY는

   "17. Broadcast Receiver에 대해서 - IntentFlag 정리"

  강좌에서 설명하니 넘어가도록 하자.)

 

두번째는 다른 Component 내에 소스가 존재한다는 것이다.

리시버를 등록할때를 생각해 보자.

 

뭐 아래와 같다고 하자. 

 

    public class testActivity extends Activity

    {

        private   int        mCount = 0;

 

        public void onCreate(Bundle b)

        ....

 

        mReceiver = new BroadcastReceiver()

        {

              pubilc void onReceiver(Context context, Intent intent)

              {

            

              }

         };

         registerReceiver( mReceiver , intentFilter);

 

        .....

    }

위에서 Activity 내에 Receiver가 달려 있다.

여기서 mCount 라는 멤버 변수로 접근해 보자.

 

 

    public class testActivity extends Activity

    {

        private   int        mCount = 0;

 

        public void onCreate(Bundle b)

        ....

 

        mReceiver = new BroadcastReceiver()

        {

              pubilc void onReceiver(Context context, Intent intent)

              {

                    mCount  = 20;

              }

         };

         registerReceiver( mReceiver , intentFilter);

 

        .....

    }

위와 같이 쉽게 mCount로 접근할 수 있다.

너무 당연한 얘기 아닌가?

즉 Activity 내에 모든 멤버 객체에 접근하고

그 객체를 참조할 수 있는 것이다.

 

정적 Receiver를 생각해 보자.

정적 Receiver는 특정 Component에 포함되어 있지 않으므로

Receiver에서 사용하고 싶은 특정 Component의 멤버변수를 전혀 접근 할 수 없다.

(물론 Application 객체를 이용하여 접근할 수는 있다. -   _-; 혹시 궁금한 사람들은

 옆에 링크로 이동하여 Activity의 name 속성 부분을 보라.  여기로 링크  )

 

이해 되었는가?

이해 되지 않는 부분은 늘 질문하라...ㅎㅎㅎ

 

1.4 Receiver 꼭 알아야 할 것

 

Activity와 마찬가지로 Receiver 또한 ActivityManagerService가 처리한다.

아래의 그림을 보자.

1번과 같이 sendBroadcast() 호출하면

2번과 같이 ActivityManagerService가 활성화 되어야 할 등록된 Receiver를 호출해 준다.

만일 여러개의 리시버가 등록되어 있다면 순서대로 활성화 시킨다.

3,4,5번을 보면 등록된 리시버가 3개이다.

만일 Receiver1이 처리되는 시간이 3초라면 다음 처리될 Receiver2는 3초후에 활성화 될 것이다.

또한 Receiver2이 6초라는 시간이 소모되면 Receiver3번은 총 9초후에 활성화 될 것이다.

 

여기서 말하고자 하는 것은 리시버에 대한 처리는 최대한 빨리 끝내는 것이 바람직하다.

^^ 만일 처리시간이 오래 걸리게 되면 Activity와 마찬가지로 ANR이 발생된다.

 

아래의 테스트 코드로 이해해 보자.

리시버를 하나 등록했다.

해당 리시버는 12초의 지연을 주 예이다.

어떻게 될까?

 

아래와 같이 실행해 보자.

눈에 에러는 보이지 않으나 에러가 발생될 것이다.

 

아래를 보고 이해해 보자.

로그를 보자.

5번을 보면 리시버가 timeout이 발생되고

6번에서 ANR을 발생시켜 해당 리시버를 죽이기 위해 리시버가 포함된 Process를 죽여 버린다.

 

즉 시스템에서도 리시버에 처리 지연을 두고 보지 않는 다는 말이고

ActivityManagerService는 다른 작업을 하기 위해 절대 기다려 주지 않는 다는 것이다.

 

아래에 Framework 소스를 잠시 보자.

리시버의 Timeout은 정확히 10초임을 알 수 있다.

그렇다고 10초 내에 끝내라는 것이라고 이해하면 안된다.

최대한 간단한 작업만을 하라는 것이다.

 

리시버는 Activirty 처럼 UI가 존재하지 않는다.

그러므로 Background에서 돌아가는 component이다.

다음에 배우겠지만 service component 역시 background에서 돌아간다.

하지만 둘을 같이 보면 안된다.

Receiver는 제약 사항이 많기 때문이다. 지금 그 제약 사항을 설명하는 것은 어렵다.

Service를 배우지 않았기 때문이다. 차후에 다시 설명 하겠다.

(참고로 몰라도 되지만 ... Receiver에서는 bindService가 허용되지 않는다.)

 

어쨌든 기억하라. ~!!!!!

 

리시버는 간단한 작업만 하도록 하자.

예를 들어 다른 component를 활성화 한다던지,

(startActivity or startService 등)

DB에 값을 변경한다던지... 꼭 구구절절히 말하기는 힘들지만 어쨌든 onReceiver 함수 내에서만 처리될 수 있는

간단한 작업만을 하자.

 

간혹 리시버에 네트워크 작업등을 처리하려는 사람을 보았다.

그러지 말자. ^^;;;