본문 바로가기

개발/Android

[Android] 컴포넌트(component) - ContentProvider, Service, BroadcastReciver

반응형

ContentProvider

 

ContentProvider는 앱 사이에서 각종 데이터를 공유할 수 있게 해주는 컴포넌트입니다. 안드로이드 표준 시스템에서는 연락처인 Contacts난 이미지나 동영상 등의 데이터를 보관하는 MediaStore 등이 ContentProvider로 공개돼 있습니다. 데이터를 검색, 추가, 갱신, 삭제할 수 있으며, 주로 SQLite 등의 관계형 데이터베이스 이용을 염두에 두고 설계됐습니다. 

 

ContentProvider로부터 데이터 읽기

 

ContentProvider로부터 데이터를 읽어오려면 해당 ContentProvider가 어디에 있는지 알아야 합니다. 경로는 'content://스키마'를 가진 URI(Universal Resource Identifier) 지정되고, 일반적으로 접근할 대상 앱에서 정의됩니다. 또한 이 URI는 authority로 불리며, ContentProvider를 직접 만들 때는 AndroidManifest.xml에 기술해야 합니다.

 

ContentResolver를 통해서 테이터 읽기

 

ContentProvider가 제공하는 데이터에는 ContentResolver를 통해 접근하도록 설계돼 있고, ContentProvider 자신에 대한 참조는 필요 없습니다. ContentResolver의 인스턴스는 getContentResolver() 메서드로 가져옵니다.

 

ContentResolver에 URI를 전달함으로써 ContentProvider의 데이터에 접근할 수 있습니다. 데이터는 ContentResolver.query()를 이용해 가져옵니다.

 

Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOder)

 

query 메서드의 인수

 

 인수 내용 
 uri  ContentProvider가 관리하는 uri 
 projection  가져오고 싶은 칼럼명(select에 해당) 
 selection  필터링할 칼럼명을 지정(where에 해당) 
selectionArgs  selection으로 지정한 칼럼명의 조건을 설정(프리페어드 스테이트먼트(prepared statement)에 해당) 
sortOder 정렬하고 싶은 칼럼명ㅇ르 지정(Order by)

 

 

BroadcastReceiver

 

어떤 이벤트가 발생한 사실을 앱에 알리고 싶을 때 BroadcastReceiver에 통지합니다. 단말기 전원이 들어왔거나 디스크 용량 부족 등 시스템의 이벤트를 앱에 알리거나, 앱간의 연계를 위해 이벤트를 알리고 싶을 때 이용합니다. 

 

BroadcastReceiver는 브로드캐스트 Intent를 받았을 때의 처리를 onReceive에서 구현합니다.  어느 브로드캐스트 Intent를 받을지는 IntentFilter로 정의합니다.

 public abstract void onReceive(Context context, Intent intent); 


두 번째 인수로 전달되는 Intent는 Context.sendBroadcast() 등에서 보내진 브로드캐스트 Intent의 인스턴스입니다. 이제부터 Intent.getAction()을 호출해 액션의 이름을 가져오거나 주어진 데이터가 있을 때는 Intent.getExtras()를 호출해 Bundle을 가져오고 거기서 데이터를 추출합니다. 또한 onReceive()의 처리는 메인 스레드에서 수행되므로 처리에 시간이 걸려서는 안 됩니다. onReceive() 처리에 10초 이상 걸리는 경우 ANR(
 Application Not Responding 비정상 종료 및 애플리케이션 응답 없음 )이 발생해 프로세스가 강제로 종료됩니다.

 

BroadcastReceiver 등록

 

BroadcastReceiver를 등록하는 방법은 2가지가 있습니다. 우선 첫 번째는 AndroidManifest.xml에 receiver 태그를 이용해 등록하는 방법입니다.

 

<receiver android:name=".MyReceiver">
    <intent-filter>
        <action android:name="com.advanced_android.wakefulbroadcastreceiversample.TEST_ACTION"/>
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>

 

두 번째는 Context.registerReceiver를 이용해 실행 시 등록하는 방법입니다. 실행 시에 등록했을 때는 해제도 직접 해줄 필요가 있습니다. 액티비티의 onResume에서 등록했다면 onPause에서 unregisterReceive()를 호출해서 해제합니다.

 

private static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";

BroadcastReceiver mReceiver = new BroadcastReceiver() {

	@Override
	public void onReceive(Context context, Intent intent) {
		String action = intent.getAction();
		intent.getExtras();
		if (TextUtils.equals(action, VOLUME_CHANGED_ACTION)) {
		Toast.makeText(MainActivity.this, "음량이 변화했습니다", Toast.LENGTH_SHORT).show();
		}
    }
};

 

 

 

LocalBroadCastReceiver 이해

 

브로드캐스는 다른 앱에 송신하는 것이 가능하지만 경우에 따라서는 다른 앱에 알릴 필요 없이 앱내에서 완결시키고 싶을 때도 있습니다. 그럴 때는 로컬 브로드캐스트로 다른 앱에 알리지 않고 끝낼 수 있습니다. 로컬 브로드캐스트를 수신하려면 LocalBroadcastReceiver를 이용합니다.

 

