BLOG ARTICLE content | 1 ARTICLE FOUND

  1. 2011.12.08 Content Providers 컨텐트 프로바이더 정의

안드로이드 Content Providers

 Content Providers
컨텐트 프로바이더는 모든 어플리케이션이 특정 데이터에 접근 하여 저장 및 검색이 가능하도록 한다. 이것은 어플리케이션들 간의 데이터 공유를 위한 유일한 방법이다. 모든 안드로이드 패키지가 접근할 수 있는 공통의 저장소가 없기 때문이다.

안드로이드는 공통의 데이터 타입을 위한 여러 컨텐트 프로바이더를 다루고 있다.(audio, video, images, 개인 주소록 등) 개발자는 android.provider 패키지에 리스트되어 있는 몇 가지 데이터 타입을 볼 수 있다. 개발자는 데이터 타입이 포함하는 데이터를 위해 이러한 프로바이더를 쿼리할 수 있다.(어떤 것은 데이터를 읽기 위해 적당한 퍼미션을 요구 한다.)

Note : 안드로이드 4.0은 캘린더 프로바이더를 소개한다.

개발자가 자신의 데이터를 공용으로 만들고자 한다면 두 가지 옵션이 있다. 개발자 자신의 컨텐트 프로바이더를 생성(컨텐트 프로바이더 서브클래스) 하거나 기 존재하는 프로바이더에 데이터를 추가하는 방법이다. 단 이때 동일 타입의 데이터를 컨트롤하는 프로바이더가 있고 개발자가 그것에 쓰기 권한을 가져야 한다는 전제조건이 있다. 

이 문서는 컨텐트 프로바이더를 사용하기 위한 방법을 설명한다. 기본사항에 대한 짧은 논의 후에 컨텐트 프로바이더를 쿼리 하는 방법과 프로바이더에 의해 컨트롤된 데이터를 수정하기 위한 방법 그리고, 개발자 자신의 컨텐트 프로바이더를 생성하는 방법을 알아보자.

Content Provider Basics
사실상 컨텐트 프로바이더가 커버하고 있는 데이터를 저장하는 방법은 설계자에게 달려있다. 하지만 모든 컨텐트 프로바이더가 프로바이더를 쿼리(데이터 추가, 변경, 삭제)하고 결과를 리턴하기 위해 공통의 인터페이스를 구현한다.

그것은 클라이언트가 대부분의 경우 ContentResolver 객체를 통해 간접적으로 사용하는 인터페이스이다. 개발자는 액티비티나 다른 어플리케이션 컴포넌트의 구현 내에서 getContentResolver()를 호출하므로써 ContentResolver를 얻는다.

ContentResolver cr = getContentResolver();

그런 다음 개발자는 사용하고자 하는 어떠한 컨텐트 프로바이더와 연동하기 위해 ContentResolver 메소드를 사용할 수 있다.

쿼리가 초기화 되면 안드로이드 시스템은 쿼리의 타겟이 되는 컨텐트 프로바이더를 식별하고, 그것이 실행중이라는 것을 보장한다. 시스템은 모든 컨텐트 프로바이더 객체를 인스턴스화 한다. 개발자는 직접 그것을 할 필요가 없다. 사실 개발자는 절대 컨텐트 프로바이더 객체를 직접적으로 다루지 않는다. 전형적으로 컨텐트 프로바이더의 각 타입에 대한 싱글 인스턴스가 존재한다. 하지만 그것은 다른 어플리케이션과 프로세스에 있는 다중 ContentResolver 객체와 커뮤니케이션할 수 있다. 프로세스간 연동은 ContentResolver와 ContentProvider 클래스에 의해 다루어진다.

The data model
컨텐트 프로바이더는 데이터를 데이터베이스 모델 상의 단순한 테이블에 배열한다. 이 모델의 각 로우는 레코드이고 각 컬럼은 특정 타입과 의미의 데이터이다. 예를 들어 People과 Phone number에 대한 정보는 다음과 같이 배열된다.


