티스토리 뷰

반응형

Receiver ?

안드로이드의 4대 컴포넌트중 하나로 그 중에서 가장 간단하다.

안드로이드 시스템에서 벌어지는 일중에 앱이 알아야하는 상황이 발생하면 BroadCast(방송)가 뿌려지는데 특정 앱에서 이 BroadCast를 받아들이고 그에대한 동작(액션)을 수행하는 역할을 하는 컴포넌트가 바로 Receiver 이다.

특히 이런 BroadCast를 수신하기 위해 사용되는 Receiver를 Broadcast Receiver 라고 부른다.

 


리시버는 크게 정적리시버, 동적리시버로 구분된다.

 

정적리시버.

리시버 클래스를 별도로 생성하고 그에대한 내용을 정의해둔다. 그렇기 때문에 정적리시버를 한번 등록하면 해제할 수 없다.

 

public void onClick(View v) {
	Intent intent = new Intent();
    // 액션명은 사용자가 지정할 수 있다. 앞에는 무조건 패키지명...? (확인 필요)
    intent.setAction("com.superoid.test.Broadcasting.action.FILE_DOWNLOADED");
    intent.putExtra("FILE_NAME", "ch4njun.png");
    
    sendBroadcast(intent); // 결국 Broadcast를 보내는 포인트는 여기
}

위 소스코드는 Broadcast를 보내는 부분의 소스코드이다.

 

<receiver android:name=".BroadcastSideReceiver">
	<intent-filter>
   		<action android:name="com.superdroid.test.Broadcasting.action.FILE_DOWNLOADED" />
    </intent-filter>
</receiver>

수신앱에 이와같이 AndroidManifest.xml에 컴포넌트정보를 등록한다.

(action을 보면 굳이 앞에 패키지명까지 적어줄 필요는 없어보이지만...)

 

 리시버 클래스는 반드시 BroadCastReceiver 클래스를 상속해야하며, 정의하는 내용은 반드시 onReceive() 메소드를 재정의한 후 그 메소드 내부에 정의를 하게 된다.

public class BroadcastSideReceiver extends BroadcastReceiver {
	public void onReceive(Context context, Intent intent) {
    	String fileName = intent.getStringExtra("FILE_NAME");
        Toast.makeText(context, "REcevier!!!", Toast.LENGTH_LONG).show();
    }
}

 

컴파일시 자동으로 해당 Application에 등록되며 리시버가 동작하게된다. Broadcast가 수신앱에 도달하면 onReceive 메소드가 자동으로 호출되게 된다. 

 

마찬가지로 onReceive 함수도 Main Thread에서 동작하는 함수이기 때문에 오래걸리는 작업을 수행할 경우 ANR이 걸려 앱이 종료되게 된다.

 

근데 정적리시버에 대한 실습을 간단하게 진행했는데 계속 안된다.. 원인이 뭘까 ??

 

정적 리시버는 현재 더이상 사용할 수 없다. 사용하려면 과거의 API 버전을 사용해야 한다.

 

 


동적리시버.

리시버를 구현할 때 주로 사용하게 될 방식으로 리시버의 추가 및 삭제가 자유롭다. AndroidManifest.xml에 Receiver에 대한 정보를 추가하지 않아도 된다.

 

클래스를 따로 구현하거나 익명클래스를 사용해

registerReceiver(BroadCastReceiver객체, IntentFilter객체) 호출을 통해 리시버 등록.

 

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("_________액션_________");

// BroadcastReceiver에 대한 객체를 익명 클래스를 사용해 생성한다.
BroadcastReceiver br = new BroadcastReceiver() {    
	public void onReceive( Context context, Intent intent ) {        
    	~~~~~~~    
    }
};

// 동적 리시버를 등록해주는 과정이다.
registerRecevier(br, intentFilter);

 

위 내용은 익명클래스를 이용했고 따로 클래스를 구현해서 객체화시켜도 된다.

 

반대로 등록된 리시버를 제거할 때는 unregisterReceiver( 객체 ); 를 통해서 삭제가 가능하다.

삭제는 주로 onDestroy() 생명주기 함수가 호출될 때 호출되도록 구성하게 된다.

 

주로 클래스의 멤버변수로써 리시버를 등록해두고 해제 메소드를 따로 구현하게된다.

   ( 지역변수로 만들게되면 해제를 못하게 되기때문 !! )

 

동적 리시버는 다른 컴포넌트에 귀속되기 때문에 다른 컴포넌트의 생명주기가 끝나면 동작하지 않는다. 동적 리시버를 등록한 액티비티가 종료되면 리시버도 등록이 삭제된다는 것이다. (메모리 누수를 방지하기 위한 대책)

 

 


 

대신 Broadccast Receiver를 이용해 악의적인 내용이 사용자 몰래 백그라운드에서 실행될 수도 있다.

( 액티비티가 존재하지 않는상태로 리시버만 동작하는 것 !! )

 