LocalBroadCastReceiver의 장점으로는 다른 앱에 통지하지 않아 보안이 향상되고, 프로세스 간 통신을 하지 않아 성능이 향상되는 것을 들 수 있습니다. 

 

브로드캐스트를 수신해 처리할 때 주의할 점을 알아보자

 

안드로이드는 전력 소비를 줄이고자 사용자가 화면을 끄면 슬립 상태로 들어갑니다. 브로드캐스트를 수신해서 뭔가 시간이 오래 걸리는 처리를 한창 하는 중에 슬립되는 경우가 있습니다.

 

단말기 상태

 

단말기는 절전을 위해 항상 화면을 켜고 있지 않습니다. 또한 화면이 꺼졌을 때도 CPU가 동작하는 상태와 동작하지 않는상태가 있습니다. 일반적으로는 화면이 꺼지고 CPU가 동작하지 않는 상태를 슬립 상태라고 합니다. 이런 슬립 상태에서도 브로드캐스트를 받을 수 있지만 받은 후에 곧 바로 슬립하므로 시간이 걸리는 처리는 취소되어 계속할 수 없습니다. 그러므로 처리를 계속하려면 CPU를 깨울 필요가 있습니다. 이런 동작을 안드로이드 시스템 세계에서는 WakeLock을 얻는다고 합니다.

상태   대응하는 WakeLock   비고 
 화면 ON(밝다) CUP ON   FULL_WAKE_LOCK   Deprecated
 화면 ON(약간 어둡다), CPU ON   SCREEN_DIM_WAKE_LOCK  Deprecated
 화면 OFF, CUP ON   PARTIAL_WAKE_LOCK  
 화면 OFF, CUP OFF 없음  

 

 

FULL_WAKE_LOCK, SCREEN_DIM_WAKE_LOCK은 현재는 폐기 예정 상태라서 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON을 대신 사용하길 권장합니다.

 

한 가지 주의할 점은 WakeLock 해제를 잊으면 CPU가 슬립할 수 없어 계속 동작하고 전력을 낭비하게 된다는 것입니다. 이런 경우를 대비하기 위해서 지원 라이브러리에 WakefulBroadcastReceiver라는 클래스가 있어서 이 클래스를 이용하면 처리 중에만 WakeLock을 얻고, 처리가 끝나면 해제하는 일련의 흐름을 실행해 줍니다. 

 

덧붙여, WakefulBroadcastReceiver로 얻은 WakeLock은 60초로 타임아웃이 설정돼 있습니다. 따라서 처리 시간이 60초 이상 걸리면 슬립 상태로 전환됩니다.


Service

 

액티비티와 프래그먼트는화면에 표시되는 동안은 생존하지만 표시되지 않게 되면 onStop이나 onDestroy가 호출되어 폐기될 가능성이 있습니다. 다른 앱의 액티비티가 최상위에 오는 경우에도 백그라운드에서 처리를 계속하고 싶을 때에도 Service 컴포넌트를 사용합니다.  Service는 UI 없이 백그라운드 처리를 수행하는 컴포넌트입니다.

 

Service의 종류

 

service는 크게 세 종류로 나눌 수 있습니다. 첫 번째는 Context.startService()를 호출해 시작되는 서비스입니다. 두 번째는 Context.bindService()를 호출해서 Service에 바인드하는 종류의 서비스입니다. 세 번째는 AIDL(Android Interface Definition Language; 안드로이드 인터페이스 정의 언어)을 이용하는 서비스입니다.

 

1. 백그라운드에서 동작하는 Service

2. Binder를 통해 바인드하는 Service

3. AIDL로 앱을 연계할 수 있는 Service

 

생명주기와 콜백

 

Service에도 생명주기가 있지만 UI가 없어서 단순합니다.

 

메서드명 내용
onCreate   Service가 생성된 뒤에 콜백된다.
onStartCommand   Service가 시작된 뒤에 콜백된다.
onBind   Contect.bindService를 통해 이 Service가 바인드되는 경우에 호출된다. 또한 바인드 후, 서비스에 접속할 때는   ServiceCommection.onServiceConnected가 콜백된다.
onRebind  이 Service가 언바인드된 다음, 다시 접속햇을 때 콜백된다.
onUnbind  이 Service가 언바인드될 때 콜백된다.
onDestroy Service가 폐기되기 직전에 콜백된다.

 

 

Service가 폐기되는 타이밍을 알아보겠습니다. 바인드된 경우는 바인드한 모든 클라이언트로부터 언바인드됐을 때 폐기됩니다. Service가 바인드되지 않은 채 startService로 시작된 경우에는 명시적으로 Service.stopSelf()로 Service 자신이 스스로 종료하거나 다른 컴포넌트에서 Context.stopService()를 호출해 Service를 종료했을 때 폐기됩니다.

 

마지막으로, Service가 바인드되고 startService로 시작된 경우는 모든 클라이언트로부터 언바인드되고 또한 명시적으로 Service.stopSelf로 Service 자신이 스스로 종료하거나 다른 컴포넌트에서 Contect.stopService()를 호출해 Service를 종료했을 때 폐기됩니다.

 

 

반응형