| page.title= 콘텐츠 제공자 생성 |
| @jd:body |
| <div id="qv-wrapper"> |
| <div id="qv"> |
| |
| |
| <h2>이 문서의 내용</h2> |
| <ol> |
| <li> |
| <a href="#DataStorage">데이터 저장소 설계</a> |
| </li> |
| <li> |
| <a href="#ContentURI">콘텐츠 URI 설계</a> |
| </li> |
| <li> |
| <a href="#ContentProvider">ContentProvider 클래스 구현</a> |
| <ol> |
| <li> |
| <a href="#RequiredAccess">필수 메서드</a> |
| </li> |
| <li> |
| <a href="#Query">query() 메서드 구현</a> |
| </li> |
| <li> |
| <a href="#Insert">insert() 메서드 구현</a> |
| </li> |
| <li> |
| <a href="#Delete">delete() 메서드 구현</a> |
| </li> |
| <li> |
| <a href="#Update">update() 메서드 구현</a> |
| </li> |
| <li> |
| <a href="#OnCreate">onCreate() 메서드 구현</a> |
| </li> |
| </ol> |
| </li> |
| <li> |
| <a href="#MIMETypes">콘텐츠 제공자 MIME 유형 구현</a> |
| <ol> |
| <li> |
| <a href="#TableMIMETypes">테이블의 MIME 유형</a> |
| </li> |
| <li> |
| <a href="#FileMIMETypes">파일의 MIME 유형</a> |
| </li> |
| </ol> |
| </li> |
| <li> |
| <a href="#ContractClass">계약 클래스 구현</a> |
| </li> |
| <li> |
| <a href="#Permissions">콘텐츠 제공자 권한 구현</a> |
| </li> |
| <li> |
| <a href="#ProviderElement"><provider> 요소</a> |
| </li> |
| <li> |
| <a href="#Intents">인텐트 및 데이터 액세스</a> |
| </li> |
| </ol> |
| <h2>Key 클래스</h2> |
| <ol> |
| <li> |
| {@link android.content.ContentProvider} |
| </li> |
| <li> |
| {@link android.database.Cursor} |
| </li> |
| <li> |
| {@link android.net.Uri} |
| </li> |
| </ol> |
| <h2>관련 샘플</h2> |
| <ol> |
| <li> |
| <a href="{@docRoot}resources/samples/NotePad/index.html"> |
| 메모장 샘플 애플리케이션 |
| </a> |
| </li> |
| </ol> |
| <h2>참고 항목</h2> |
| <ol> |
| <li> |
| <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> |
| 콘텐츠 제공자 기본 정보</a> |
| </li> |
| <li> |
| <a href="{@docRoot}guide/topics/providers/calendar-provider.html"> |
| 캘린더 제공자</a> |
| </li> |
| </ol> |
| </div> |
| </div> |
| |
| |
| <p> |
| 콘텐츠 제공자는 데이터의 중앙 리포지토리로의 액세스를 관리합니다. Android 애플리케이션에서는 |
| 제공자를 하나 이상의 클래스로, 매니페스트 파일에 있는 요소와 함께 구현합니다. |
| 클래스 중 하나가 하위 클래스 |
| {@link android.content.ContentProvider}를 구현하며, |
| 이것이 제공자와 다른 애플리케이션 사이의 인터페이스입니다. 콘텐츠 제공자는 다른 애플리케이션에 데이터를 사용할 수 있게 해주도록 만들어져 있지만, |
| 물론 애플리케이션 내에 사용자로 하여금 제공자가 관리하는 데이터를 쿼리하고 수정할 수 있게 허용하는 |
| 액티비티가 있을 수도 있습니다. |
| </p> |
| <p> |
| 이 주제의 나머지 부분은 콘텐츠 제공자를 구축하기 위한 기본 단계 목록과 |
| 사용할 API 목록으로 이루어져 있습니다. |
| </p> |
| |
| |
| <!-- Before You Start Building --> |
| <h2 id="BeforeYouStart">구축을 시작하기 전에</h2> |
| <p> |
| 제공자 구축을 시작하기 전에 우선 다음 단계를 수행하십시오. |
| </p> |
| <ol> |
| <li> |
| <strong>콘텐츠 제공자가 필요한지 결정합니다</strong>. 다음 기능 중 하나 이상을 제공하려면 |
| 콘텐츠 제공자를 구축해야 합니다. |
| <ul> |
| <li>다른 애플리케이션에 복잡한 데이터나 파일을 제공하고자 하는 경우</li> |
| <li>사용자로 하여금 개발자의 앱에서 다른 앱으로 복잡한 데이터를 복사하도록 허용하고자 하는 경우</li> |
| <li>검색 프레임워크를 사용한 사용자 지정 검색 제안을 제공하고자 하는 경우</li> |
| </ul> |
| <p> |
| 용도가 본인의 애플리케이션 안에서로 완전히 한정되어 있는 경우에는 |
| 제공자가 SQLite 데이터베이스를 사용하도록 하지 <em>않아도</em> 됩니다. |
| </p> |
| </li> |
| <li> |
| 아직 읽지 않았다면, 지금 바로 |
| <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> |
| 콘텐츠 제공자 기본 정보</a>를 읽고 제공자에 대해 자세히 알아보십시오. |
| </li> |
| </ol> |
| <p> |
| 그런 다음, 다음 단계를 따라 제공자를 구축합니다. |
| </p> |
| <ol> |
| <li> |
| 데이터를 위한 원시 저장소를 설계합니다. 콘텐츠 제공자는 두 가지 방식으로 데이터를 제공합니다. |
| <dl> |
| <dt> |
| 파일 데이터 |
| </dt> |
| <dd> |
| 일반적으로 사진, 오디오 또는 동영상과 같은 |
| 파일에 들어가는 데이터입니다. 이런 파일을 애플리케이션의 비공개 |
| 공간에 저장합니다. 제공자는 다른 애플리케이션으로부터 온 파일 요청에 응답하여 |
| 해당 파일로의 핸들을 제공할 수 있습니다. |
| </dd> |
| <dt> |
| "구조적" 데이터 |
| </dt> |
| <dd> |
| 일반적으로 데이터베이스, 배열 또는 유사 구조에 들어가는 데이터입니다. |
| 이 데이터를 행과 열로 이루어진 테이블과 호환되는 형식으로 저장합니다. |
| 행은 사람이나 인벤토리의 항목과 같은 엔티티를 나타냅니다. |
| 열은 해당 엔티티에 대한 몇 가지 데이터, 예를 들어 사람 이름이나 항목 가격 등을 나타냅니다. |
| 이 유형의 데이터를 저장하는 보편적인 방법은 SQLite 데이터베이스 안에 저장하는 것이지만, |
| 모든 유형의 영구적인 저장소를 사용해도 됩니다. Android 시스템에서 사용할 수 있는 저장소 유형에 대해 자세히 알아보려면, |
| <a href="#DataStorage"> |
| 데이터 저장소 설계</a> 섹션을 참조하십시오. |
| </dd> |
| </dl> |
| </li> |
| <li> |
| {@link android.content.ContentProvider} 클래스와 |
| 필수 메서드의 구체적인 구현을 정의합니다. 이 클래스는 데이터와 나머지 Android 시스템 사이의 |
| 인터페이스입니다. 이 클래스에 관한 자세한 정보는 |
| <a href="#ContentProvider">ContentProvider 클래스 구현</a> 섹션을 참조하십시오. |
| </li> |
| <li> |
| 제공자의 권한 문자열, 그 콘텐츠 URI 및 열 이름을 정의합니다. |
| 제공자 애플리케이션이 인텐트를 처리하게 하려면, 인텐트 작업과 추가 데이터 및 |
| 플래그도 정의합니다. 데이터에 액세스하기를 원하는 애플리케이션에 요구할 권한도 |
| 정의합니다. 이 모든 값은 별도의 계약 클래스에서 상수로 정의하는 것을 고려해보는 |
| 것이 좋습니다. 이 클래스를 나중에 다른 개발자에게 노출할 수 있습니다. |
| 콘텐츠 URI에 관한 자세한 정보는 |
| <a href="#ContentURI">콘텐츠 URI 설계</a> 섹션을 참조하십시오. |
| 인텐트에 관한 자세한 정보는 |
| <a href="#Intents">인텐트 및 데이터 액세스</a> 섹션을 참조하십시오. |
| </li> |
| <li> |
| 샘플 데이터, 또는 제공자와 클라우드 기반 데이터 사이에서 |
| 데이터를 동기화할 수 있는 {@link android.content.AbstractThreadedSyncAdapter} 구현 등과 같이 |
| 다른 선택적 조각을 추가합니다. |
| </li> |
| </ol> |
| |
| |
| <!-- Designing Data Storage --> |
| <h2 id="DataStorage">데이터 저장소 설계</h2> |
| <p> |
| 콘텐츠 제공자는 구조화된 형식으로 저장된 데이터로의 인터페이스입니다. |
| 인터페이스를 생성하기 전에 우선 데이터 저장 방식부터 결정해야 합니다. |
| 데이터는 원하는 형식 아무 것으로나 저장할 수 있으며 그런 다음에 필요에 따라 해당 데이터를 읽고 쓸 인터페이스를 설계합니다. |
| </p> |
| <p> |
| 다음은 Android에서 사용할 수 있는 몇 가지 데이터 저장소 기술입니다. |
| </p> |
| <ul> |
| <li> |
| Android 시스템에는 Android 자체 제공자가 테이블 지향적 데이터를 |
| 저장하는 데 사용하는 SQLite 데이터베이스 API가 포함됩니다. |
| {@link android.database.sqlite.SQLiteOpenHelper} 클래스는 데이터베이스를 생성할 수 있게 돕고, |
| {@link android.database.sqlite.SQLiteDatabase} 클래스는 데이터베이스 액세스를 위한 |
| 기본 클래스입니다. |
| <p> |
| 리포지토리를 구현하기 위해 데이터베이스를 사용하지 않아도 된다는 점을 기억하십시오. |
| 제공자는 외부에 테이블 집합으로 나타나 관계적 데이터베이스와 비슷해 보이지만, |
| 이것은 제공자의 내부 구현에 필요한 것은 아닙니다. |
| </p> |
| </li> |
| <li> |
| 파일 데이터를 저장하는 데 있어 Android에는 다양한 파일 지향적 API가 있습니다. |
| 파일 저장소에 관해 자세히 알아보려면 |
| <a href="{@docRoot}guide/topics/data/data-storage.html">데이터 저장소</a> 주제를 읽어 보십시오. |
| 음악이나 동영상 등 미디어 관련 데이터를 제공하는 제공자를 설계하는 경우, |
| 제공자가 테이블 데이터와 파일을 조합 할 수 있습니다. |
| </li> |
| <li> |
| 네트워크 기반 데이터를 다루는 경우, {@link java.net} 및 |
| {@link android.net} 내의 클래스를 사용하십시오. 네트워크 기반 데이터를 |
| 데이터베이스와 같은 로컬 데이터 스토어와 동기화한 다음, 해당 데이터를 테이블이나 파일로 제공할 수도 있습니다. |
| <a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html"> |
| 샘플 동기화 어댑터</a> 샘플 애플리케이션이 이런 유형의 동기화를 보여줍니다. |
| </li> |
| </ul> |
| <h3 id="DataDesign"> |
| 데이터 설계 시 고려할 사항 |
| </h3> |
| <p> |
| 다음은 제공자의 데이터 구조를 설계할 때 유용한 몇 가지 팁입니다. |
| </p> |
| <ul> |
| <li> |
| 테이블 데이터는 언제나 제공자가 유지관리하는 "기본 키" 열을 |
| 각 행의 고유한 숫자 값으로 보유하고 있어야 합니다. 이 값을 사용하여 해당 행을 다른 테이블의 |
| 관련 행에 연결시킬 수 있습니다(이를 "외래 키"로 사용). 이 열에는 어느 이름이든 사용할 수 있지만 |
| {@link android.provider.BaseColumns#_ID BaseColumns._ID}를 사용하는 것이 가장 좋습니다. |
| 왜냐하면 제공자 쿼리 결과를 |
| {@link android.widget.ListView}에 연결하려면 검색된 열 중 하나가 |
| <code>_ID</code>라는 이름을 사용해야 하기 때문입니다. |
| </li> |
| <li> |
| 비트맵 이미지나 파일 지향적 데이터의 매우 큰 조각을 제공하려면 |
| 테이블 안에 직접 저장하기보다는 파일에 데이터를 저장한 뒤 |
| 간접적으로 제공합니다. 이렇게 하는 경우, 제공자의 사용자들에게 데이터에 액세스하려면 |
| {@link android.content.ContentResolver} 파일 메서드를 사용해야 한다고 알려야 합니다. |
| </li> |
| <li> |
| BLOB(Binary Large OBject) 데이터 유형을 사용하여 크기가 다르거나 |
| 구조가 다양한 데이터를 저장합니다. 예를 들어, BLOB 열을 사용하여 |
| <a href="http://code.google.com/p/protobuf">프로토콜 버퍼</a> 또는 |
| <a href="http://www.json.org">JSON 구조</a>를 저장할 수 있습니다. |
| <p> |
| BLOB를 사용하여 <em>스키마에 종속되지 않은</em> 테이블을 구현할 수도 있습니다. |
| 이 유형의 테이블에서는, 기본 키 열, MIME 유형 열 및 하나 이상의 일반적인 열을 BLOB로 정의합니다. |
| |
| BLOB 열에 있는 데이터의 의미는 MIME 유형 열에 있는 값으로 나타냅니다. |
| 이렇게 하면 같은 테이블에 여러 가지 행 유형을 저장할 수 있습니다. 연락처 제공자의 "데이터" 테이블 |
| {@link android.provider.ContactsContract.Data}가 |
| 스키마에 종속되지 않은 테이블의 한 가지 예입니다. |
| </p> |
| </li> |
| </ul> |
| <!-- Designing Content URIs --> |
| <h2 id="ContentURI">콘텐츠 URI 설계</h2> |
| <p> |
| <strong>콘텐츠 URI</strong>는 제공자에서 데이터를 식별하는 URI입니다. |
| 콘텐츠 URI에는 전체 제공자의 상징적인 이름(제공자의 <strong>권한</strong>)과 |
| 테이블 또는 파일을 가리키는 이름(<strong>경로</strong>)이 포함됩니다. |
| 선택 항목 ID 부분은 테이블 내의 개별적인 행을 가리킵니다. |
| {@link android.content.ContentProvider}의 모든 데이터 액세스 메서드는 |
| 콘텐츠 URI를 인수로 가집니다. 이를 통해 액세스할 테이블, 행 또는 파일을 결정할 수 있습니다. |
| </p> |
| <p> |
| 콘텐츠 URI의 기본 정보는 |
| <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> |
| 콘텐츠 제공자 기본 정보</a>에 설명되어 있습니다. |
| </p> |
| <h3>권한 설계</h3> |
| <p> |
| 제공자에는 보통 하나의 권한이 있으며, 이것이 Android 내부 이름 역할을 합니다. |
| 다른 제공자와의 충돌을 피하려면, 제공자 권한의 기반으로 인터넷 도메인 소유권(역방향)을 |
| 사용해야 합니다. 이 권장 사항은 Android 패키지 이름에도 적용되므로, |
| 제공자 권한을 제공자가 들어 있는 패키지의 이름 확장자로 정의해도 됩니다. |
| 예를 들어, Android 패키지 이름이 |
| <code>com.example.<appname></code>라면, 제공자에게 |
| <code>com.example.<appname>.provider</code> 권한을 부여해야 합니다. |
| </p> |
| <h3>경로 구조 설계</h3> |
| <p> |
| 개발자는 보통 권한으로부터 콘텐츠 URI를 생성할 때 개별적인 테이블을 가리키는 |
| 경로를 추가하는 방식을 사용합니다. 예를 들어, <em>table1</em>과 |
| <em>table2</em>라는 테이블이 있다면, 이전 예시의 권한을 조합하여 |
| 콘텐츠 URI<code>com.example.<appname>.provider/table1</code>와 |
| <code>com.example.<appname>.provider/table2</code>를 도출합니다. |
| |
| 경로는 하나의 세그먼트에 국한되지 않으며, 경로의 각 수준에 대한 테이블이 아니어도 됩니다. |
| </p> |
| <h3>콘텐츠 URI ID 처리</h3> |
| <p> |
| 규칙에 의하면, 제공자는 URI 맨 끝에서 행에 대한 ID 값이 있는 콘텐츠 URI를 허용하여 |
| 테이블 내 하나의 행으로의 액세스를 제공합니다. 또한 규칙에 의해 제공자는 |
| 이 ID 값을 테이블의 <code>_ID</code> 열에 일치시켜야 하며, |
| 일치한 행에 대하여 요청된 액세스 허가를 수행해야 합니다. |
| </p> |
| <p> |
| 이 규칙은 제공자에 액세스하는 앱을 위한 공통 설계 패턴을 세우는 데 유용합니다. |
| 앱이 제공자에 대한 쿼리를 수행하고 그 결과로 나온 {@link android.database.Cursor}를 |
| {@link android.widget.ListView}에 {@link android.widget.CursorAdapter}를 사용하여 표시합니다. |
| {@link android.widget.CursorAdapter}의 정의에 따르면 |
| {@link android.database.Cursor} 안의 열 중 하나는 <code>_ID</code>여야 합니다. |
| </p> |
| <p> |
| 그러면 사용자가 데이터를 살펴보거나 수정하기 위하여 |
| UI에서 표시된 여러 행 중 하나를 선택합니다. 앱은 {@link android.widget.ListView}를 지원하는 {@link android.database.Cursor}에서 해당하는 열을 가져오고, |
| 해당 열에 대한 <code>_ID</code> 값을 가져와서 |
| 콘텐츠 URI에 추가하고, 제공자에 액세스 요청을 전송합니다. 그런 다음 제공자는 |
| 사용자가 선택한 바로 그 행에 대해 쿼리 또는 수정 작업을 수행할 수 있습니다. |
| </p> |
| <h3>콘텐츠 URI 패턴</h3> |
| <p> |
| 수신되는 콘텐츠 URI에 대해 어떤 조치를 취할지 선택하는 데 도움이 되도록 하기 위해 제공자 API에 |
| 편의 클래스 {@link android.content.UriMatcher}가 |
| 포함되어 있습니다. 이는 콘텐츠 URI "패턴"을 정수값으로 매핑합니다. 이 정수값은 특정 패턴에 일치하는 |
| 콘텐츠 URI 또는 여러 URI에 대해 원하는 작업을 선택하는 데 <code>switch</code> 문에서 사용할 수 있습니다. |
| </p> |
| <p> |
| 콘텐츠 URI 패턴은 와일드카드 문자를 사용하는 콘텐츠 URI와 일치합니다. |
| </p> |
| <ul> |
| <li> |
| <strong><code>*</code>:</strong> 모든 길이의 모든 유효한 문자로 구성된 문자열과 일치합니다. |
| </li> |
| <li> |
| <strong><code>#</code>:</strong> 모든 길이의 숫자 문자로 구성된 문자열과 일치합니다. |
| </li> |
| </ul> |
| <p> |
| 콘텐츠 URI 처리의 설계와 코딩에 대한 예시로서 임의의 제공자를 들어 보겠습니다. |
| 이 제공자에는 권한 <code>com.example.app.provider</code>가 있고 |
| 이 권한이 테이블을 가리키는 다음 콘텐츠 URI를 인식합니다. |
| </p> |
| <ul> |
| <li> |
| <code>content://com.example.app.provider/table1</code>: <code>table1</code>이라는 테이블입니다. |
| </li> |
| <li> |
| <code>content://com.example.app.provider/table2/dataset1</code>: |
| <code>dataset1</code>이라는 테이블입니다. |
| </li> |
| <li> |
| <code>content://com.example.app.provider/table2/dataset2</code>: |
| <code>dataset2</code>라는 테이블입니다. |
| </li> |
| <li> |
| <code>content://com.example.app.provider/table3</code>: <code>table3</code>이라는 테이블입니다. |
| </li> |
| </ul> |
| <p> |
| 제공자는 추가된 행 ID가 있으면 이런 콘텐츠 URI도 인식합니다. |
| 예를 들어, <code>table3</code>에서 <code>1</code>이 식별한 행에 대한 |
| <code>content://com.example.app.provider/table3/1</code>이 이에 해당됩니다. |
| </p> |
| <p> |
| 가능한 콘텐츠 URI 패턴은 다음과 같습니다. |
| </p> |
| <dl> |
| <dt> |
| <code>content://com.example.app.provider/*</code> |
| </dt> |
| <dd> |
| 제공자에 있는 모든 콘텐츠 URI와 일치합니다. |
| </dd> |
| <dt> |
| <code>content://com.example.app.provider/table2/*</code>: |
| </dt> |
| <dd> |
| 테이블 <code>dataset1</code>과 |
| <code>dataset2</code>의 콘텐츠 URI와 일치하지만 <code>table1</code>이나 |
| <code>table3</code>에 대한 콘텐츠 URI와 일치하지 않습니다. |
| </dd> |
| <dt> |
| <code>content://com.example.app.provider/table3/#</code>: |
| <code>table3</code>의 단일 행에 대한 콘텐츠 URI와 일치합니다. 예를 들어, |
| <code>6</code>이 식별한 행에 대한 <code>content://com.example.app.provider/table3/6</code>이 이에 해당됩니다. |
| |
| </dt> |
| </dl> |
| <p> |
| 다음 코드 조각은 {@link android.content.UriMatcher} 작업에서 메서드의 작용 원리를 나타낸 것입니다. |
| 이 코드는 테이블에 대한 콘텐츠 URI 패턴 <code>content://<authority>/<path></code>와 |
| 단일 행에 대한 콘텐츠 URI 패턴 <code>content://<authority>/<path>/<id></code>를 사용하여 |
| 단일 행에 대한 URI와 전체 테이블에 대한 URI를 서로 다르게 처리합니다. |
| |
| </p> |
| <p> |
| {@link android.content.UriMatcher#addURI(String, String, int) addURI()} 메서드는 |
| 권한과 경로를 정수값으로 매핑합니다. 메서드 {@link android.content.UriMatcher#match(Uri) |
| match()}는 URI에 대한 정수값을 반환합니다. <code>switch</code> 문이 |
| 전체 테이블을 쿼리할 것인지, 하나의 레코드를 쿼리할 것인지 선택합니다. |
| </p> |
| <pre class="prettyprint"> |
| public class ExampleProvider extends ContentProvider { |
| ... |
| // Creates a UriMatcher object. |
| private static final UriMatcher sUriMatcher; |
| ... |
| /* |
| * The calls to addURI() go here, for all of the content URI patterns that the provider |
| * should recognize. For this snippet, only the calls for table 3 are shown. |
| */ |
| ... |
| /* |
| * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used |
| * in the path |
| */ |
| sUriMatcher.addURI("com.example.app.provider", "table3", 1); |
| |
| /* |
| * Sets the code for a single row to 2. In this case, the "#" wildcard is |
| * used. "content://com.example.app.provider/table3/3" matches, but |
| * "content://com.example.app.provider/table3 doesn't. |
| */ |
| sUriMatcher.addURI("com.example.app.provider", "table3/#", 2); |
| ... |
| // Implements ContentProvider.query() |
| public Cursor query( |
| Uri uri, |
| String[] projection, |
| String selection, |
| String[] selectionArgs, |
| String sortOrder) { |
| ... |
| /* |
| * Choose the table to query and a sort order based on the code returned for the incoming |
| * URI. Here, too, only the statements for table 3 are shown. |
| */ |
| switch (sUriMatcher.match(uri)) { |
| |
| |
| // If the incoming URI was for all of table3 |
| case 1: |
| |
| if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; |
| break; |
| |
| // If the incoming URI was for a single row |
| case 2: |
| |
| /* |
| * Because this URI was for a single row, the _ID value part is |
| * present. Get the last path segment from the URI; this is the _ID value. |
| * Then, append the value to the WHERE clause for the query |
| */ |
| selection = selection + "_ID = " uri.getLastPathSegment(); |
| break; |
| |
| default: |
| ... |
| // If the URI is not recognized, you should do some error handling here. |
| } |
| // call the code to actually do the query |
| } |
| </pre> |
| <p> |
| 또 다른 클래스, {@link android.content.ContentUris}가 |
| 콘텐츠 URI의 <code>id</code> 부분을 다루기 위한 편의 메서드를 제공합니다. 클래스 {@link android.net.Uri}와 |
| {@link android.net.Uri.Builder}에는 |
| 기존 {@link android.net.Uri} 개체를 구문 분석하고 새로운 개체를 구축하기 위한 편의 메서드가 포함되어 있습니다. |
| </p> |
| |
| <!-- Implementing the ContentProvider class --> |
| <h2 id="ContentProvider">ContentProvider 클래스 구현</h2> |
| <p> |
| {@link android.content.ContentProvider} 인스턴스는 |
| 다른 애플리케이션으로부터의 요청을 처리하여 구조화된 데이터 세트로의 액세스를 관리합니다. |
| 모든 형태의 액세서가 궁극적으로 {@link android.content.ContentResolver}를 호출하며, |
| 그러면 이것이 액세스 권한을 얻기 위해 구체적인 {@link android.content.ContentProvider} 메서드를 호출합니다. |
| </p> |
| <h3 id="RequiredAccess">필수 메서드</h3> |
| <p> |
| 추상 클래스 {@link android.content.ContentProvider}는 |
| 개발자가 나름의 구체적인 하위 클래스의 일부분으로 구현해야만 하는 여섯 가지 추상 메서드를 정의합니다. 이와 같은 메서드는 모두 |
| ({@link android.content.ContentProvider#onCreate() onCreate()}는 예외) |
| 콘텐츠 제공자에 액세스하려 시도 중인 클라이언트 애플리케이션이 호출합니다. |
| </p> |
| <dl> |
| <dt> |
| {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) |
| query()} |
| </dt> |
| <dd> |
| 제공자에서 데이터를 검색합니다. 인수를 사용하여 쿼리할 테이블과 반환할 열/행, 결과의 정렬 순서를 선택합니다. |
| |
| 데이터를 {@link android.database.Cursor} 개체로 반환합니다. |
| </dd> |
| <dt> |
| {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} |
| </dt> |
| <dd> |
| 제공자에 새로운 행을 삽입합니다. 인수를 사용하여 대상 테이블을 선택하고 |
| 사용할 열 값을 가져옵니다. |
| 새로 삽입된 행에 대한 콘텐츠 URI를 반환합니다. |
| </dd> |
| <dt> |
| {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) |
| update()} |
| </dt> |
| <dd> |
| 제공자 내의 기존 행을 업데이트합니다. 인수를 사용하여 |
| 업데이트할 테이블과 행을 선택하고 업데이트한 열 값을 가져옵니다. 업데이트한 행 개수를 반환합니다. |
| </dd> |
| <dt> |
| {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} |
| </dt> |
| <dd> |
| 제공자에서 행을 삭제합니다. 인수를 사용하여 삭제할 테이블과 행을 선택합니다. |
| 삭제한 행 개수를 반환합니다. |
| </dd> |
| <dt> |
| {@link android.content.ContentProvider#getType(Uri) getType()} |
| </dt> |
| <dd> |
| 콘텐츠 URI에 상응하는 MIME 유형을 반환합니다. 이 메서드는 |
| <a href="#MIMETypes">콘텐츠 제공자 MIME 유형</a> 섹션에 더 자세하게 설명되어 있습니다. |
| </dd> |
| <dt> |
| {@link android.content.ContentProvider#onCreate() onCreate()} |
| </dt> |
| <dd> |
| 제공자를 초기화합니다. Android 시스템은 제공자를 생성한 직후 |
| 이 메서드를 호출합니다. |
| {@link android.content.ContentResolver} 개체가 제공자에 액세스하려고 시도할 때까지는 제공자가 생성된 것이 아니라는 점을 유의하십시오. |
| </dd> |
| </dl> |
| <p> |
| 이와 같은 메서드에는 동일하게 이름 붙여진 |
| {@link android.content.ContentResolver} 메서드와 같은 서명이 있다는 것을 눈여겨 보십시오. |
| </p> |
| <p> |
| 이러한 메서드의 구현에는 다음과 같은 내용을 감안해야 합니다. |
| </p> |
| <ul> |
| <li> |
| 이런 메서드는 모두({@link android.content.ContentProvider#onCreate() onCreate()}는 예외) |
| 한꺼번에 여러 스레드가 호출할 수 있으므로, 스레드로부터 안전해야 합니다. |
| 다중 스레드에 대한 자세한 내용은 |
| <a href="{@docRoot}guide/components/processes-and-threads.html"> |
| 프로세스 및 스레드</a> 주제를 참조하십시오. |
| </li> |
| <li> |
| {@link android.content.ContentProvider#onCreate() |
| onCreate()}에서는 긴 작업을 수행하는 것을 삼가는 것이 좋습니다. 실제로 필요할 때까지 초기화 작업을 미뤄두십시오. |
| 이 내용은 <a href="#OnCreate">onCreate() 메서드 구현</a> |
| 섹션에서 더욱 자세히 논의합니다. |
| </li> |
| <li> |
| 이와 같은 메서드는 반드시 구현해야 하는 것이지만, |
| 예상되는 데이터 유형을 반환하는 것 외에 달리 코드가 해야 할 일은 없습니다. |
| 예를 들어 몇몇 테이블에 다른 애플리케이션이 데이터를 삽입하지 못하도록 방지하려고 합니다. 이렇게 하려면, |
| {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}로의 |
| 호출을 무시하고 0을 반환하면 됩니다. |
| </li> |
| </ul> |
| <h3 id="Query">query() 메서드 구현</h3> |
| <p> |
| |
| {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) |
| ContentProvider.query()} 메서드는 {@link android.database.Cursor} 개체를 반환해야 하고, 그렇지 못할 경우 |
| {@link java.lang.Exception}을 발생시킵니다. SQLite 데이터베이스를 데이터 저장소로 사용하는 경우, |
| {@link android.database.sqlite.SQLiteDatabase} 클래스의 <code>query()</code> 메서드 중 하나로 반환되는 {@link android.database.Cursor}를 |
| 반환하기만 하면 됩니다. |
| 쿼리가 어느 행에도 일치하지 않으면, {@link android.database.Cursor#getCount()} 메서드가 0을 반환하는 |
| {@link android.database.Cursor} 인스턴스를 반환해야 합니다. |
| <code>null</code>을 반환하는 것은 쿼리 과정 중에 내부 오류가 발생했을 때뿐입니다. |
| </p> |
| <p> |
| SQLite 데이터베이스를 데이터 저장소로 사용하지 않는 경우, {@link android.database.Cursor}의 |
| 구체적인 하위 클래스 중 하나를 사용하십시오. 예를 들어, {@link android.database.MatrixCursor} 클래스는 |
| 각 행이 {@link java.lang.Object} 배열인 커서를 구현합니다. 이 클래스에서는 |
| {@link android.database.MatrixCursor#addRow(Object[]) addRow()}를 사용하여 새 행을 추가합니다. |
| </p> |
| <p> |
| Android 시스템이 프로세스 경계를 가로질러 {@link java.lang.Exception}을 |
| 통신으로 전달할 수 있어야 한다는 점을 유의하십시오. Android가 이 작업을 할 수 있는 경우는 |
| 쿼리 오류 처리에 유용할 수 있는 다음과 같은 예외에 해당될 때입니다. |
| </p> |
| <ul> |
| <li> |
| {@link java.lang.IllegalArgumentException} |
| (제공자가 유효하지 않은 콘텐츠 URI를 수신할 경우 이 예외를 발생시킬 수 있습니다.) |
| </li> |
| <li> |
| {@link java.lang.NullPointerException} |
| </li> |
| </ul> |
| <h3 id="Insert">insert() 메서드 구현</h3> |
| <p> |
| {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 메서드는 |
| {@link android.content.ContentValues} 인수의 값을 이용하여 |
| 적절한 테이블에 새 행을 추가합니다. 열 이름이 {@link android.content.ContentValues} 인수에 없는 경우, |
| 제공자 코드 또는 데이터베이스 스키마 내에 그에 대한 기본값을 제공하는 것이 좋을 수도 있습니다. |
| |
| </p> |
| <p> |
| 이 메서드가 새 행에 대한 콘텐츠 URI를 반환하는 것이 정상입니다. 이것을 구성하려면 새 행의 |
| <code>_ID</code>(또는 다른 기본 키) 값을 테이블의 콘텐츠 URI에 추가하며, 이때 |
| {@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()}를 사용합니다. |
| </p> |
| <h3 id="Delete">delete() 메서드 구현</h3> |
| <p> |
| {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} 메서드의 경우 |
| 데이터 저장소에서 물리적으로 행을 삭제하지 않아도 됩니다. |
| 제공자와 동기화 어댑터를 함께 사용하고 있는 경우, |
| 삭제된 행을 완전히 제거하기보다는 "삭제" 플래그로 표시하는 방법을 고려해볼 만합니다. |
| 동기화 어댑터가 삭제된 행을 확인한 다음, 이를 제공자에서 삭제하기 전에 우선 서버에서 제거합니다. |
| </p> |
| <h3 id="Update">Update() 메서드 구현</h3> |
| <p> |
| {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) |
| update()} 메서드는{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}가 사용하는 것과 같은 {@link android.content.ContentValues} 인수와 |
| |
| {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()}와 {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) |
| ContentProvider.query()}가 사용하는 것과 같은 <code>selection</code> 및 <code>selectionArgs</code> 인수를 |
| 취합니다. |
| 이 때문에 이와 같은 메서드 사이에서 코드를 다시 사용할 수 있게 해주기도 합니다. |
| </p> |
| <h3 id="OnCreate">onCreate() 메서드 구현</h3> |
| <p> |
| Android 시스템은 제공자를 시작할 때 {@link android.content.ContentProvider#onCreate() |
| onCreate()}를 호출합니다. 이 메서드에서는 빠르게 실행되는 초기화만 수행해야 하며, |
| 데이터베이스 생성과 데이터 로딩은 제공자가 실제로 데이터에 대한 요청을 받을 때까지 미뤄두어야 합니다. |
| |
| {@link android.content.ContentProvider#onCreate() onCreate()}에서 긴 작업을 수행하면 |
| 제공자의 시동 속도가 느려집니다. 이 때문에 제공자에서 다른 애플리케이션으로 전달되는 응답도 따라서 느려집니다. |
| |
| </p> |
| <p> |
| 예를 들어, SQLite 데이터베이스를 사용하는 경우 |
| {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}에서 |
| 새로운 {@link android.database.sqlite.SQLiteOpenHelper} 개체를 생성하고, |
| 그런 다음 데이터베이스를 처음 열 때 SQL 테이블을 생성할 수 있습니다. 이를 용이하게 하기 위해 |
| {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase |
| getWritableDatabase()}를 처음 호출하면 이것이 자동으로 |
| {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) |
| SQLiteOpenHelper.onCreate()} 메서드를 호출합니다. |
| </p> |
| <p> |
| 다음 두 개의 조각은 |
| {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}와 |
| {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) |
| SQLiteOpenHelper.onCreate()} 사이의 상호 작용을 나타낸 것입니다. 첫 번째 조각은 |
| {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}의 구현입니다. |
| </p> |
| <pre class="prettyprint"> |
| public class ExampleProvider extends ContentProvider |
| |
| /* |
| * Defines a handle to the database helper object. The MainDatabaseHelper class is defined |
| * in a following snippet. |
| */ |
| private MainDatabaseHelper mOpenHelper; |
| |
| // Defines the database name |
| private static final String DBNAME = "mydb"; |
| |
| // Holds the database object |
| private SQLiteDatabase db; |
| |
| public boolean onCreate() { |
| |
| /* |
| * Creates a new helper object. This method always returns quickly. |
| * Notice that the database itself isn't created or opened |
| * until SQLiteOpenHelper.getWritableDatabase is called |
| */ |
| mOpenHelper = new MainDatabaseHelper( |
| getContext(), // the application context |
| DBNAME, // the name of the database) |
| null, // uses the default SQLite cursor |
| 1 // the version number |
| ); |
| |
| return true; |
| } |
| |
| ... |
| |
| // Implements the provider's insert method |
| public Cursor insert(Uri uri, ContentValues values) { |
| // Insert code here to determine which table to open, handle error-checking, and so forth |
| |
| ... |
| |
| /* |
| * Gets a writeable database. This will trigger its creation if it doesn't already exist. |
| * |
| */ |
| db = mOpenHelper.getWritableDatabase(); |
| } |
| } |
| </pre> |
| <p> |
| 그 다음 조각은 |
| 도우미 클래스를 포함한 {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) |
| SQLiteOpenHelper.onCreate()}의 구현입니다. |
| </p> |
| <pre class="prettyprint"> |
| ... |
| // A string that defines the SQL statement for creating a table |
| private static final String SQL_CREATE_MAIN = "CREATE TABLE " + |
| "main " + // Table's name |
| "(" + // The columns in the table |
| " _ID INTEGER PRIMARY KEY, " + |
| " WORD TEXT" |
| " FREQUENCY INTEGER " + |
| " LOCALE TEXT )"; |
| ... |
| /** |
| * Helper class that actually creates and manages the provider's underlying data repository. |
| */ |
| protected static final class MainDatabaseHelper extends SQLiteOpenHelper { |
| |
| /* |
| * Instantiates an open helper for the provider's SQLite data repository |
| * Do not do database creation and upgrade here. |
| */ |
| MainDatabaseHelper(Context context) { |
| super(context, DBNAME, null, 1); |
| } |
| |
| /* |
| * Creates the data repository. This is called when the provider attempts to open the |
| * repository and SQLite reports that it doesn't exist. |
| */ |
| public void onCreate(SQLiteDatabase db) { |
| |
| // Creates the main table |
| db.execSQL(SQL_CREATE_MAIN); |
| } |
| } |
| </pre> |
| |
| |
| <!-- Implementing ContentProvider MIME Types --> |
| <h2 id="MIMETypes">ContentProvider MIME 유형 구현</h2> |
| <p> |
| {@link android.content.ContentProvider} 클래스에는 MIME 유형을 반환하는 두 가지 메서드가 있습니다. |
| </p> |
| <dl> |
| <dt> |
| {@link android.content.ContentProvider#getType(Uri) getType()} |
| </dt> |
| <dd> |
| 모든 제공자에 대해 구현해야 하는 필수 메서드 중 하나입니다. |
| </dd> |
| <dt> |
| {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} |
| </dt> |
| <dd> |
| 제공자가 파일을 제공하는 경우 구현해야 하는 메서드입니다. |
| </dd> |
| </dl> |
| <h3 id="TableMIMETypes">테이블의 MIME 유형</h3> |
| <p> |
| {@link android.content.ContentProvider#getType(Uri) getType()} 메서드는 |
| 콘텐츠 URI 인수가 반환하는 데이터 유형을 설명하는 MIME 형식의 {@link java.lang.String}을 반환합니다. |
| {@link android.net.Uri} 인수는 특정 URI라기보다 패턴일 수 있습니다. |
| 이 경우, 이 패턴과 일치하는 콘텐츠 URI와 연관된 유형의 데이터를 반환해야 합니다. |
| |
| </p> |
| <p> |
| 텍스트, HTML 또는 JPEG와 같은 보편적인 유형의 데이터라면 |
| {@link android.content.ContentProvider#getType(Uri) getType()}이 |
| 해당 데이터에 대한 표준 MIME 유형을 반환하는 것이 정상입니다. 이러한 표준 유형의 전체 목록은 |
| <a href="http://www.iana.org/assignments/media-types/index.htm">IANA MIME 미디어 유형</a> |
| 웹사이트에서 확인할 수 있습니다. |
| </p> |
| <p> |
| 테이블 데이터의 행 하나 또는 여러 행을 가리키는 콘텐츠 URI의 경우, |
| {@link android.content.ContentProvider#getType(Uri) getType()}이 Android의 공급업체별 MIME 형식에서 |
| MIME 형식을 반환해야 합니다. |
| </p> |
| <ul> |
| <li> |
| 유형 부분: <code>vnd</code> |
| </li> |
| <li> |
| 하위 유형 부분: |
| <ul> |
| <li> |
| URI 패턴이 하나의 행에 대한 것일 경우: <code>android.cursor.<strong>item</strong>/</code> |
| </li> |
| <li> |
| URI 패턴이 하나 이상의 행에 대한 것일 경우: <code>android.cursor.<strong>dir</strong>/</code> |
| </li> |
| </ul> |
| </li> |
| <li> |
| 제공자별 부분: <code>vnd.<name></code>.<code><type></code> |
| <p> |
| 개발자가 <code><name></code>과 <code><type></code>을 제공합니다. |
| <code><name></code> 값은 전체적으로 고유해야 하고, |
| <code><type></code> 값은 상응하는 URI 패턴에 고유해야 |
| 합니다. <code><name></code>으로 좋은 예는 회사 이름이나 |
| 애플리케이션의 Android 패키지 이름을 들 수 있습니다. |
| <code><type></code>으로 좋은 예는 URI와 연관된 테이블을 식별하는 |
| 문자열을 들 수 있습니다. |
| </p> |
| |
| </li> |
| </ul> |
| <p> |
| 예를 들어 어떤 제공자의 권한이 |
| <code>com.example.app.provider</code>이고, 이것이 |
| <code>table1</code>이라는 테이블을 노출하는 경우, <code>table1</code>의 여러 행에 대한 MIME 유형은 다음과 같습니다. |
| </p> |
| <pre> |
| vnd.android.cursor.<strong>dir</strong>/vnd.com.example.provider.table1 |
| </pre> |
| <p> |
| <code>table1</code>의 행 하나의 경우, MIME 유형은 다음과 같습니다. |
| </p> |
| <pre> |
| vnd.android.cursor.<strong>item</strong>/vnd.com.example.provider.table1 |
| </pre> |
| <h3 id="FileMIMETypes">파일의 MIME 유형</h3> |
| <p> |
| 제공자가 파일을 제공하는 경우, |
| {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}를 구현합니다. |
| 이 메서드는 제공자가 주어진 콘텐츠 URI에 대해 반환할 수 있는 파일에 대한 MIME 유형의 {@link java.lang.String} 배열을 반환합니다. |
| 제공하는 MIME 유형을 MIME 유형 필터 인수 기준으로 필터링해야 |
| 클라이언트가 처리하고자 하는 MIME 유형만 반환할 수 있습니다. |
| </p> |
| <p> |
| 예를 들어, 사진 이미지를 <code>.jpg</code>, |
| <code>.png</code> 및 <code>.gif</code> 형식의 파일로 제공하는 제공자가 있다고 하겠습니다. |
| 애플리케이션이 필터 문자열 <code>image/*</code>("이미지"인 어떤 것)로 {@link android.content.ContentResolver#getStreamTypes(Uri, String) |
| ContentResolver.getStreamTypes()}를 호출하면 |
| |
| {@link android.content.ContentProvider#getStreamTypes(Uri, String) |
| ContentProvider.getStreamTypes()} 메서드가 다음과 같은 배열을 반환하는 것이 정상입니다. |
| </p> |
| <pre> |
| { "image/jpeg", "image/png", "image/gif"} |
| </pre> |
| <p> |
| 앱이 <code>.jpg</code> 파일에만 관심이 있는 경우에는 |
| 필터 문자열 <code>*\/jpeg</code>으로 {@link android.content.ContentResolver#getStreamTypes(Uri, String) |
| ContentResolver.getStreamTypes()}를 호출할 수 있습니다. 그러면 |
| {@link android.content.ContentProvider#getStreamTypes(Uri, String) |
| ContentProvider.getStreamTypes()}가 다음과 같이 반환하는 것이 정상입니다. |
| <pre> |
| {"image/jpeg"} |
| </pre> |
| <p> |
| 제공자가 필터 문자열에서 요청한 MIME 유형 중 제공하는 것이 없는 경우, |
| {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}가 |
| <code>null</code>을 반환하는 것이 정상입니다. |
| </p> |
| |
| |
| <!-- Implementing a Contract Class --> |
| <h2 id="ContractClass">계약 클래스 구현</h2> |
| <p> |
| 계약 클래스는 <code>public final</code> 클래스로, 이 안에 URI, 열 이름, MIME 유형의 |
| 상수 정의 및 제공자에 관련된 다른 메타 데이터가 들어 있습니다. |
| 이 클래스는 URI, 열 이름 등의 실제 값에 변경된 내용이 있더라도 |
| 제공자에 올바르게 액세스할 수 있도록 보장하여 제공자와 |
| 다른 애플리케이션 사이의 계약을 확립합니다. |
| </p> |
| <p> |
| 계약 클래스가 개발자에게 유용한 이유는 또 있습니다. 이 클래스는 보통 자신의 상수 이름으로 |
| 니모닉 이름을 가지기 때문에 개발자가 열 이름 또는 URI에 잘못된 값을 사용할 가능성이 덜합니다. |
| 이것도 클래스의 일종이기 때문에 Javadoc 문서를 포함할 수 있습니다. |
| Eclipse와 같은 통합 개발 환경은 계약 클래스의 상수 이름을 자동 완성하고 |
| 해당 상수에 대한 Javadoc을 표시할 수 있습니다. |
| </p> |
| <p> |
| 개발자가 애플리케이션에서 계약 클래스의 클래스 파일에 액세스할 수는 없지만 |
| 여러분이 제공하는 <code>.jar</code> 파일에서 이를 애플리케이션 안으로 정적으로 컴파일링할 수 있습니다. |
| </p> |
| <p> |
| {@link android.provider.ContactsContract} 클래스와 |
| 이에 중첩된 클래스가 계약 클래스의 예시입니다. |
| </p> |
| <h2 id="Permissions">콘텐츠 제공자 권한 구현</h2> |
| <p> |
| Android 시스템의 모든 측면에 대한 권한과 액세스는 |
| <a href="{@docRoot}guide/topics/security/security.html">보안 및 권한</a> 주제에 설명되어 있습니다. |
| <a href="{@docRoot}guide/topics/data/data-storage.html">데이터 저장소</a> 주제에서도 |
| 다양한 유형의 저장소에 적용되는 보안 및 권한을 설명하고 있습니다. |
| 간략히 말해 요점은 다음과 같습니다. |
| </p> |
| <ul> |
| <li> |
| 기본적으로, 기기의 내부 저장소에 저장된 데이터 파일은 |
| 본인의 애플리케이션과 제공자 전용입니다. |
| </li> |
| <li> |
| 본인이 생성한 {@link android.database.sqlite.SQLiteDatabase} 데이터베이스는 |
| 본인의 애플리케이션과 제공자만의 비공개 데이터입니다. |
| </li> |
| <li> |
| 기본적으로 외부 저장소에 저장하는 데이터 파일은 <em>공개</em>이고 |
| <em>누구나 읽을 수 있습니다</em>. 외부 저장소에 있는 파일로의 액세스를 제공하는 데 콘텐츠 제공자를 쓸 수는 |
| 없습니다. 다른 애플리케이션이 다른 API 호출을 사용하여 해당 파일을 읽고 쓸 수 있기 때문입니다. |
| </li> |
| <li> |
| 기기의 내부 저장소에 있는 파일 또는 SQLite 데이터베이스를 열거나 생성하기 위한 메서드 호출은 |
| 다른 모든 애플리케이션에 읽기 및 쓰기 액세스 권한을 허가할 가능성이 있습니다. |
| 내부 파일이나 데이터베이스를 제공자의 리포지토리로 사용하고 |
| "누구나 읽을 수 있는" 또는 "누구나 쓸 수 있는" 액세스를 부여하면 |
| 매니페스트에서 제공자에 대해 설정한 권한이 데이터를 보호하지 못합니다. |
| 내부 저장소 안에 있는 파일과 데이터베이스에 대한기본 액세스는 "비공개"이며, 제공자의 리포지토리가 이것을 변경하면 안 됩니다. |
| </li> |
| </ul> |
| <p> |
| 데이터로의 액세스를 제어하기 위해 콘텐츠 제공자 권한을 쓰고자 하는 경우, |
| 데이터를 내부 파일, SQLite 데이터베이스 또는 "클라우드"(예: 원격 서버) 안의 |
| 내부 파일로 저장해야 하고, 파일과 데이터베이스를 애플리케이션만의 비공개로 유지해야 합니다. |
| </p> |
| <h3>권한 구현</h3> |
| <p> |
| 기본 데이터가 비공개라고 하더라도 모든 애플리케이션이 제공자를 읽고 제공자에 쓸 수 있습니다. |
| 기본적으로 제공자에는 권한이 설정되어 있지 않기 때문입니다. 이를 변경하려면, |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code> 요소의 속성이나 하위 요소를 사용하여 |
| 매니페스트 파일에 있는 제공자의 권한을 설정합니다. 권한은 제공자 전체에 적용되도록 설정할 수도 있고, |
| 특정 테이블에, 또는 심지어 특정 레코드에 적용되게 할 수도 있고 세 가지 모두를 택할 수도 있습니다. |
| </p> |
| <p> |
| 제공자에 대한 권한은 매니페스트 파일에 있는 하나 이상의 |
| <code><a href="{@docRoot}guide/topics/manifest/permission-element.html"> |
| <permission></a></code> 요소로 정의합니다. |
| 제공자에 고유한 권한을 설정하려면 |
| <code><a href="{@docRoot}guide/topics/manifest/permission-element.html#nm"> |
| android:name</a></code> 속성에 Java 스타일 범위를 사용합니다. 예를 들어 읽기 권한의 이름을 |
| <code>com.example.app.provider.permission.READ_PROVIDER</code>로 지정합니다. |
| |
| </p> |
| <p> |
| 다음 목록은 제공자 권한의 범위를 설명한 것입니다. |
| 제공자 전체에 적용되는 권한부터 시작하여 점차 세분화된 권한이 됩니다. |
| 보다 세부화된 권한이 범위가 큰 것보다 우선합니다. |
| </p> |
| <dl> |
| <dt> |
| 단일 읽기-쓰기 제공자 수준 권한 |
| </dt> |
| <dd> |
| 제공자 전체로의 읽기와 쓰기 액세스 양쪽 모두를 제어하는 하나의 권한으로, |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code> 요소의 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn"> |
| android:permission</a></code> 속성으로 지정됩니다. |
| </dd> |
| <dt> |
| 별도의 읽기 및 쓰기 제공자 수준 권한 |
| </dt> |
| <dd> |
| 제공자 전체에 대한 읽기 권한과 쓰기 권한입니다. 이들 권한은 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code> 요소의 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#rprmsn"> |
| android:readPermission</a></code>와 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#wprmsn"> |
| android:writePermission</a></code> 속성으로 |
| 지정합니다. 이들이 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn"> |
| android:permission</a></code>에서 요구하는 권한보다 우선합니다. |
| </dd> |
| <dt> |
| 경로 수준 권한 |
| </dt> |
| <dd> |
| 제공자의 콘텐츠 URI에 대한 읽기, 쓰기 또는 읽기/쓰기 권한입니다. 제어하고자 하는 각 URI를 직접 지정하되, |
| 이때 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code> 요소의 |
| <code><a href="{@docRoot}guide/topics/manifest/path-permission-element.html"> |
| <path-permission></a></code> 하위 요소를 사용합니다. 지정하는 콘텐츠 URI마다 |
| 읽기/쓰기 권한, 읽기 권한 또는 쓰기 권한을 하나씩 지정하거나 셋 모두를 지정할 수 있습니다. |
| 읽기 및 쓰기 권한이 읽기/쓰기 권한보다 우선합니다. |
| 또한, 경로 수준 권한이 제공자 수준 권한보다 우선합니다. |
| </dd> |
| <dt> |
| 임시 권한 |
| </dt> |
| <dd> |
| 애플리케이션에 임시 액세스를 허용하는 권한 수준입니다. |
| 해당 애플리케이션에 일반적으로 요구되는 권한이 없더라도 무관합니다. |
| 임시 액세스 기능은 매니페스트에서 요청해야 하는 |
| 권한과 애플리케이션 개수를 줄여줍니다. 임시 권한을 사용하는 경우, |
| 제공자에 대하여 "영구" 권한을 필요로하는 애플리케이션은 |
| 모든 데이터에 지속적으로 액세스하는 것들뿐입니다. |
| <p> |
| 이메일 제공자와 앱을 구현할 때 필요한 권한을 예로 들어 보겠습니다. |
| 외부 이미지 뷰어 애플리케이션으로 하여금 제공자에서 보낸 사진 첨부 파일을 |
| 표시하도록 허용하고자 한다고 가정합니다. 권한을 요구하지 않고 이미지 뷰어에 필수 액세스를 부여하려면, |
| 사진에 대한 콘텐츠 URI에 해단되는 임시 권한을 설정하십시오. |
| 사용자가 사진을 표시하기를 원할 때 앱이 사진의 콘텐츠 URI와 권한 플래그를 포함하는 인텐트를 |
| 이미지 뷰어에 보내도록 이메일 앱을 설계합니다. 그러면 해당 이미지 뷰어가 |
| 이메일 제공자에 사진 검색을 쿼리할 수 있으며, 이 뷰어에 제공자에 대한 정상적인 읽기 권한이 없더라도 무방합니다. |
| |
| </p> |
| <p> |
| 임시 권한을 사용하려면, |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code> 요소의 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> |
| android:grantUriPermissions</a></code> 속성을 설정하거나 |
| 하나 이상의 |
| <code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"> |
| <grant-uri-permission></a></code> 하위 요소를 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code> 요소에 추가하면 됩니다. 임시 권한을 사용하는 경우, |
| 제공자에서 콘텐츠 URI에 대한 지원을 제거할 때마다 {@link android.content.Context#revokeUriPermission(Uri, int) |
| Context.revokeUriPermission()}을 호출해야 합니다. |
| 그러면 콘텐츠 URI가 임시 권한과 연관됩니다. |
| </p> |
| <p> |
| 속성의 값에 따라 제공자에 액세스 가능한 정도가 결정됩니다. |
| 속성이 <code>true</code>로 설정되어 있는 경우라면 |
| 시스템이 제공자 전체에 임시 권한을 허용하며, 제공자 수준 또는 |
| 경로 수준 권한에서 요구하는 다른 모든 권한을 재정의합니다. |
| </p> |
| <p> |
| 이 플래그가 <code>false</code>로 설정되면, 반드시 |
| <code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"> |
| <grant-uri-permission></a></code> 하위 요소를 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code> 요소에 추가해야 합니다. 각 하위 요소는 임시 권한을 허용한 |
| 콘텐츠 URI(하나 또는 여러 개)를 나타냅니다. |
| </p> |
| <p> |
| 애플리케이션에 임시 액세스를 위임하려면, 인텐트에 |
| {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} 또는 |
| {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} 플래그, 또는 둘 모두가 들어 있어야 합니다. 이들은 |
| {@link android.content.Intent#setFlags(int) setFlags()} 메서드로 설정됩니다. |
| </p> |
| <p> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> |
| android:grantUriPermissions</a></code> 속성이 존재하지 않으면 |
| <code>false</code>인 것으로 가정합니다. |
| </p> |
| </dd> |
| </dl> |
| |
| |
| |
| <!-- The Provider Element --> |
| <h2 id="ProviderElement"><provider> 요소</h2> |
| <p> |
| {@link android.app.Activity}와 {@link android.app.Service} 구성 요소와 마찬가지로, |
| {@link android.content.ContentProvider}의 하위 클래스는 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code> 요소를 사용하여 매니페스트 파일에서 |
| 애플리케이션에 대해 정의되어야 합니다. Android 시스템이 |
| 해당 요소에서 다음과 같은 정보를 가져옵니다. |
| <dl> |
| <dt> |
| 권한 |
| (<a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">{@code |
| android:authorities}</a>) |
| </dt> |
| <dd> |
| 시스템 내에서 제공자 전체를 식별하는 상징적 이름입니다. 이 속성은 |
| |
| <a href="#ContentURI">콘텐츠 URI 설계</a> 섹션에서 자세히 설명되어 있습니다. |
| </dd> |
| <dt> |
| 제공자 클래스 이름 |
| (<code> |
| <a href="{@docRoot}guide/topics/manifest/provider-element.html#nm">android:name</a> |
| </code>) |
| </dt> |
| <dd> |
| {@link android.content.ContentProvider}를 구현하는 클래스입니다. 이 클래스는 |
| |
| <a href="#ContentProvider">ContentProvider 클래스 구현</a> 섹션에 자세히 설명되어 있습니다. |
| </dd> |
| <dt> |
| 권한 |
| </dt> |
| <dd> |
| 제공자의 데이터에 액세스하기 위해 다른 애플리케이션이 |
| 반드시 가지고 있어야 하는 권한을 나타내는 속성입니다. |
| <ul> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> |
| android:grantUriPermssions</a></code>: 임시 권한 플래그입니다. |
| </li> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn"> |
| android:permission</a></code>: 단일 제공자 전범위 읽기/쓰기 권한입니다. |
| </li> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#rprmsn"> |
| android:readPermission</a></code>: 제공자 전범위 읽기 권한입니다. |
| </li> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#wprmsn"> |
| android:writePermission</a></code>: 제공자 전범위 쓰기 권한입니다. |
| </li> |
| </ul> |
| <p> |
| 각종 권한과 그에 상응하는 속성은 |
| |
| <a href="#Permissions">콘텐츠 제공자 권한 구현</a> 섹션에 자세히 설명되어 있습니다. |
| </p> |
| </dd> |
| <dt> |
| 시작 및 제어 속성 |
| </dt> |
| <dd> |
| 이와 같은 속성은 Android 시스템이 제공자를 시작하는 방법과 시점, |
| 제공자의 프로세스 특징과 기타 런타임 설정 등을 결정합니다. |
| <ul> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#enabled"> |
| android:enabled</a></code>: 시스템이 제공자를 시작할 수 있게 해주는 플래그입니다. |
| </li> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#exported"> |
| android:exported</a></code>: 다른 애플리케이션이 이 제공자를 사용할 수 있게 해주는 플래그입니다. |
| </li> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#init"> |
| android:initOrder</a></code>: 같은 프로세스 내의 다른 제공자와 비교하여 |
| 이 제공자가 시작되어야 하는 순서입니다. |
| </li> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#multi"> |
| android:multiProcess</a></code>: 클라이언트를 호출하는 것과 |
| 같은 프로세스에서 시스템이 제공자를 시작할 수 있게 해주는 플래그입니다. |
| </li> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#proc"> |
| android:process</a></code>: 제공자가 실행해야 하는 프로세스의 |
| 이름입니다. |
| </li> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#sync"> |
| android:syncable</a></code>: 제공자의 데이터가 |
| 서버에 있는 데이터와 동기화될 예정임을 나타내는 플래그입니다. |
| </li> |
| </ul> |
| <p> |
| 이 속성은 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code> |
| 요소에 대한 개발자 가이드 주제에 상세하게 기록되어 있습니다. |
| </p> |
| </dd> |
| <dt> |
| 정보 속성 |
| </dt> |
| <dd> |
| 제공자에 대한 선택 항목 아이콘 및 레이블입니다. |
| <ul> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#icon"> |
| android:icon</a></code>: 제공자의 아이콘이 들어 있는 드로어블 리소스입니다. |
| 이 아이콘은 |
| <em>설정</em> > <em>앱</em> > <em>모두</em>에 있는 앱 목록에서 제공자의 레이블 옆에 표시됩니다. |
| </li> |
| <li> |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#label"> |
| android:label</a></code>: 제공자 또는 그 데이터, 또는 둘 모두를 설명하는 정보 레이블입니다. |
| 이 레이블은 |
| <em>설정</em> > <em>앱</em> > <em>모두</em>에 있는 앱 목록에 표시됩니다. |
| </li> |
| </ul> |
| <p> |
| 이 속성은 |
| <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> |
| <provider></a></code>요소에 대한 개발자 가이드 주제에 상세하게 기록되어 있습니다. |
| </p> |
| </dd> |
| </dl> |
| |
| <!-- Intent Access --> |
| <h2 id="Intents">인텐트 및 데이터 액세스</h2> |
| <p> |
| 애플리케이션이 콘텐츠 제공자에 간접적으로 액세스하려면 {@link android.content.Intent}를 사용하면 됩니다. |
| 이 애플리케이션은 {@link android.content.ContentResolver} 또는 |
| {@link android.content.ContentProvider}의 메서드 중 어느 하나도 호출하지 않습니다. |
| 대신, 액티비티를 시작하는 인텐트를 전송합니다. 이 인텐트는 제공자가 소유한 애플리케이션의 일부인 경우가 많습니다. |
| 대상 액티비티가 데이터를 자체 UI에서 검색하고 표시하는 역할을 맡습니다. |
| 인텐트의 동작에 따라 대상 액티비티가 사용자에게 프롬프트를 표시하여 제공자의 데이터를 수정하도록 할 수도 있습니다. |
| 인텐트에는 대상 액티비티가 UI에 표시하는 "추가" 데이터가 들어 있을 수도 있습니다. |
| 그러면 사용자에게 이 데이터를 변경할 수 있는 옵션이 주어지고, 그런 다음 이를 사용하여 |
| 제공자 내의 데이터를 수정할 수 있습니다. |
| </p> |
| <p> |
| |
| </p> |
| <p> |
| 데이터 무결성을 보장하는 데 유용한 것을 원하면 인텐트 액세스를 사용하는 것이 좋습니다. |
| 엄격하게 정의된 비즈니스 논리에 따라 데이터가 삽입, 업데이트되고 삭제되는 것이 제공자를 크게 좌우할 수도 있습니다. |
| 이런 경우에 해당되면, 다른 애플리케이션에 데이터를 직접 수정하도록 허용하면 데이터가 잘못되는 |
| 결과를 초래할 수 있습니다. 개발자들에게 인텐트 액세스 사용을 허용하려면, 그 내용을 철저히 기록해두어야 합니다. |
| 개발자들에게 자기 애플리케이션의 UI를 사용한 인텐트 액세스가 |
| 코드로 데이터를 수정하려 시도하는 것보다 나은 이유를 설명해주십시오. |
| </p> |
| <p> |
| 제공자의 데이터를 수정하고자 하는 수신되는 인텐트 처리도 다른 인텐트 처리와 다를 바가 없습니다. |
| 인텐트 사용에 대한 자세한 내용은 |
| <a href="{@docRoot}guide/components/intents-filters.html">인텐트 및 인텐트 필터</a> 주제를 읽으면 확인할 수 있습니다. |
| </p> |