슈퍼드로이드 카페의 안드로이드 강좌가 책으로 나왔습니다.
도서명 : 이것이 안드로이드다.
도서링크 : http://www.yes24.com//24/goods/13950202
================================================================================================
2. Broadcast Receiver에 관련된 Intent Flag
Broadcast Receiver와 관련된 Intent Flag는 총 2가지 이다.
| 정의된 Flag |
| FLAG_GRANT_READ_URI_PERMISSION |
| FLAG_GRANT_WRITE_URI_PERMISSION |
| FLAG_FROM_BACKGROUND |
| FLAG_DEBUG_LOG_RESOLUTION |
| FLAG_ACTIVITY_NO_HISTORY |
| FLAG_ACTIVITY_SINGLE_TOP |
| FLAG_ACTIVITY_NEW_TASK |
| FLAG_ACTIVITY_MULTIPLE_TASK |
| FLAG_ACTIVITY_CLEAR_TOP |
| FLAG_ACTIVITY_FORWARD_RESULT |
| FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
| FLAG_ACTIVITY_BROUGHT_TO_FRONT |
| FLAG_ACTIVITY_RESET_TASK_IF_NEEDED |
| FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY |
| FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET |
| FLAG_ACTIVITY_NO_USER_ACTION |
| FLAG_ACTIVITY_REORDER_TO_FRONT |
| FLAG_ACTIVITY_NO_ANIMATION |
| FLAG_RECEIVER_REGISTERED_ONLY |
|
FLAG_RECEIVER_REPLACE_PENDING |
|
FLAG_INCLUDE_STOPPED_PACKAGES |
| IMMUTABLE_FLAGS |
2.1 FLAG_RECEIVER_REGISTERED_ONLY
이 속성을 주고 Braodcast를 보내면
오직 동적 Receiver에게만 전달이 된다.
아래의 패키지를 구성해서 테스트를 해 보자.
패키지 구성은 이전 강좌에서 작성한 패키지를 재 사용하였다.
위와 같이 Broadcast를 보낼때 FLAG_RECEIVER_REGISTERED_ONLY 속성을
Intent Flag에 넣으면 된다.
리시버는 이전에 작성한 패키지 정적,동적 리시버를 사용한다.
아래와 같이 테스트를 해 보자.
1,2번과 같이 동적 리시벌르 구동시키고
정적리시버는 패키지가 설치만 되어 있으면 동작하므로
별 작업이 필요없다.
3,4번에서 Broadcast를 날리도록 하자.
보면 동적 리시버만 반을 한 것을 알 수 있다.
그렇다면 왜 동적리시버만 동작시키는 Flag가 존재할까?
그것은 현재 활성화 된 패키지에게만 전달하기 위함이다.
동적리시버는 전 강좌에도 설명 했지만 특정 component 생명주기 내에서만 동작한다고 하였다.
그 생명 주기가 끝나면 리시버의 작동도 끝나기 때문이다.
2.2 FLAG_RECEIVER_REPLACE_PENDING
이 Flag는 많은 사람들이 좀 생소할 것이다.
간략히 설명하자면 특정 패키지에서 같은 Broadcast를
동시에 여러번 보내는 경우가 있다.
그러면 리시버는 보내는 족족히 처리한다. 중복된 작업이라 할지라도 말이다.
이렇게 중복된 작업은 시스템에 부화만 가져다 줄 것이고,
중복된 작업이 예기치 못한 현상이 생길수도 있다.
이 경우에 해당 Flag를 추가하여
여러개의 Broadcast를 보내면,
중복된 Broadcast는 하나로 교체될 수 있다.
글로 하는 설명이라 좀 어렵게 느껴질 수 있다.
아래의 패키지로 이해해 보자.
이전 강좌에서 진행한 패키지를 다시 재사용해 보자.
발송 패키지에 아래와 같이 약간을 수정하자.
2번과 같이 같은 Intnet를 여러번 sendBroadcast() 를 실행한다.
리시버는 이전 패키지를 그대로 설치해 두자.
아래와 같이 실행해 보자.
5번과 같이 리시번가 5번 호출되면서 Toast가 5번 깜빡 깜빡 할 것이다.
자 아래에서 발송시 소스를 약간 수정한다.
2번과 같이 같은 Intent를 다시 보내기 전에
intent.setFlags( Intent.FLAG_RECEIVER_REPLACE_PENDING);
이라는 Flag를 설정하였다.
똑 같이 실행해 보자.
그런데 5번을 보면 Toast가 5번 깜빡이지 않고 2번만 깜빡일 것이다.
왜 그럴까?
당연히 intent.setFlags( Intent.FLAG_RECEIVER_REPLACE_PENDING); 속성 때문이다.
그런데 이 속성을 주면 같은 방송은 하나로 대체 된다고 하지 않았는가?
그렇다면 1번만 깜빡이는게 정상이다.
그런데 왜 2번을 깜빡일까?
그 이유는 아주 단순하다.
아래의 그림을 보자.
1번과 같이 5번의 방송을 보내면
2번과 같이 ActivityManager는
3번과 같이 첫번째 온 방송을 해당 Receiver에게 전달하고
4번과 같이 리시버가 처리하고 있을 것이다.
이렇게 4번과 같이 리시버가 처리하고 있는 중에
2,3,4,5번째 방송이 밀려 들어 올 것이다.
2번과 과정처럼 ActivityManager는 유입된 방송을 Queue에 쌓아둔다.
이제 설명이 가능해 진다.
위에서 Intent.FLAG_RECEIVER_REPLACE_PENDING Flag를 셋팅하면
분명 하나로 대체된다고 하였다.
이렇게 대체되는 기준은 바로 ActivityManager의 Queue 내부가 대상이다.
아래의 그림을 보고 다시 이해해 보자.
1번과 같이 해당 속성을 추가하여 5회 발송하였다.
그렇다면 제일 처음 발송된 방송은 리시버까지 전달되어 처리 중일 것이고
Queue에는 같은 방송이 4개 존재할 것이다.
하지만 Intent.FLAG_RECEIVER_REPLACE_PENDING Flag 를 셋팅하였으므로
2번 과정과 같이 2번째 방송과 3번째 방송이 같으므로 2번째 방송은 3번째 방송으로 대체되고,
3번 과정과 같이 3번째 방송과 4번째 방송이 같으므로 3번째 방송은 4번째 방송으로 대체되고,
4번 과정과 같이 4번째 방송과 5번째 방송이 같으므로 4번째 방송은 5번째 방송으로 대체되는 것이다.
결국 아래의 그림과 같이
Queue에는 5번째 방송밖에 남지 않는다.
그러므로 이미 유입된 1번 방송이 리시버가 처리되고 난뒤
다시 Queue에 남은 5번 방송이 리시버에게 전달되어 처리될 것이다.
그러므로 2번 Toast가 보여지는 것이다.
이해가 되었는가?
아주 간단한 Flag인데 설명이 너무 복잡하네... - _-a
본 Flag를 이용하여 같은 방송을 여러번 보내는 경우에
성능을 높이도록 하자.
2.3 FLAG_INCLUDE_STOPPED_PACKAGES
이 Flag는 Api Level 12(허니콤)부터 추가되었다.
허니콤에서 Broadcast를 날렸는데 아무리 해도 해당 Receiver가
받지 못해서 고생했던 분들이 많을 것이다.
허니콤에서 예를 들어
- A 패키지 Broadcast 발송
- B 패키지 Receiver 등록(정적 리시버)
인 경우
A에서 아무리 Broadcast를 날려도 B에서 받지 못할 수 있다.
받지 못하는 경우는 무엇일까?
B 패키지 process가 구동되지 않은 경우이다.
Android에서는 하나의 패키지가 바로 하나의 process라고 했었다.
한번이라도 패키지가 실행(Activity활성화, Service 활성화, Receiver 활성화...)
되지 않으면 당연히 Process는 존재하지 않을 것이다.
이 경우에 Receiver는 활성화 되지 않는 것이다.
아직 허니콤 Full source를 구하지 못해 왜 그렇게 처리한지는 모르겠다.
개인적으로 사용자들이 너무 빈번히 Broadcasting으로
처리하는 경우가 많아 시스템의 부하가 커서 그랬던것 같기도 하고,
어쨌든 이해가 안간다.
그럼 방법이 없을까?
이 것을 해결해 주는 것이 바로
FLAG_INCLUDE_STOPPED_PACKAGES
이 Flag이다.
이 Flag를 셋팅하여 Broadcast를 날리면
process가 존재하지 않아도 Receiver에게
전달되도록 한다.
즉 예전 진저 버젼이하와 같이 동작한다는 것이다.
더 이상 상세한 설명은 하지 않겠다.
허니콤 소스가 확보되면 그때 다시 설명토록 하겠다.
!!! 위의 주제에 해당하는 적당한 예를 댓글로 남겨 주세요. ^^
활용 방안의 예는 다른 개발자들에게 많은 도움이 됩니다.
!!! 카페의 활성화를 위해 추천 버튼을 눌러 주세요.
댓글
댓글 리스트-
작성자몰캉몰캉쿡 작성시간 14.10.14 2번 질문이 있습니다 : )
같은 Action을 보내지만
그 안의 Data, 예를들어서 String을 다르게 담아서 보내도
같은 broadcast라고 인식되나요? -
답댓글 작성자슈퍼성근 작성자 본인 여부 작성자 작성시간 14.10.14 안녕하세요.
Data는 타입이 Uri입니다.
Uri 타입은 보내는 곳과 받는 곳이 일치되지 않으면
받을 수 없습니다.
질문에서 Data로 추가한 것은 Uri의 scheme에 해당하는데요.
intent.setData(Uri.parse("sample:"));
의 sample:의 문자열을 aaa:로 바꾸시면
받는 곳의 AndroidManifest.xml에서
<data android:scheme="aaa"/>
라고 변경해주어야 받을 수 있습니다.
동적 리시버라면 리시버를 등록할 때 인텐트 필터의 data를 aaa로 설정해야겠죠. ^^
인텐트 필터에 대해서는
http://cafe.daum.net/superdroid/aAfL/79 강좌에서 3) mData와 mType부분부터 읽어보시면
이해되시것 같습니다. -
작성자무늬 작성시간 15.04.29 안녕하세요 언제나 명쾌한 해답을 주시는 슈퍼성근님!!!
FLAG_INCLUDE_STOPPED_PACKAGES에 대해 질문 드립니다.
안드로이드 3.1 부터는 system이 디폴트로 FLAG_EXCLUDE_STOPPED_PACKAGES를 Set하고 있기 때문에,,,
한번이라도 패키지가 실행(Activity활성화, Service 활성화, Receiver 활성화...) 되지 않는 상태, 즉 어플리케이션이 stopped state에 있는때, Receiver에서 Intent를 수신하지 못하는 것으로 이해하고 있습니다.
그런데 네이티브(시스템) 어플리케이션의 경우에도 동일하게 받을 수 없는 건가요?
(구글링하다 보니 /system/app에 빌트인된 어플리케이션은 상기 플래그와 상관 없이 Intent를 받았다는 내용이 있어서요) -
답댓글 작성자슈퍼성근 작성자 본인 여부 작성자 작성시간 15.04.30 네 맞습니다.
시스템앱은 이런 제한이 없습니다.
제조사에서 만든 앱들은 단말에 미리 탑재해서 나가고 그래서 preload app이라 부릅니다. 이런 앱들중 말씀하신 system 하위 app이나 priv-app, framework 경로 등에 포함된 apk는 시스템 앱 권한을 가집니다.
시스템앱은 특정 패키지를 설치 삭제 등도 가능합니다.
수고하세요 ^^ -
답댓글 작성자무늬 작성시간 15.04.30 슈퍼성근 감사합니다.
명쾌한 답변 도움이 되었습니다.