모든 레코드는 테이블 내에서 레코드를 유니크하게 식별하는 숫자형 _ID 필드를 포함한다. ID는 관련 테이블 내의 레코드를 매치하기 위해 사용된다. 예를 들어 테이블에서 Person의 phone번호를 찾거나 또 다른 테이블에서 해당 Person의 Picture를 찾기 위한 것이다.

쿼리는 각 필드의 컨텐츠를 읽기 위해 레코드나 컬럼간 이동이 가능한 Cursor 객체를 리턴한다. 그것은 각 데이터의 타입을 읽기 위한 특별한 메소드를 가진다. 필드를 읽기 위해서 개발자는 필드가 포함하는 타입이 무엇인지 알아야 한다. (쿼리 결과와 Cursor 객체에 대해서는 나중에 더 알아본다.)

URIs
각 컨텐트 프로바이더는 데이터 집합을 유니크하게 식별하는 Uri객체로 래핑된 공용 URI를 가진다. 다중 데이터 집합을 컨트롤하는 컨텐트 프로바이더는 각각을 위해 분리된 URI를 가진다. 프로바이더를 위한 모든 URI들은 문자열 "content://"로 시작한다. content: 스킴은 컨텐트 프로바이더에 의해 컨트롤 될 때 데이터를 식별한다.

개발자가 컨텐트 프로바이더를 정의하고 있다면 클라이언트 코드를 단순화하고 차후 업데이터를 더 깔끔하게 하기 원한다면 URI를 위한 상수 또한 정의하는 것이 좋은 생각이다. 안드로이드는 플랫폼의 모든 프로바이더를 위해 CONTENT_URI 상수를 정의한다. 예를 들어 People과 Phone number가 매치하는 테이블을 위한 URI와 People의 Picture를 가지고 있는 테이블을 위한 URI이다.(둘 다 Contants content provier에 의해 컨트롤 된다.)

android.provider.Contacts.Phones.CONTENT_URI
android.provider.Contacts.Photos.CONTENT_URI

URI상수는 컨텐트 프로바이더와의 모든 연동에 사용된다. 모든 ContentResolver 메소드는 URI를 첫번째 인수로 취한다. 그것은 ContentResolver가 어떤 프로바이더에게 이야기 해야하고 프로바이더의 어떤 테이블을 타겟으로 해야하는지 식별하는 것이다.

Querying a Content Provider
개발자는 컨텐트 프로바이더를 쿼리하기 위해 3개의 정보가 필요하다.
1. 프로바이더를 식별하는 URI
2. 개발자가 원하는 데이터 필드명
3. 그러한 필드들을 위한 데이터 타입들

개발자가 특정 레코드를 쿼리하는 중이라면 레코드를 위해서는 역시 ID가 필요하다.

Making the query
컨텐트 프로바이더를 쿼리하기 위해 개발자는 ContentResolver.query() 메소드나 Activity.managedQuery() 메소드를 사용할 수 있다. 두 개의 메소드는 모두 동일 집합의 인수를 취하고, Cursor 객체를 반환한다. 하지만 managedQuery()는 액티비티가 Cursor의 생명주기를 관리하도록 한다. 관리된 Cursor는 액티비티가 정지할 때 자신을 업로드 하거나 액티비티가 재시작할 때 쿼리를 다시 하는 것과 같은 모든 세부사항을 다룬다. 개발자는 Activity.startManagingCurosr()를 호출하므로써 액티비티가 관리되지 않은 Cursor 객체를 시작하도록 요청할 수 있다.

query()나 managedQuery()를 위한 첫 번째 인수는 프로바이더 URI이다. - 특정 ContentProvider와 데이터 집합을 식별하는 CONTENT_URI상수).

하나의 레코드를 위한 쿼리를 제한하기 위해 개발자는 URI에 레코드를 위한 _ID값을 추가할 수 있다. - 즉, URI의 path 파트의 마지막 세그먼트로써 ID와 매칭하는 문자열을 둔다. 예를 들어 ID가 23이면 URI는 다음과 같을 것이다.