1. 각종 개인정보 유출

2. 사용자의 실수로 스미싱 등의 불법앱이  설치.

 

 

이를 방지하기위해 FLAG_EXCLUDE_STOPPED_PACKAGES 인텐트 플래그를 사용.

위 플래그는 한번이라도 앱이 실행되어야만 정적리시버가 실행되게끔 해준다.

요즘은 이 플래그가 DEFAULT로 지정되어 있다.

 

FLAG_INCLUDE_STOPPED_PACKAGES 인텐트 플래그는 위 플래그의 반대로, 실행되지 않았던 앱의 정적 리시버라도 Broadcast를 수신하도록 하는 플래그지만 쓰지 말자 그냥.... 쓰지 말아야한다는 것을 알아야 하기 때문에 소개한다.

 

애초에 이제 정적 리시버가 사용되지 않는데 이걸 알려주는 이유가 무엇인가요..? ㅎ

 

 

FLAG_RECEIVER_REGISTERED_ONLY 인텐트 플래그

 : 오직 동적 리시버만 Broadcast를 Receive할 수 있도록 하는 인텐트 플래그이다. (오....?)

 

FLAG_RECEIVER_REPLACE_PENDING 인텐트 플래그

 : 중복해서 동일한 Action으로 Broadcast될 경우 중복된 방송을 제거하는 인텐트 플래그이다.

 

 


 

추가로 브로드캐스트를 발생시킬 때에는 sendBroadcast(intent객체) 를 사용하면된다.

마치 startActivity(intent 객체) 와같이 사용하는 것이다.

 

이 때 인자로 넣어준 intent객체에 저장된 Action값과 Receiver의 intentFilter에 저장된 Action값과 동일해야 해당 Receiver가 호출되는 것이다. 당연한게 모든 Broadcast를 다 읽어들이면 안되는 것이기 때문이다.

 

그리고 intent객체에 저장된 데이터는 onReceiver의 매개변수로넘어온 intent에서 사용할 수 있다.

 

 


Broadcast Receivere의 우선순위

정적 리시버에 대한 내용은 넘어가고 동적 리시버의 호출 순서에 대해서 살펴본다.

 

 

정적 리시버와 다르게 모든 리시버가 동시에 실행된다. 단 종료되는 순서는 다르게 나타날 수 있다. Receiver의 시작이 1, 2, 3, 4, 5 순서대로 시작되는 것 또한 보장할 수 없는듯 하다.

 

이 때 정적 리시버처럼 순서대로 처리되도록 하는 방법에는 어떤 것이 있을까?

public void onClick(View v) {
	Intent intent = new Intent();
    intent.setAction("ch4njun");
    
    // sendBroadcast(intent);
    sendOrderedBroadcast(intent, null);
}

 

이런 sendOrderedBroadcast를 사용할 경우 특수한 함수를 사용할 수 있다.

abortBroadcast();

특정 Receiver Application에서 위 함수가 호출되면 그 뒤에 호출되어야 할 동적 리시버가 더이상 Broadcast를 받아 리시버를 실행할 수 없다.

 

이러한 기능은 "문자 메시지가 왔을 때 하나의 앱에서만 해당 방송을 받아 동작하도록 하고싶다" 와 같은 상황에서 사용할 수 있다. 문자 메시지가 왔다고 모든 앱에서 해당 방송에 대한 Broadcast Receiver가 호출되면 시스템에 부하가 갈 수 있고 번거로워질 수 있다.

 

 

 

그렇다면 Broadcast Receiver의 우선순위를 변경해보자.

protected void onCreate(Bundle savedInstanceState) {
	...
	intentFilter.setPriority(1); // Default는 0이고 숫자가 높을수록 우선순위가 높아진다.
    ...
}
<receiver android:name=".Receiver3">
	<intent-filter android:priority="1">
    <action ....>
</receiver>

정적 리시버는 아래와 같이 Priorty를 추가할 수 있다.

 

우선순위가 높은 Broadcasta Receiver가 먼저 실행되지만, 종료되는 순서가 반드시 우선순위가 높은 Receiver가 먼저 종료되는 것은 아니다.

 

 


특정 앱으로만 Broadcast 보내기

Intent intent = new Intent();
intent.setAction("ch4njun");
intent.setPackage("com.superdroid.test.Receiver3");
sendBroadcast(intent);

setPackage를 사용해 특정 앱으로만 Broadcast를 보낼 수 있다. (꽤 괜찮은 것 같다)

 

 

반응형

'Android > Concept' 카테고리의 다른 글

[Android] Thread 제약사항  (0) 2019.08.19
[Android] 권한 부여하기  (0) 2019.08.04
[Android] 액티비티의 생명주기  (0) 2019.07.29
[Android] Bundle  (0) 2019.07.29
[Android] 명시적 인텐트 & 암시적 인텐트  (0) 2019.07.29
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함