반응형

 

 

Android를 개발하면서 4대 컴포넌트는 알아야한다는 이야기를 많이 한다.

4대 컴포넌트에 대해서는 나중에 주제를 잡고 자세히 작성해보기로 하고, 각각에 명칭에 대해서만 언급하면 다음과 같다.

 

1. Activity

2. Service

3. BroadcastReceiver

4. ContentProvider

 

이번 포스팅에서는 4대 컴포넌트 중 ContentProvider에 대해 기술하고자 한다.

 

ContentProvider란?

ContentProvider란 안드로이드 응용 프로그램을 구성하는 컴포넌트 중 하나로서 데이터를 제공하는 역할을 하며 응용 프로그램끼리 데이터를 공유하는 유일한 방법이다.

 

쉽게 이야기하면 A라는 앱과 B라는 앱 사이에 데이터를 공유한다고 생각하면 된다.

 

ContentProvider는 여러 블로그나 책에서 매우 어렵게 기술되어 있으나 필자가 학습한 바로는 AndroidManifest에 BroadCastReceiver를 등록하는 것 만큼이나 쉽게 느껴졌다.

 

 

그렇게 쉽게 어떻게?

ContentProvider는 위에서 기술한 바와 같이 아주 쉽게! AndroidManifest에 등록하는 것 만으로 절반은 끝나게 된다.

<provider
    android:name=".TestContentProvider"         <!-- 클래스 맵핑 -->
    android:authorities="com.dabal.external"    <!-- Uri Authority 정보 -->
    android:exported="true">                    <!-- 외부 접근을 위한 옵션 -->
</provider>

여기서 포인트는 "exported" 옵션이다. ContentProvider가 개발중인 앱 내에서만 호출한다면 해당 옵션은 별도 처리 하지 않아도 되지만, 본 블로그에서 포스팅하는 내용처럼 외부 앱에서 개발중인 앱을 호출하여 데이터를 제공해야하는 경우, exported 옵션은 무조건 true로 주어야 한다.

 

exported 옵션을 true로 주어야하는 이유에 대해서는 다음과 같다. 안드로이드 4.1 이하 버전에서는 별도의 옵션 처리 없이도 ContentProvider는 항상 외부로 공개되었으나, 4.2 이상 버전에서는 보안성 개선을 위해 디폴트로 ContentProvider는 공개되지 않으므로 개발자는 exported 옵션을 true로 지정하여 명시적으로 공개해야하기 때문이다.

 

 

 

ContentProvider의 구현은 어떻게 하나요?

ContentProvider의 구현은 ContentProvider를 상속받아 구현하면 된다.

상속받아 구현된 클래스는 앞서 AndroidManifest에 작성한 클래스 맵핑의 클래스가 된다.

 

그렇게 작성된 ContentProvider에 대해 코드로 설명해보면 아래와 같다.

/**
 * 외부 앱 연동 용 ContentProvider
 */
class TestContentProvider : ContentProvider() {

    /**
     * TestContentProvider 로드될 때 호출.
     * 제공할 데이터를 준비.
     */
    override fun onCreate(): Boolean {
        return false
    }

    /**
     * 외부에서 데이터 조회.
     * 보통 Uri, Projection(필드)로만 가져온다.
     */
    override fun query(uri: Uri?, projection: Array<String>?, selection: String?, 
    		selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
        return null
    }

    /**
     * 제공하는 데이터의 MIME 타입을 조사하는 메서드.
     * 정보의 개수에 따라 MIME 타입의 형식이 다름.
     * 강제 규칙은 아니지만 대체로 다음 형식에 작성
     *
     * 단수 : vnd.회사명.curosr.item/타입
     * 복수 : vnd.회사명.cursor.dir/타입
     */
    override fun getType(uri: Uri?): String? {
        val uriMatcher = TestUriMatcher()
        return uri?.let {
            uriMatcher.getExternalCallInfo(uri)
        } ?: return null
    }

    /**
     * 외부에서 데이터 등록, 수정, 삭제는 제한한다.
     */
    override fun insert(uri: Uri?, values: ContentValues?): Uri? {
        return null
    }

    /**
     * 외부에서 데이터 등록, 수정, 삭제는 제한한다.
     */
    override fun delete(uri: Uri?, selection: String?, selectionArgs: Array<String>?): Int {
        return 0
    }

    /**
     * 외부에서 데이터 등록, 수정, 삭제는 제한한다.
     */
    override fun update(uri: Uri?, values: ContentValues?, selection: String?, 
    	selectionArgs: Array<String>?): Int {
        return 0
    }

    /**
     * 외부에서 메서드 호출 접근 시 처리한다.
     */
    override fun call(method: String?, arg: String?, extras: Bundle?): Bundle {
        return Bundle()
    }
}

 

여기까지 잘 따라왔다면 외부에서 Uri 호출을 통해 쉽게 ContentProvider를 호출 할 수 있으며, 호출시에 각 메서드로 콜이 들어오게 된다.

 

 

 

외부에서 연동은 어떻게 하나요?

구현한 ContentProvider를 외부에서 호출하려면 ContentResolver를 통해 호출하면 된다.

ContentResolver를 이용한 ContentProvider호출은 ContentProvider 구현에 비해 의외로 단순하며 쉽다.

 

아래 샘플 코드를 보자.

private fun getData() {
	//ContentProvider 호출을 위한 Uri 생성
    val testUri:Uri = Uri.parse("content://authority/path/id")
  
    //ContentResolver 통한 데이터 반환
    val bundle:Bundle? = this.contentResolver.call(testUri, "getData()",null, null)
    val resultData: String? = bundle?.getString("result")
    resultString?.text = resultData
}

 

위의 코드에 간략하게 설명하면 다음과 같다.

AndroidManifest에 등록한 ContentProvider 호출을 위한 Uri를 생성한다. 이때 Uri는 AndroidManifest에 등록한 Authority를 기반으로 생성한다.

 

생성된 uri를 바탕으로 contentResolver를 통해 메서드 콜하여 해당 데이터를 반환한다.

 

 

마치며....

본 포스팅에서는 안드로이드 4대 컴포넌트 중 하나인 ContentProvider에 대해 기술하였다.

 

대부분의 블로그, 서적에서는 Sqlite를 통한 Database 데이터를 접근 및 제공하는 구조로 설명되어 있기때문에 onCreate, query, insert, delete, update의 동작 정의에 대해 포커싱 된 내용이 많다.

 

실제로도 Android 가이드 라인은 Sqlite 또는 Room을 통해 외부 앱과의 데이터 연동에 대해 정의하고 있다.

 

하지만 필자는 ContentProvider의 기본적인 사용 방법이 아닌 call 메서드를 이용한 Bundle 객체 수신 방식으로 사용해 보았다.

 

상세 내용은 다음 포스팅에서 기술하도록 하며, ContentProvider 설명에 대한 본 포스팅을 마치도록 한다.

반응형

+ Recent posts