content://..../23

몇 몇의 헬퍼 메소드가 있다. 특히 ContentUris.withAppendedId()와 Uri.withAppendedPath() 는 ID를 URI에 추가하기 쉽게 한다. 둘 다 static 메소드이며 추가된 ID를 가진 Uri객체를 반환한다. 예를 들어 개발자가 people contacts에서 레코드 23을 찾는 중이라면 다음과 같은 쿼리를 생성할 수 있다.


query()와 managedQuery() 메소드를 위한 다른 인수들은 더 많은 세부사항으로 쿼리를 구분한다.

그것들은 다음과 같다.
1. 리턴되어야 하는 데이터 컬럼의 이름. null값은 모든 컬럼을 반환한다. 그렇지 않으면 이름에 의해 리스트된 유일한 컬럼이 리턴된다. 플랫폼에 있는 모든 컨텐트 프로바이더는 컬럼을 위한 상수를 정의한다. 예를 들어 android.provider.Contacts.Phones 클래스는 phone 테이블에 있는 컬럼의 이름을 위한 상수를 정의한다. - _ID, NUMBER, NUMBER_KEY, NAME 등.

2. 어떤 로우가 리턴될 지 세부화한 필터. 이것은 SQL WHERE절 포맷이다.(WHERE 자체는 제외). null 값은 URI가 싱글 레코드를 위한 쿼리를 제한하지 않으면 모든 로우를 리턴한다.

3. Selection 인수들.

4. 리턴된 로우를 위한 정렬 순서. 이것은 SQL ORDER BY절 포맷이다. (ORDER BY 자체는 제외). null 값은 비순서화된 테이블을 위한 기본 순서로 레코드를 반환한다.

contact name과 phone number의 리스트를 검색하기 위한 예제 쿼리를 살펴보자. 


이 쿼리는 Contacts content provider의 People 테이블에서 데이터를 검색한다. 각 contact를 위해 성명, 기본 폰 번호, 그리고 유니크한 레코드 ID를 얻는다. 또한 각 레코드 필드의 _COUNT필드로써 리턴된 레코드의 수를 리포트 한다.

What a query returns
쿼리는 0이나 더 많은 데이터베이스 레코드를 리턴한다. 컬럼명과 그것들의 기본 순서, 그리고 데이터 타입들이 컨텐트 프로바이더에 기술된다. 하지만 모든 프로바이더가 _ID컬럼을 가지며 이 컬럼은 각 레코드에 대해 유니크한 숫자형 ID로 되어 있다. 또한 모든 프로바이더는 _COUNT컬럼으로 리턴된 레코드의 수를 리포트할 수 있다. 그것의 값은 모든 로우에 대해 같다.

여기 모든 로우에 대한 결과 집합의 예가 있다.


검색된 데이터는 결과셋을 통해 앞뒤로 반복하기 위해 사용할 수 있는 Cursor 객체에 의해 확인된다. 개발자는 오직 데이터를 읽기 위해 이 객체를 사용할 수 있다. 데이터를 추가하거나 수정, 삭제하기 위해 개발자는 ContentResolver 객체를 사용해야 한다.

Reading retrieved data
쿼리에 의해 리턴된 Cursor 객체는 결과 레코드셋에 접속할 수 있게 한다. 개발자가 ID에 의한 특정 레코드를 얻기 위해 쿼리를 했다면 결과셋은 오직 하나의 값만을 포함할 것이다. 그렇지 않으면 그것은 다중 결과값을 포함할 수 있다. 매치되는 데이터가 없을 경우 빈 결과값 또한 가능하다. 개발자는 레코드내 특정 필드로부터 데이터를 읽을 수 있다. 하지만 Cursor객체가 각 데이터 타입을 읽기위해 분리된 메소드를 가지기 때문에 getString(), getInt(), getFloat()과 같은 필드의 데이터 타입을 알아야 한다. 대부분의 타입들을 위해 문자열(strings)을 읽기 위한 메소드를 호출한다면 Cursor객체는 데이터의 String 표현을 돌려줄 것이다. Cursor는 컬럼의 인덱스나 컬럼명의 인덱스 번호로 부터 컬럼명을 요청하도록 한다.

다음 부분은 이미 일러스트된 쿼리로부터 이름과 폰번호를 읽는 것을 보여준다. 


쿼리가 이미지나 사운드와 같은 바이너리 데이터를 리턴할 수 있다면, 데이터는 content: URI를 기술하는 문자열이 될 수 있는 데이터를 위해 테이블이나 테이블 엔트리에 직접 삽입될 수 있다. content: URI는 데이터를 얻기위해 사용된다. 일반적으로 더 적은 양의 데이터(20~50K 또는 더 적은)는 대부분 테이블에 직접 삽입되고, Cursor.getBlob()을 호출해서 그것을 읽을 수 있다. 그것은 바이트 배열을 리턴한다.

테이블 엔트리가 content: URI라면 개발자는 직접적으로 파일을 열거나 읽어서는 안된다. 퍼미션 문제가 이것을 실패하도록 한다. 대신에 데이터를 읽기위해 사용할 수 있는 InputStream을 얻기위해 ContentResolver.openInputStream()을 호출해야 한다.

Modifying Data

컨텐트 프로바이더에 의해 유지된 데이터는 다음의 경우에 수정된다.
1. 새로운 레코드 삽입
2. 이미 존재하는 레코드에 새로운 값 삽입
3. 이미 존재하는 레코드를 업데이트하는 배치
4. 레코드 삭제

모든 데이터는 ContentResolver 메소드를 사용해서 수정할 수 있다. 약간의 컨텐트 프로바이더는 읽기 위해 하는 것보다 더 제한적인 쓰기 권한을 요구한다. 만약 컨텐트 프로바이더에 쓰기 권한을 가지지 않으면 ContentResolver 메소드는 실패할 것이다.

Adding records
컨텐트 프로바이더에 새로운 레코드를 추가하기 위해 처음 ContentValues 객체에 key-value 쌍의 map을 설정해야 한다. 각 key는 컨텐트 프로바이더의 컬럼명과 매치되고, value는 컬럼의 새로운 레코드를 위한 값이다. 다음으로 ContentResolver.insert()를 호출하여 프로바이더의 URI와 ContentValues map을 패스한다. 이 메소드는 새로운 레코드의 전체URI를 리턴한다. - 즉, 새로운 레코드를 위해 추가된 ID를 가진 프로바이더의 URI이다. 개발자는 쿼리하거나 새로운 레코드에 대한 Cursor를 얻거나 더 나아가서 레코드를 수정하기 위해 이 URI를 사용할 수 있다.
여기에 예가 있다.

Adding new values
일단 레코드가 존재하면 새로운 정보를 그것에 추가하거나 기존 정보를 수정할 수 있다. 예를 들어 상단의 예의 다음 단계는 폰 번호나 IM, e-mail주소와 같은 contact정보를 새로운 엔트리에 추가하는 것이다.

Contacts 데이터베이스에 레코드를 삽입하기 위한 최상의 방법은 테이블명을 추가하는 것이다. 이 테이블은 새로운 데이터가 레코드를 위해 URI로 가는 곳이다.(?) 그리고 나서 새로운 데이터 값을 추가하기 위해 수정된 URI를 사요하라. 각 Contacts테이블은 이러한 용도로 네임을 CONTENT_DIRECTORY 상수로써 노출한다. 다음 코드는 방금 생성된 레코드를 위한 폰번호와 e-mail주소를 추가하므로써 이전 예제의 내용에 이어진다.

개발자는 바이트 배열을 취하는 ContentValues.put()의 버전을 호출해서 적은 양의 바이너리 데이터를 테이블에 삽입할 수 있다. 그것은 작은 아이콘 같은 이미지나 짧은 오디오 클립에 유용하다. 하지만 개발자가 추가할 데이터가 사진애나 완전한 노래와 같은 아주 큰 바이너리라면 테이블에 데이터를 위한 content: URI를 넣어라. 그리고 파일의 URI와 함께 ContentResolver.openOutputStream()을 호출하라. 이것은 컨텐트 프로바이더가 파일에 데이터를 저장하여 레코드의 숨겨진 필드에 파일 경로를 기록하게 한다.

이러한 범주에서, 이미지, 오디오 그리고, 비디오 데이터를 제공하는 메인 프로바이더인 MediaStore 컨텐트 프로바이더 특별한 관습을 이용한다. 사진 캡션이나 찍혀진 날짜와 같은 바이너리 데이터에 대한 메타 정보를 얻기 위해 query()나 managedQuery()와 함께 사용된 동일 URI는 데이터 그 자체를 얻기 위해 openInputStream()과 함께 사용된다.
유사하게 메타데이터 정보를 MediaStore 레코드에 넣기 위해 insert()와 함께 사용된 동일 URI는 거기에 바이너리 데이터를 넣기 위해 openOutputStream()과 함께 사용된다. 다음 코드 부분은 이 관습에 대해 삽화된 것이다.

Batch updating records
레코드 그룹(예를 들어 모든 필드 안의 "NY"를 "New York"로 변경한다.)을 일괄 업데이트 하기 위해서 변경할 컬럼과 값과 함께 ContentResolver.update() 메소드를 호출하라.

Deleting a record
싱글 레코드를 삭제하기 위해 특정 로우의 URI와 함께 ContentResolver.delete()을 호출하라.

다중 로우들을 삭제하기 위해서 삭제할 레코드의 타입의 URI와 함께 ContentResolver.delete()을 호출하라. 예를 들어 android:provider.Contacts.People.CONTENT_URI)와 삭제할 로우들을 정의한 SQL WHERE절이다. 주의 할 점은 일반적인 타입을 삭제하는 중이거나 의도한 것보다 많은 레코드들이 삭제될 위험이 있다면 유효한 WHERE절을 포함하는 지를 확인하라.

Creating a Content Provider
컨텐트 프로바이더를 생성하기 위해 개발자는 다음과 같은 것을 해야 한다.
1. 데이터를 저장하기 위한 시스템을 설정하라. 대부분의 컨텐트 프로바이더는 안드로이드의 파일 저장 메소드나 SQLite 데이터베이스를 이용해서 데이터를 저장한다. 하지만 개발자는 원하는 어떠한 방법으로 데이터를 저장할 수 있다. 안드로이드는 개발자가 데이터베이스를 생성하고 그것을 관리할 수 있도록 SQLiteOpenHelper 클래스를 제공한다.

2. 데이터에 접근을 제공하기 위해 ContentProvider 클래스를 확장하라.
3. 어플리케이션을 위해 매니페스트 파일에 컨텐트 프로바이더를 선언하라.(AndroidManifest.xml)

다음 섹션들이 이러한 태스크 중 마지막 2가지에 대해서 설명한다.
Extending the ContentProvider class
개발자는 데이터를 다른 사람에게 노출하기 위해 ContentResolver와 Cursor 객체에 의해 기대되는 관습을 사용하면서 ContentProvider 서브클래스를 정의한다. 주로 이것은 ContentProvider 클래스에 선언되어 있는 6개의 추상 메소드를 구현하는 것을 의미한다.

query()
insert()
update()
delete()
getType()
onCreate()

query() 메소드는 요청된 데이터에 반복 접근할 수 있는 Cursor객체를 리턴해야 한다. Cursor 그 자체는 인터페이스이지만 안드로이드는 사용가능한 미리 준비된 Cursor 객체를 제공한다. 예를 들어 SQLiteCurosr는 SQLite 데이터베이스에 저장된 데이터를 반복 접근할 수 있다. 개발자는 SQLiteDatabase 클래스의 query() 메소드들 중 어떤 것을 호출해서 Cursor 객체를 얻는다. 데이터베이스에 저장되지 않은 데이터를 위해 MatrixCursor와 같은 다른 종류의 Cursor 구현도 있다.

이러한 컨텐트 프로바이더 메소드가 다른 프로세스나 스레드에 있는 다양한 ContentResolver 객체로부터 호출될 수 있기 때문에 그것들은 스레드 안전한 방식으로 구현되어야 한다.

예의 상, 개발자는 수정된 데이터가 있다는 것을 리스너에게 통보하기 위해 ContentResolver.notifyChange()를 호출하기 원할지 모른다.

서브클래스 그 자체를 정의하는 것 이상으로 클라이언트의 작업을 단순화하고 클래스를 더 접근가능하도록 만들기 위해서 취해야하는 단계들이 있다.

1. CONTENT_URI로 명명된 public static final Uri를 정의하라. 이것은 전체 컨텐트 프로바이더가 다루는 content: URI를 기술하는 스트링이다. 개발자는 이 값을 위해 유니크한 문자열을 정의해야 한다. 최상의 해결책은 컨텐트 프로바이더의 완전히 보증된 클래스명을 사용하는 것이다.(대문자로 생성되었다.) 예를 들어 TransportationProvider 클래스는 다음과 같이 정의될 수 있다.


만약 프로바이더가 서브테이블을 가진다면 각 서브테이블들을 위한 CONTENT_URI를 정의하라. 그것이 컨텐트 프로바이더를 식별하기 때문에 이러한 URI들은 모두 같은 authority를 가져야 하고 오직 그것들의 경로에 의해 구분되어야 한다. 예를 들어,

content: URI들의 개요를 위해 이 문서의 끝에 있는 Content URI Summary를 봐라. 

2. 컨텐트 프로바이더가 클라이언트에게 리턴할 컬럼명을 정의한다. 개발자가 데이터베이스를 사용한다면 이런 컬럼명들은 전형적으로 대표하는 SQL 데이터베이스 컬럼명과 구별한다. 또한 쿼리와 다른 인스트럭션에 있는 컬럼을 클라이언트가 기술하기 위해 사용할 수 있는 public static 문자열 상수를 정의한다.
레코드의 ID를 위해 "_id"(상수 _ID)로 명명된 integer컬럼을 포함해야 한다. 개발자는 URL과 같이 모든 레코드들 사이에서 유니크한 다른 필드를 가졌는지 여부에 상관없이 이 필드를 가져야 한다. SQLite 데이터베이스를 사용중이라면 _ID필드는 다음의 타입이 되어야 한다.

INTEGER PRIMARY KEY AUTOINCREMENT

AUTOINCREMENT 기술자는 옵셔널하다. 하지만 이것이 없이 SQLite는 ID 카운터 필드를 컬럼에 존재하는 가장 큰 수 위의 다음 수로 증가시킨다. 개발자가 마지막 로우를 삭제한다면 추가된 다음 로우는 삭제된 로우와 동일한 ID를 가질 것이다. AUTOINCREMENT는 데이터의 삭제 여부에 상관없이 다음 가장 큰 값으로 증가시킴으로써 이러한 현상이 발생하는 것을 피할 수 있다.

3. 각 컬럼의 데이터 타입을 주의깊게 문서화하라. 클라이언트는 데이터를 읽기위해 이 정보가 필요하다.

4. 개발자가 새로운 데이터 타입을 다루는 중이라면 ContentProvider.getType()의 구현에서 리턴하기 위해 새로운 MIME타입을 정의해야 한다. 타입은 요청이 getType()에 전송된 content: URI가 특정한 레코드에 제한되는지 여부에 따라 일부분 종속된다. 싱글레코드 및 다중레코드를 위한 MIME타입의 형태는 구분되어진다. 요청된 것을 결정할 수 있게 도움을 주는 Uri메소드를 사용하라. 여기에 각 타입을 위한 일반적인 포맷이 있다.


5. 개발자가 큰 비트맵 파일 같은 테이블 그 자체에 넣기에 상당히 큰 바이트 데이터를 노출하는 중이라면 클라이언트에 데이터를 노출하는 필드는 사실상 content: URI 문자열을 포함해야 한다. 이 필드는 데이터 파일에 클라이언트가 접속하도록 한다. 레코드는 또한 그 파일을 위해 디바이스 상에 정확한 파일 경로를 나열하는 "_data"라 명명된 또 다른 필드를 가져야 한다. 이 필드는 클라이언트가 아니라 ContentResolver에 의해 읽혀지도록 의도된 것이다. 클라이언트는 아이템을 위해 URI를 홀드하고 있는 사용자 관련 필드상에서 ContentResolver.openInputStream()을 호출할 것이다. ContentResolver는 그 레코드를 위해 "_data"필드를 요청할 것이다. 그리고, 그것이 클라이언트 보다 더 높은 권한을 가지기 때문에 파일에 직접 접근할 수 있어야 하고 클라이언트에 파일을 위한 읽기 랩퍼를 리턴해야 한다.

프라이빗 컨텐트 프로바이더 구현의 예를 들기 위해 SDK와 함께 배포된 샘플 어플리케이션인 Notepad의 NodePadProvider를 봐라.

Declaring the content provider
안드로이드 시스템이 개발한 컨텐트 프로바이더에 대해 알도록 하기 위해 AndroidManifest.xml 파일에 엘리먼트로 그것을 선언한다. 매니페스트에 선언되지 않은 컨텐트 프로바이더는 안드로이드 시스템에 보이지 않는다.

name 속성은 ContentProvider 서브클래스의 완전히 자격을 갖춘 이름이다. authorities 속성은 프로바이더를 식별하는 content: URI의 authority 파트이다. 예를 들어 ContentProvider 서브클래스는 AutoInfoProvider이다. 엘리먼트는 이와 같을 수 있다.


authorities 속성이 content: URI의 경로 부분에서 생략됨을 노트하라. 예를 들어 AutoInfoProvider가 여러 타입의 autos나 제조사들을 위한 서브테이블을 컨틀롤했다면 매니페스트에 선언되지 않을 것이다.


authority는 경로가 아니라 프로바이더를 식별하는 것이다. 개발자의 프로바이더는 선택한 어떠한 방식으로도 URI의 경로부분을 해석할 수 있다.

다른 속성은 읽기와 쓰기를 위해 권한 설정을 할 수 있고, 사용자에게 디스플레이되는 아이콘과 텍스트를 제공할 수 있으며 프로바이더를 가능하게도 불가능하게 할 수 있다. 데이터가 컨텐트 프로바이더의 다중 실행 버전 사이에서 동기화 될 필요가 없다면 multiprocess 속성을 "true"로 설정하라. 이것은 IPC를 수행할 필요를 없애면서 프로바이더의 인스턴스가 각 클라이언트의 프로세스에서 생성되도록 허가한다.

Content URI Summary
여기서는 content URI의 주요 부분에 대해 설명한다.


A. 데이터가 컨텐트 프로바이더에 의해 컨트롤된다는 것을 나타내는 표준 접두어

B. URI의 authority 부분. 컨텐트 프로바이더를 식별한다. 써드파티 어플리케이션을 위해서 이것은 완전 규격화되고 유니크성을 보장한 클래스명이어야 한다. authority는 엘리먼트의 authorities 속성에서 선언된다.

C. 어떤 종류의 데이터가 요청되는지 결정하기 위해 컨텐트 프로바이더가 사용하는 경로. 이것은 제로나 더 많은 세그먼트 길이가 될 수 있다. 컨텐트 프로바이더가 오직 trains라는 하나의 타입의 데이터만 노출한다면 그것은 생략될 수 있다. 프로바이더가 서브타입을 포함하여 여러타입을 노출한다면 그것은 여러 세그먼트 길이가 될 수 있다. 예를 들어, "land/bus", "land/train", "sea/ship", "sea/submarine"와 같은 4개의 가능성들을 준다.

D. 요청되는 특정 레코드의 ID. 이것은 요청된 레코드의 _ID이다. 요청이 싱글레코드로 제한되지 않으면 이 세그먼트와 구분 슬래시는 생략된다.
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST