1page.title= 콘텐츠 제공자 생성 2@jd:body 3<div id="qv-wrapper"> 4<div id="qv"> 5 6 7<h2>이 문서의 내용</h2> 8<ol> 9 <li> 10 <a href="#DataStorage">데이터 저장소 설계</a> 11 </li> 12 <li> 13 <a href="#ContentURI">콘텐츠 URI 설계</a> 14 </li> 15 <li> 16 <a href="#ContentProvider">ContentProvider 클래스 구현</a> 17 <ol> 18 <li> 19 <a href="#RequiredAccess">필수 메서드</a> 20 </li> 21 <li> 22 <a href="#Query">query() 메서드 구현</a> 23 </li> 24 <li> 25 <a href="#Insert">insert() 메서드 구현</a> 26 </li> 27 <li> 28 <a href="#Delete">delete() 메서드 구현</a> 29 </li> 30 <li> 31 <a href="#Update">update() 메서드 구현</a> 32 </li> 33 <li> 34 <a href="#OnCreate">onCreate() 메서드 구현</a> 35 </li> 36 </ol> 37 </li> 38 <li> 39 <a href="#MIMETypes">콘텐츠 제공자 MIME 유형 구현</a> 40 <ol> 41 <li> 42 <a href="#TableMIMETypes">테이블의 MIME 유형</a> 43 </li> 44 <li> 45 <a href="#FileMIMETypes">파일의 MIME 유형</a> 46 </li> 47 </ol> 48 </li> 49 <li> 50 <a href="#ContractClass">계약 클래스 구현</a> 51 </li> 52 <li> 53 <a href="#Permissions">콘텐츠 제공자 권한 구현</a> 54 </li> 55 <li> 56 <a href="#ProviderElement"><provider> 요소</a> 57 </li> 58 <li> 59 <a href="#Intents">인텐트 및 데이터 액세스</a> 60 </li> 61</ol> 62<h2>Key 클래스</h2> 63 <ol> 64 <li> 65 {@link android.content.ContentProvider} 66 </li> 67 <li> 68 {@link android.database.Cursor} 69 </li> 70 <li> 71 {@link android.net.Uri} 72 </li> 73 </ol> 74<h2>관련 샘플</h2> 75 <ol> 76 <li> 77 <a href="{@docRoot}resources/samples/NotePad/index.html"> 78 메모장 샘플 애플리케이션 79 </a> 80 </li> 81 </ol> 82<h2>참고 항목</h2> 83 <ol> 84 <li> 85 <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> 86 콘텐츠 제공자 기본 정보</a> 87 </li> 88 <li> 89 <a href="{@docRoot}guide/topics/providers/calendar-provider.html"> 90 캘린더 제공자</a> 91 </li> 92 </ol> 93</div> 94</div> 95 96 97<p> 98 콘텐츠 제공자는 데이터의 중앙 리포지토리로의 액세스를 관리합니다. Android 애플리케이션에서는 99제공자를 하나 이상의 클래스로, 매니페스트 파일에 있는 요소와 함께 구현합니다. 100 클래스 중 하나가 하위 클래스 101{@link android.content.ContentProvider}를 구현하며, 102이것이 제공자와 다른 애플리케이션 사이의 인터페이스입니다. 콘텐츠 제공자는 다른 애플리케이션에 데이터를 사용할 수 있게 해주도록 만들어져 있지만, 103물론 애플리케이션 내에 사용자로 하여금 제공자가 관리하는 데이터를 쿼리하고 수정할 수 있게 허용하는 104액티비티가 있을 수도 있습니다. 105</p> 106<p> 107 이 주제의 나머지 부분은 콘텐츠 제공자를 구축하기 위한 기본 단계 목록과 108사용할 API 목록으로 이루어져 있습니다. 109</p> 110 111 112<!-- Before You Start Building --> 113<h2 id="BeforeYouStart">구축을 시작하기 전에</h2> 114<p> 115 제공자 구축을 시작하기 전에 우선 다음 단계를 수행하십시오. 116</p> 117<ol> 118 <li> 119 <strong>콘텐츠 제공자가 필요한지 결정합니다</strong>. 다음 기능 중 하나 이상을 제공하려면 120콘텐츠 제공자를 구축해야 합니다. 121 <ul> 122 <li>다른 애플리케이션에 복잡한 데이터나 파일을 제공하고자 하는 경우</li> 123 <li>사용자로 하여금 개발자의 앱에서 다른 앱으로 복잡한 데이터를 복사하도록 허용하고자 하는 경우</li> 124 <li>검색 프레임워크를 사용한 사용자 지정 검색 제안을 제공하고자 하는 경우</li> 125 </ul> 126 <p> 127 용도가 본인의 애플리케이션 안에서로 완전히 한정되어 있는 경우에는 128제공자가 SQLite 데이터베이스를 사용하도록 하지 <em>않아도</em> 됩니다. 129 </p> 130 </li> 131 <li> 132 아직 읽지 않았다면, 지금 바로 133<a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> 134콘텐츠 제공자 기본 정보</a>를 읽고 제공자에 대해 자세히 알아보십시오. 135 </li> 136</ol> 137<p> 138 그런 다음, 다음 단계를 따라 제공자를 구축합니다. 139</p> 140<ol> 141 <li> 142 데이터를 위한 원시 저장소를 설계합니다. 콘텐츠 제공자는 두 가지 방식으로 데이터를 제공합니다. 143 <dl> 144 <dt> 145 파일 데이터 146 </dt> 147 <dd> 148 일반적으로 사진, 오디오 또는 동영상과 같은 149파일에 들어가는 데이터입니다. 이런 파일을 애플리케이션의 비공개 150공간에 저장합니다. 제공자는 다른 애플리케이션으로부터 온 파일 요청에 응답하여 151해당 파일로의 핸들을 제공할 수 있습니다. 152 </dd> 153 <dt> 154 "구조적" 데이터 155 </dt> 156 <dd> 157 일반적으로 데이터베이스, 배열 또는 유사 구조에 들어가는 데이터입니다. 158 이 데이터를 행과 열로 이루어진 테이블과 호환되는 형식으로 저장합니다. 159행은 사람이나 인벤토리의 항목과 같은 엔티티를 나타냅니다. 160열은 해당 엔티티에 대한 몇 가지 데이터, 예를 들어 사람 이름이나 항목 가격 등을 나타냅니다. 161이 유형의 데이터를 저장하는 보편적인 방법은 SQLite 데이터베이스 안에 저장하는 것이지만, 162모든 유형의 영구적인 저장소를 사용해도 됩니다. Android 시스템에서 사용할 수 있는 저장소 유형에 대해 자세히 알아보려면, 163<a href="#DataStorage"> 164데이터 저장소 설계</a> 섹션을 참조하십시오. 165 </dd> 166 </dl> 167 </li> 168 <li> 169 {@link android.content.ContentProvider} 클래스와 170필수 메서드의 구체적인 구현을 정의합니다. 이 클래스는 데이터와 나머지 Android 시스템 사이의 171인터페이스입니다. 이 클래스에 관한 자세한 정보는 172<a href="#ContentProvider">ContentProvider 클래스 구현</a> 섹션을 참조하십시오. 173 </li> 174 <li> 175 제공자의 권한 문자열, 그 콘텐츠 URI 및 열 이름을 정의합니다. 176제공자 애플리케이션이 인텐트를 처리하게 하려면, 인텐트 작업과 추가 데이터 및 177플래그도 정의합니다. 데이터에 액세스하기를 원하는 애플리케이션에 요구할 권한도 178정의합니다. 이 모든 값은 별도의 계약 클래스에서 상수로 정의하는 것을 고려해보는 179것이 좋습니다. 이 클래스를 나중에 다른 개발자에게 노출할 수 있습니다. 180콘텐츠 URI에 관한 자세한 정보는 181<a href="#ContentURI">콘텐츠 URI 설계</a> 섹션을 참조하십시오. 182 인텐트에 관한 자세한 정보는 183<a href="#Intents">인텐트 및 데이터 액세스</a> 섹션을 참조하십시오. 184 </li> 185 <li> 186 샘플 데이터, 또는 제공자와 클라우드 기반 데이터 사이에서 187데이터를 동기화할 수 있는 {@link android.content.AbstractThreadedSyncAdapter} 구현 등과 같이 188다른 선택적 조각을 추가합니다. 189 </li> 190</ol> 191 192 193<!-- Designing Data Storage --> 194<h2 id="DataStorage">데이터 저장소 설계</h2> 195<p> 196 콘텐츠 제공자는 구조화된 형식으로 저장된 데이터로의 인터페이스입니다. 197인터페이스를 생성하기 전에 우선 데이터 저장 방식부터 결정해야 합니다. 198데이터는 원하는 형식 아무 것으로나 저장할 수 있으며 그런 다음에 필요에 따라 해당 데이터를 읽고 쓸 인터페이스를 설계합니다. 199</p> 200<p> 201 다음은 Android에서 사용할 수 있는 몇 가지 데이터 저장소 기술입니다. 202</p> 203<ul> 204 <li> 205 Android 시스템에는 Android 자체 제공자가 테이블 지향적 데이터를 206저장하는 데 사용하는 SQLite 데이터베이스 API가 포함됩니다. 207{@link android.database.sqlite.SQLiteOpenHelper} 클래스는 데이터베이스를 생성할 수 있게 돕고, 208{@link android.database.sqlite.SQLiteDatabase} 클래스는 데이터베이스 액세스를 위한 209기본 클래스입니다. 210 <p> 211 리포지토리를 구현하기 위해 데이터베이스를 사용하지 않아도 된다는 점을 기억하십시오. 212제공자는 외부에 테이블 집합으로 나타나 관계적 데이터베이스와 비슷해 보이지만, 213이것은 제공자의 내부 구현에 필요한 것은 아닙니다. 214 </p> 215 </li> 216 <li> 217 파일 데이터를 저장하는 데 있어 Android에는 다양한 파일 지향적 API가 있습니다. 218 파일 저장소에 관해 자세히 알아보려면 219<a href="{@docRoot}guide/topics/data/data-storage.html">데이터 저장소</a> 주제를 읽어 보십시오. 220음악이나 동영상 등 미디어 관련 데이터를 제공하는 제공자를 설계하는 경우, 221제공자가 테이블 데이터와 파일을 조합 할 수 있습니다. 222 </li> 223 <li> 224 네트워크 기반 데이터를 다루는 경우, {@link java.net} 및 225{@link android.net} 내의 클래스를 사용하십시오. 네트워크 기반 데이터를 226데이터베이스와 같은 로컬 데이터 스토어와 동기화한 다음, 해당 데이터를 테이블이나 파일로 제공할 수도 있습니다. 227 <a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html"> 228샘플 동기화 어댑터</a> 샘플 애플리케이션이 이런 유형의 동기화를 보여줍니다. 229 </li> 230</ul> 231<h3 id="DataDesign"> 232 데이터 설계 시 고려할 사항 233</h3> 234<p> 235 다음은 제공자의 데이터 구조를 설계할 때 유용한 몇 가지 팁입니다. 236</p> 237<ul> 238 <li> 239 테이블 데이터는 언제나 제공자가 유지관리하는 "기본 키" 열을 240각 행의 고유한 숫자 값으로 보유하고 있어야 합니다. 이 값을 사용하여 해당 행을 다른 테이블의 241관련 행에 연결시킬 수 있습니다(이를 "외래 키"로 사용). 이 열에는 어느 이름이든 사용할 수 있지만 242{@link android.provider.BaseColumns#_ID BaseColumns._ID}를 사용하는 것이 가장 좋습니다. 243왜냐하면 제공자 쿼리 결과를 244{@link android.widget.ListView}에 연결하려면 검색된 열 중 하나가 245<code>_ID</code>라는 이름을 사용해야 하기 때문입니다. 246 </li> 247 <li> 248 비트맵 이미지나 파일 지향적 데이터의 매우 큰 조각을 제공하려면 249테이블 안에 직접 저장하기보다는 파일에 데이터를 저장한 뒤 250간접적으로 제공합니다. 이렇게 하는 경우, 제공자의 사용자들에게 데이터에 액세스하려면 251{@link android.content.ContentResolver} 파일 메서드를 사용해야 한다고 알려야 합니다. 252 </li> 253 <li> 254 BLOB(Binary Large OBject) 데이터 유형을 사용하여 크기가 다르거나 255구조가 다양한 데이터를 저장합니다. 예를 들어, BLOB 열을 사용하여 256<a href="http://code.google.com/p/protobuf">프로토콜 버퍼</a> 또는 257<a href="http://www.json.org">JSON 구조</a>를 저장할 수 있습니다. 258 <p> 259 BLOB를 사용하여 <em>스키마에 종속되지 않은</em> 테이블을 구현할 수도 있습니다. 260이 유형의 테이블에서는, 기본 키 열, MIME 유형 열 및 하나 이상의 일반적인 열을 BLOB로 정의합니다. 261 262BLOB 열에 있는 데이터의 의미는 MIME 유형 열에 있는 값으로 나타냅니다. 263이렇게 하면 같은 테이블에 여러 가지 행 유형을 저장할 수 있습니다. 연락처 제공자의 "데이터" 테이블 264{@link android.provider.ContactsContract.Data}가 265스키마에 종속되지 않은 테이블의 한 가지 예입니다. 266 </p> 267 </li> 268</ul> 269<!-- Designing Content URIs --> 270<h2 id="ContentURI">콘텐츠 URI 설계</h2> 271<p> 272 <strong>콘텐츠 URI</strong>는 제공자에서 데이터를 식별하는 URI입니다. 273콘텐츠 URI에는 전체 제공자의 상징적인 이름(제공자의 <strong>권한</strong>)과 274테이블 또는 파일을 가리키는 이름(<strong>경로</strong>)이 포함됩니다. 275선택 항목 ID 부분은 테이블 내의 개별적인 행을 가리킵니다. 276{@link android.content.ContentProvider}의 모든 데이터 액세스 메서드는 277콘텐츠 URI를 인수로 가집니다. 이를 통해 액세스할 테이블, 행 또는 파일을 결정할 수 있습니다. 278</p> 279<p> 280 콘텐츠 URI의 기본 정보는 281<a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> 282콘텐츠 제공자 기본 정보</a>에 설명되어 있습니다. 283</p> 284<h3>권한 설계</h3> 285<p> 286 제공자에는 보통 하나의 권한이 있으며, 이것이 Android 내부 이름 역할을 합니다. 287다른 제공자와의 충돌을 피하려면, 제공자 권한의 기반으로 인터넷 도메인 소유권(역방향)을 288사용해야 합니다. 이 권장 사항은 Android 패키지 이름에도 적용되므로, 289제공자 권한을 제공자가 들어 있는 패키지의 이름 확장자로 정의해도 됩니다. 290 예를 들어, Android 패키지 이름이 291<code>com.example.<appname></code>라면, 제공자에게 292<code>com.example.<appname>.provider</code> 권한을 부여해야 합니다. 293</p> 294<h3>경로 구조 설계</h3> 295<p> 296 개발자는 보통 권한으로부터 콘텐츠 URI를 생성할 때 개별적인 테이블을 가리키는 297경로를 추가하는 방식을 사용합니다. 예를 들어, <em>table1</em>과 298<em>table2</em>라는 테이블이 있다면, 이전 예시의 권한을 조합하여 299콘텐츠 URI<code>com.example.<appname>.provider/table1</code>와 300<code>com.example.<appname>.provider/table2</code>를 도출합니다. 301 302경로는 하나의 세그먼트에 국한되지 않으며, 경로의 각 수준에 대한 테이블이 아니어도 됩니다. 303</p> 304<h3>콘텐츠 URI ID 처리</h3> 305<p> 306 규칙에 의하면, 제공자는 URI 맨 끝에서 행에 대한 ID 값이 있는 콘텐츠 URI를 허용하여 307테이블 내 하나의 행으로의 액세스를 제공합니다. 또한 규칙에 의해 제공자는 308이 ID 값을 테이블의 <code>_ID</code> 열에 일치시켜야 하며, 309일치한 행에 대하여 요청된 액세스 허가를 수행해야 합니다. 310</p> 311<p> 312 이 규칙은 제공자에 액세스하는 앱을 위한 공통 설계 패턴을 세우는 데 유용합니다. 313앱이 제공자에 대한 쿼리를 수행하고 그 결과로 나온 {@link android.database.Cursor}를 314{@link android.widget.ListView}에 {@link android.widget.CursorAdapter}를 사용하여 표시합니다. 315 {@link android.widget.CursorAdapter}의 정의에 따르면 316{@link android.database.Cursor} 안의 열 중 하나는 <code>_ID</code>여야 합니다. 317</p> 318<p> 319 그러면 사용자가 데이터를 살펴보거나 수정하기 위하여 320UI에서 표시된 여러 행 중 하나를 선택합니다. 앱은 {@link android.widget.ListView}를 지원하는 {@link android.database.Cursor}에서 해당하는 열을 가져오고, 321해당 열에 대한 <code>_ID</code> 값을 가져와서 322콘텐츠 URI에 추가하고, 제공자에 액세스 요청을 전송합니다. 그런 다음 제공자는 323사용자가 선택한 바로 그 행에 대해 쿼리 또는 수정 작업을 수행할 수 있습니다. 324</p> 325<h3>콘텐츠 URI 패턴</h3> 326<p> 327 수신되는 콘텐츠 URI에 대해 어떤 조치를 취할지 선택하는 데 도움이 되도록 하기 위해 제공자 API에 328편의 클래스 {@link android.content.UriMatcher}가 329포함되어 있습니다. 이는 콘텐츠 URI "패턴"을 정수값으로 매핑합니다. 이 정수값은 특정 패턴에 일치하는 330콘텐츠 URI 또는 여러 URI에 대해 원하는 작업을 선택하는 데 <code>switch</code> 문에서 사용할 수 있습니다. 331</p> 332<p> 333 콘텐츠 URI 패턴은 와일드카드 문자를 사용하는 콘텐츠 URI와 일치합니다. 334</p> 335 <ul> 336 <li> 337 <strong><code>*</code>:</strong> 모든 길이의 모든 유효한 문자로 구성된 문자열과 일치합니다. 338 </li> 339 <li> 340 <strong><code>#</code>:</strong> 모든 길이의 숫자 문자로 구성된 문자열과 일치합니다. 341 </li> 342 </ul> 343<p> 344 콘텐츠 URI 처리의 설계와 코딩에 대한 예시로서 임의의 제공자를 들어 보겠습니다. 345이 제공자에는 권한 <code>com.example.app.provider</code>가 있고 346이 권한이 테이블을 가리키는 다음 콘텐츠 URI를 인식합니다. 347</p> 348<ul> 349 <li> 350 <code>content://com.example.app.provider/table1</code>: <code>table1</code>이라는 테이블입니다. 351 </li> 352 <li> 353 <code>content://com.example.app.provider/table2/dataset1</code>: 354<code>dataset1</code>이라는 테이블입니다. 355 </li> 356 <li> 357 <code>content://com.example.app.provider/table2/dataset2</code>: 358<code>dataset2</code>라는 테이블입니다. 359 </li> 360 <li> 361 <code>content://com.example.app.provider/table3</code>: <code>table3</code>이라는 테이블입니다. 362 </li> 363</ul> 364<p> 365 제공자는 추가된 행 ID가 있으면 이런 콘텐츠 URI도 인식합니다. 366예를 들어, <code>table3</code>에서 <code>1</code>이 식별한 행에 대한 367<code>content://com.example.app.provider/table3/1</code>이 이에 해당됩니다. 368</p> 369<p> 370 가능한 콘텐츠 URI 패턴은 다음과 같습니다. 371</p> 372<dl> 373 <dt> 374 <code>content://com.example.app.provider/*</code> 375 </dt> 376 <dd> 377 제공자에 있는 모든 콘텐츠 URI와 일치합니다. 378 </dd> 379 <dt> 380 <code>content://com.example.app.provider/table2/*</code>: 381 </dt> 382 <dd> 383 테이블 <code>dataset1</code>과 384<code>dataset2</code>의 콘텐츠 URI와 일치하지만 <code>table1</code>이나 385<code>table3</code>에 대한 콘텐츠 URI와 일치하지 않습니다. 386 </dd> 387 <dt> 388 <code>content://com.example.app.provider/table3/#</code>: 389<code>table3</code>의 단일 행에 대한 콘텐츠 URI와 일치합니다. 예를 들어, 390<code>6</code>이 식별한 행에 대한 <code>content://com.example.app.provider/table3/6</code>이 이에 해당됩니다. 391 392 </dt> 393</dl> 394<p> 395 다음 코드 조각은 {@link android.content.UriMatcher} 작업에서 메서드의 작용 원리를 나타낸 것입니다. 396 이 코드는 테이블에 대한 콘텐츠 URI 패턴 <code>content://<authority>/<path></code>와 397단일 행에 대한 콘텐츠 URI 패턴 <code>content://<authority>/<path>/<id></code>를 사용하여 398단일 행에 대한 URI와 전체 테이블에 대한 URI를 서로 다르게 처리합니다. 399 400</p> 401<p> 402 {@link android.content.UriMatcher#addURI(String, String, int) addURI()} 메서드는 403권한과 경로를 정수값으로 매핑합니다. 메서드 {@link android.content.UriMatcher#match(Uri) 404match()}는 URI에 대한 정수값을 반환합니다. <code>switch</code> 문이 405전체 테이블을 쿼리할 것인지, 하나의 레코드를 쿼리할 것인지 선택합니다. 406</p> 407<pre class="prettyprint"> 408public class ExampleProvider extends ContentProvider { 409... 410 // Creates a UriMatcher object. 411 private static final UriMatcher sUriMatcher; 412... 413 /* 414 * The calls to addURI() go here, for all of the content URI patterns that the provider 415 * should recognize. For this snippet, only the calls for table 3 are shown. 416 */ 417... 418 /* 419 * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used 420 * in the path 421 */ 422 sUriMatcher.addURI("com.example.app.provider", "table3", 1); 423 424 /* 425 * Sets the code for a single row to 2. In this case, the "#" wildcard is 426 * used. "content://com.example.app.provider/table3/3" matches, but 427 * "content://com.example.app.provider/table3 doesn't. 428 */ 429 sUriMatcher.addURI("com.example.app.provider", "table3/#", 2); 430... 431 // Implements ContentProvider.query() 432 public Cursor query( 433 Uri uri, 434 String[] projection, 435 String selection, 436 String[] selectionArgs, 437 String sortOrder) { 438... 439 /* 440 * Choose the table to query and a sort order based on the code returned for the incoming 441 * URI. Here, too, only the statements for table 3 are shown. 442 */ 443 switch (sUriMatcher.match(uri)) { 444 445 446 // If the incoming URI was for all of table3 447 case 1: 448 449 if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; 450 break; 451 452 // If the incoming URI was for a single row 453 case 2: 454 455 /* 456 * Because this URI was for a single row, the _ID value part is 457 * present. Get the last path segment from the URI; this is the _ID value. 458 * Then, append the value to the WHERE clause for the query 459 */ 460 selection = selection + "_ID = " uri.getLastPathSegment(); 461 break; 462 463 default: 464 ... 465 // If the URI is not recognized, you should do some error handling here. 466 } 467 // call the code to actually do the query 468 } 469</pre> 470<p> 471 또 다른 클래스, {@link android.content.ContentUris}가 472콘텐츠 URI의 <code>id</code> 부분을 다루기 위한 편의 메서드를 제공합니다. 클래스 {@link android.net.Uri}와 473{@link android.net.Uri.Builder}에는 474기존 {@link android.net.Uri} 개체를 구문 분석하고 새로운 개체를 구축하기 위한 편의 메서드가 포함되어 있습니다. 475</p> 476 477<!-- Implementing the ContentProvider class --> 478<h2 id="ContentProvider">ContentProvider 클래스 구현</h2> 479<p> 480 {@link android.content.ContentProvider} 인스턴스는 481다른 애플리케이션으로부터의 요청을 처리하여 구조화된 데이터 세트로의 액세스를 관리합니다. 482모든 형태의 액세서가 궁극적으로 {@link android.content.ContentResolver}를 호출하며, 483그러면 이것이 액세스 권한을 얻기 위해 구체적인 {@link android.content.ContentProvider} 메서드를 호출합니다. 484</p> 485<h3 id="RequiredAccess">필수 메서드</h3> 486<p> 487 추상 클래스 {@link android.content.ContentProvider}는 488개발자가 나름의 구체적인 하위 클래스의 일부분으로 구현해야만 하는 여섯 가지 추상 메서드를 정의합니다. 이와 같은 메서드는 모두 489({@link android.content.ContentProvider#onCreate() onCreate()}는 예외) 490콘텐츠 제공자에 액세스하려 시도 중인 클라이언트 애플리케이션이 호출합니다. 491</p> 492<dl> 493 <dt> 494 {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) 495query()} 496 </dt> 497 <dd> 498 제공자에서 데이터를 검색합니다. 인수를 사용하여 쿼리할 테이블과 반환할 열/행, 결과의 정렬 순서를 선택합니다. 499 500 데이터를 {@link android.database.Cursor} 개체로 반환합니다. 501 </dd> 502 <dt> 503 {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 504 </dt> 505 <dd> 506 제공자에 새로운 행을 삽입합니다. 인수를 사용하여 대상 테이블을 선택하고 507사용할 열 값을 가져옵니다. 508새로 삽입된 행에 대한 콘텐츠 URI를 반환합니다. 509 </dd> 510 <dt> 511 {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) 512update()} 513 </dt> 514 <dd> 515 제공자 내의 기존 행을 업데이트합니다. 인수를 사용하여 516업데이트할 테이블과 행을 선택하고 업데이트한 열 값을 가져옵니다. 업데이트한 행 개수를 반환합니다. 517 </dd> 518 <dt> 519 {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} 520 </dt> 521 <dd> 522 제공자에서 행을 삭제합니다. 인수를 사용하여 삭제할 테이블과 행을 선택합니다. 523 삭제한 행 개수를 반환합니다. 524 </dd> 525 <dt> 526 {@link android.content.ContentProvider#getType(Uri) getType()} 527 </dt> 528 <dd> 529 콘텐츠 URI에 상응하는 MIME 유형을 반환합니다. 이 메서드는 530<a href="#MIMETypes">콘텐츠 제공자 MIME 유형</a> 섹션에 더 자세하게 설명되어 있습니다. 531 </dd> 532 <dt> 533 {@link android.content.ContentProvider#onCreate() onCreate()} 534 </dt> 535 <dd> 536 제공자를 초기화합니다. Android 시스템은 제공자를 생성한 직후 537이 메서드를 호출합니다. 538{@link android.content.ContentResolver} 개체가 제공자에 액세스하려고 시도할 때까지는 제공자가 생성된 것이 아니라는 점을 유의하십시오. 539 </dd> 540</dl> 541<p> 542 이와 같은 메서드에는 동일하게 이름 붙여진 543{@link android.content.ContentResolver} 메서드와 같은 서명이 있다는 것을 눈여겨 보십시오. 544</p> 545<p> 546 이러한 메서드의 구현에는 다음과 같은 내용을 감안해야 합니다. 547</p> 548<ul> 549 <li> 550 이런 메서드는 모두({@link android.content.ContentProvider#onCreate() onCreate()}는 예외) 551 한꺼번에 여러 스레드가 호출할 수 있으므로, 스레드로부터 안전해야 합니다. 552다중 스레드에 대한 자세한 내용은 553<a href="{@docRoot}guide/components/processes-and-threads.html"> 554프로세스 및 스레드</a> 주제를 참조하십시오. 555 </li> 556 <li> 557 {@link android.content.ContentProvider#onCreate() 558onCreate()}에서는 긴 작업을 수행하는 것을 삼가는 것이 좋습니다. 실제로 필요할 때까지 초기화 작업을 미뤄두십시오. 559 이 내용은 <a href="#OnCreate">onCreate() 메서드 구현</a> 560섹션에서 더욱 자세히 논의합니다. 561 </li> 562 <li> 563 이와 같은 메서드는 반드시 구현해야 하는 것이지만, 564예상되는 데이터 유형을 반환하는 것 외에 달리 코드가 해야 할 일은 없습니다. 565예를 들어 몇몇 테이블에 다른 애플리케이션이 데이터를 삽입하지 못하도록 방지하려고 합니다. 이렇게 하려면, 566{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}로의 567호출을 무시하고 0을 반환하면 됩니다. 568 </li> 569</ul> 570<h3 id="Query">query() 메서드 구현</h3> 571<p> 572 573{@link android.content.ContentProvider#query(Uri, String[], String, String[], String) 574ContentProvider.query()} 메서드는 {@link android.database.Cursor} 개체를 반환해야 하고, 그렇지 못할 경우 575{@link java.lang.Exception}을 발생시킵니다. SQLite 데이터베이스를 데이터 저장소로 사용하는 경우, 576{@link android.database.sqlite.SQLiteDatabase} 클래스의 <code>query()</code> 메서드 중 하나로 반환되는 {@link android.database.Cursor}를 577반환하기만 하면 됩니다. 578 쿼리가 어느 행에도 일치하지 않으면, {@link android.database.Cursor#getCount()} 메서드가 0을 반환하는 579{@link android.database.Cursor} 인스턴스를 반환해야 합니다. 580 <code>null</code>을 반환하는 것은 쿼리 과정 중에 내부 오류가 발생했을 때뿐입니다. 581</p> 582<p> 583 SQLite 데이터베이스를 데이터 저장소로 사용하지 않는 경우, {@link android.database.Cursor}의 584 구체적인 하위 클래스 중 하나를 사용하십시오. 예를 들어, {@link android.database.MatrixCursor} 클래스는 585각 행이 {@link java.lang.Object} 배열인 커서를 구현합니다. 이 클래스에서는 586{@link android.database.MatrixCursor#addRow(Object[]) addRow()}를 사용하여 새 행을 추가합니다. 587</p> 588<p> 589 Android 시스템이 프로세스 경계를 가로질러 {@link java.lang.Exception}을 590 통신으로 전달할 수 있어야 한다는 점을 유의하십시오. Android가 이 작업을 할 수 있는 경우는 591쿼리 오류 처리에 유용할 수 있는 다음과 같은 예외에 해당될 때입니다. 592</p> 593<ul> 594 <li> 595 {@link java.lang.IllegalArgumentException} 596(제공자가 유효하지 않은 콘텐츠 URI를 수신할 경우 이 예외를 발생시킬 수 있습니다.) 597 </li> 598 <li> 599 {@link java.lang.NullPointerException} 600 </li> 601</ul> 602<h3 id="Insert">insert() 메서드 구현</h3> 603<p> 604 {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 메서드는 605{@link android.content.ContentValues} 인수의 값을 이용하여 606적절한 테이블에 새 행을 추가합니다. 열 이름이 {@link android.content.ContentValues} 인수에 없는 경우, 607제공자 코드 또는 데이터베이스 스키마 내에 그에 대한 기본값을 제공하는 것이 좋을 수도 있습니다. 608 609</p> 610<p> 611 이 메서드가 새 행에 대한 콘텐츠 URI를 반환하는 것이 정상입니다. 이것을 구성하려면 새 행의 612<code>_ID</code>(또는 다른 기본 키) 값을 테이블의 콘텐츠 URI에 추가하며, 이때 613{@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()}를 사용합니다. 614</p> 615<h3 id="Delete">delete() 메서드 구현</h3> 616<p> 617 {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} 메서드의 경우 618 데이터 저장소에서 물리적으로 행을 삭제하지 않아도 됩니다. 619제공자와 동기화 어댑터를 함께 사용하고 있는 경우, 620삭제된 행을 완전히 제거하기보다는 "삭제" 플래그로 표시하는 방법을 고려해볼 만합니다. 621동기화 어댑터가 삭제된 행을 확인한 다음, 이를 제공자에서 삭제하기 전에 우선 서버에서 제거합니다. 622</p> 623<h3 id="Update">Update() 메서드 구현</h3> 624<p> 625 {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) 626update()} 메서드는{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}가 사용하는 것과 같은 {@link android.content.ContentValues} 인수와 627 628{@link android.content.ContentProvider#delete(Uri, String, String[]) delete()}와 {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) 629ContentProvider.query()}가 사용하는 것과 같은 <code>selection</code> 및 <code>selectionArgs</code> 인수를 630취합니다. 631 이 때문에 이와 같은 메서드 사이에서 코드를 다시 사용할 수 있게 해주기도 합니다. 632</p> 633<h3 id="OnCreate">onCreate() 메서드 구현</h3> 634<p> 635 Android 시스템은 제공자를 시작할 때 {@link android.content.ContentProvider#onCreate() 636onCreate()}를 호출합니다. 이 메서드에서는 빠르게 실행되는 초기화만 수행해야 하며, 637데이터베이스 생성과 데이터 로딩은 제공자가 실제로 데이터에 대한 요청을 받을 때까지 미뤄두어야 합니다. 638 639{@link android.content.ContentProvider#onCreate() onCreate()}에서 긴 작업을 수행하면 640제공자의 시동 속도가 느려집니다. 이 때문에 제공자에서 다른 애플리케이션으로 전달되는 응답도 따라서 느려집니다. 641 642</p> 643<p> 644 예를 들어, SQLite 데이터베이스를 사용하는 경우 645{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}에서 646새로운 {@link android.database.sqlite.SQLiteOpenHelper} 개체를 생성하고, 647그런 다음 데이터베이스를 처음 열 때 SQL 테이블을 생성할 수 있습니다. 이를 용이하게 하기 위해 648{@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase 649getWritableDatabase()}를 처음 호출하면 이것이 자동으로 650{@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) 651SQLiteOpenHelper.onCreate()} 메서드를 호출합니다. 652</p> 653<p> 654 다음 두 개의 조각은 655{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}와 656{@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) 657SQLiteOpenHelper.onCreate()} 사이의 상호 작용을 나타낸 것입니다. 첫 번째 조각은 658{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}의 구현입니다. 659</p> 660<pre class="prettyprint"> 661public class ExampleProvider extends ContentProvider 662 663 /* 664 * Defines a handle to the database helper object. The MainDatabaseHelper class is defined 665 * in a following snippet. 666 */ 667 private MainDatabaseHelper mOpenHelper; 668 669 // Defines the database name 670 private static final String DBNAME = "mydb"; 671 672 // Holds the database object 673 private SQLiteDatabase db; 674 675 public boolean onCreate() { 676 677 /* 678 * Creates a new helper object. This method always returns quickly. 679 * Notice that the database itself isn't created or opened 680 * until SQLiteOpenHelper.getWritableDatabase is called 681 */ 682 mOpenHelper = new MainDatabaseHelper( 683 getContext(), // the application context 684 DBNAME, // the name of the database) 685 null, // uses the default SQLite cursor 686 1 // the version number 687 ); 688 689 return true; 690 } 691 692 ... 693 694 // Implements the provider's insert method 695 public Cursor insert(Uri uri, ContentValues values) { 696 // Insert code here to determine which table to open, handle error-checking, and so forth 697 698 ... 699 700 /* 701 * Gets a writeable database. This will trigger its creation if it doesn't already exist. 702 * 703 */ 704 db = mOpenHelper.getWritableDatabase(); 705 } 706} 707</pre> 708<p> 709 그 다음 조각은 710도우미 클래스를 포함한 {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) 711SQLiteOpenHelper.onCreate()}의 구현입니다. 712</p> 713<pre class="prettyprint"> 714... 715// A string that defines the SQL statement for creating a table 716private static final String SQL_CREATE_MAIN = "CREATE TABLE " + 717 "main " + // Table's name 718 "(" + // The columns in the table 719 " _ID INTEGER PRIMARY KEY, " + 720 " WORD TEXT" 721 " FREQUENCY INTEGER " + 722 " LOCALE TEXT )"; 723... 724/** 725 * Helper class that actually creates and manages the provider's underlying data repository. 726 */ 727protected static final class MainDatabaseHelper extends SQLiteOpenHelper { 728 729 /* 730 * Instantiates an open helper for the provider's SQLite data repository 731 * Do not do database creation and upgrade here. 732 */ 733 MainDatabaseHelper(Context context) { 734 super(context, DBNAME, null, 1); 735 } 736 737 /* 738 * Creates the data repository. This is called when the provider attempts to open the 739 * repository and SQLite reports that it doesn't exist. 740 */ 741 public void onCreate(SQLiteDatabase db) { 742 743 // Creates the main table 744 db.execSQL(SQL_CREATE_MAIN); 745 } 746} 747</pre> 748 749 750<!-- Implementing ContentProvider MIME Types --> 751<h2 id="MIMETypes">ContentProvider MIME 유형 구현</h2> 752<p> 753 {@link android.content.ContentProvider} 클래스에는 MIME 유형을 반환하는 두 가지 메서드가 있습니다. 754</p> 755<dl> 756 <dt> 757 {@link android.content.ContentProvider#getType(Uri) getType()} 758 </dt> 759 <dd> 760 모든 제공자에 대해 구현해야 하는 필수 메서드 중 하나입니다. 761 </dd> 762 <dt> 763 {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} 764 </dt> 765 <dd> 766 제공자가 파일을 제공하는 경우 구현해야 하는 메서드입니다. 767 </dd> 768</dl> 769<h3 id="TableMIMETypes">테이블의 MIME 유형</h3> 770<p> 771 {@link android.content.ContentProvider#getType(Uri) getType()} 메서드는 772콘텐츠 URI 인수가 반환하는 데이터 유형을 설명하는 MIME 형식의 {@link java.lang.String}을 반환합니다. 773 {@link android.net.Uri} 인수는 특정 URI라기보다 패턴일 수 있습니다. 774이 경우, 이 패턴과 일치하는 콘텐츠 URI와 연관된 유형의 데이터를 반환해야 합니다. 775 776</p> 777<p> 778 텍스트, HTML 또는 JPEG와 같은 보편적인 유형의 데이터라면 779{@link android.content.ContentProvider#getType(Uri) getType()}이 780해당 데이터에 대한 표준 MIME 유형을 반환하는 것이 정상입니다. 이러한 표준 유형의 전체 목록은 781<a href="http://www.iana.org/assignments/media-types/index.htm">IANA MIME 미디어 유형</a> 782웹사이트에서 확인할 수 있습니다. 783</p> 784<p> 785 테이블 데이터의 행 하나 또는 여러 행을 가리키는 콘텐츠 URI의 경우, 786{@link android.content.ContentProvider#getType(Uri) getType()}이 Android의 공급업체별 MIME 형식에서 787MIME 형식을 반환해야 합니다. 788</p> 789<ul> 790 <li> 791 유형 부분: <code>vnd</code> 792 </li> 793 <li> 794 하위 유형 부분: 795 <ul> 796 <li> 797 URI 패턴이 하나의 행에 대한 것일 경우: <code>android.cursor.<strong>item</strong>/</code> 798 </li> 799 <li> 800 URI 패턴이 하나 이상의 행에 대한 것일 경우: <code>android.cursor.<strong>dir</strong>/</code> 801 </li> 802 </ul> 803 </li> 804 <li> 805 제공자별 부분: <code>vnd.<name></code>.<code><type></code> 806 <p> 807 개발자가 <code><name></code>과 <code><type></code>을 제공합니다. 808 <code><name></code> 값은 전체적으로 고유해야 하고, 809<code><type></code> 값은 상응하는 URI 패턴에 고유해야 810합니다. <code><name></code>으로 좋은 예는 회사 이름이나 811애플리케이션의 Android 패키지 이름을 들 수 있습니다. 812<code><type></code>으로 좋은 예는 URI와 연관된 테이블을 식별하는 813문자열을 들 수 있습니다. 814 </p> 815 816 </li> 817</ul> 818<p> 819 예를 들어 어떤 제공자의 권한이 820<code>com.example.app.provider</code>이고, 이것이 821<code>table1</code>이라는 테이블을 노출하는 경우, <code>table1</code>의 여러 행에 대한 MIME 유형은 다음과 같습니다. 822</p> 823<pre> 824vnd.android.cursor.<strong>dir</strong>/vnd.com.example.provider.table1 825</pre> 826<p> 827 <code>table1</code>의 행 하나의 경우, MIME 유형은 다음과 같습니다. 828</p> 829<pre> 830vnd.android.cursor.<strong>item</strong>/vnd.com.example.provider.table1 831</pre> 832<h3 id="FileMIMETypes">파일의 MIME 유형</h3> 833<p> 834 제공자가 파일을 제공하는 경우, 835{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}를 구현합니다. 836 이 메서드는 제공자가 주어진 콘텐츠 URI에 대해 반환할 수 있는 파일에 대한 MIME 유형의 {@link java.lang.String} 배열을 반환합니다. 837제공하는 MIME 유형을 MIME 유형 필터 인수 기준으로 필터링해야 838클라이언트가 처리하고자 하는 MIME 유형만 반환할 수 있습니다. 839</p> 840<p> 841 예를 들어, 사진 이미지를 <code>.jpg</code>, 842<code>.png</code> 및 <code>.gif</code> 형식의 파일로 제공하는 제공자가 있다고 하겠습니다. 843 애플리케이션이 필터 문자열 <code>image/*</code>("이미지"인 어떤 것)로 {@link android.content.ContentResolver#getStreamTypes(Uri, String) 844ContentResolver.getStreamTypes()}를 호출하면 845 846{@link android.content.ContentProvider#getStreamTypes(Uri, String) 847ContentProvider.getStreamTypes()} 메서드가 다음과 같은 배열을 반환하는 것이 정상입니다. 848</p> 849<pre> 850{ "image/jpeg", "image/png", "image/gif"} 851</pre> 852<p> 853 앱이 <code>.jpg</code> 파일에만 관심이 있는 경우에는 854필터 문자열 <code>*\/jpeg</code>으로 {@link android.content.ContentResolver#getStreamTypes(Uri, String) 855 ContentResolver.getStreamTypes()}를 호출할 수 있습니다. 그러면 856 {@link android.content.ContentProvider#getStreamTypes(Uri, String) 857 ContentProvider.getStreamTypes()}가 다음과 같이 반환하는 것이 정상입니다. 858<pre> 859{"image/jpeg"} 860</pre> 861<p> 862 제공자가 필터 문자열에서 요청한 MIME 유형 중 제공하는 것이 없는 경우, 863{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}가 864 <code>null</code>을 반환하는 것이 정상입니다. 865</p> 866 867 868<!-- Implementing a Contract Class --> 869<h2 id="ContractClass">계약 클래스 구현</h2> 870<p> 871 계약 클래스는 <code>public final</code> 클래스로, 이 안에 URI, 열 이름, MIME 유형의 872상수 정의 및 제공자에 관련된 다른 메타 데이터가 들어 있습니다. 873이 클래스는 URI, 열 이름 등의 실제 값에 변경된 내용이 있더라도 874 제공자에 올바르게 액세스할 수 있도록 보장하여 제공자와 875다른 애플리케이션 사이의 계약을 확립합니다. 876</p> 877<p> 878 계약 클래스가 개발자에게 유용한 이유는 또 있습니다. 이 클래스는 보통 자신의 상수 이름으로 879니모닉 이름을 가지기 때문에 개발자가 열 이름 또는 URI에 잘못된 값을 사용할 가능성이 덜합니다. 880이것도 클래스의 일종이기 때문에 Javadoc 문서를 포함할 수 있습니다. 881Eclipse와 같은 통합 개발 환경은 계약 클래스의 상수 이름을 자동 완성하고 882해당 상수에 대한 Javadoc을 표시할 수 있습니다. 883</p> 884<p> 885 개발자가 애플리케이션에서 계약 클래스의 클래스 파일에 액세스할 수는 없지만 886여러분이 제공하는 <code>.jar</code> 파일에서 이를 애플리케이션 안으로 정적으로 컴파일링할 수 있습니다. 887</p> 888<p> 889 {@link android.provider.ContactsContract} 클래스와 890이에 중첩된 클래스가 계약 클래스의 예시입니다. 891</p> 892<h2 id="Permissions">콘텐츠 제공자 권한 구현</h2> 893<p> 894 Android 시스템의 모든 측면에 대한 권한과 액세스는 895<a href="{@docRoot}guide/topics/security/security.html">보안 및 권한</a> 주제에 설명되어 있습니다. 896 <a href="{@docRoot}guide/topics/data/data-storage.html">데이터 저장소</a> 주제에서도 897다양한 유형의 저장소에 적용되는 보안 및 권한을 설명하고 있습니다. 898 간략히 말해 요점은 다음과 같습니다. 899</p> 900<ul> 901 <li> 902 기본적으로, 기기의 내부 저장소에 저장된 데이터 파일은 903본인의 애플리케이션과 제공자 전용입니다. 904 </li> 905 <li> 906 본인이 생성한 {@link android.database.sqlite.SQLiteDatabase} 데이터베이스는 907본인의 애플리케이션과 제공자만의 비공개 데이터입니다. 908 </li> 909 <li> 910 기본적으로 외부 저장소에 저장하는 데이터 파일은 <em>공개</em>이고 911<em>누구나 읽을 수 있습니다</em>. 외부 저장소에 있는 파일로의 액세스를 제공하는 데 콘텐츠 제공자를 쓸 수는 912없습니다. 다른 애플리케이션이 다른 API 호출을 사용하여 해당 파일을 읽고 쓸 수 있기 때문입니다. 913 </li> 914 <li> 915 기기의 내부 저장소에 있는 파일 또는 SQLite 데이터베이스를 열거나 생성하기 위한 메서드 호출은 916다른 모든 애플리케이션에 읽기 및 쓰기 액세스 권한을 허가할 가능성이 있습니다. 917내부 파일이나 데이터베이스를 제공자의 리포지토리로 사용하고 918"누구나 읽을 수 있는" 또는 "누구나 쓸 수 있는" 액세스를 부여하면 919매니페스트에서 제공자에 대해 설정한 권한이 데이터를 보호하지 못합니다. 920내부 저장소 안에 있는 파일과 데이터베이스에 대한기본 액세스는 "비공개"이며, 제공자의 리포지토리가 이것을 변경하면 안 됩니다. 921 </li> 922</ul> 923<p> 924 데이터로의 액세스를 제어하기 위해 콘텐츠 제공자 권한을 쓰고자 하는 경우, 925데이터를 내부 파일, SQLite 데이터베이스 또는 "클라우드"(예: 원격 서버) 안의 926내부 파일로 저장해야 하고, 파일과 데이터베이스를 애플리케이션만의 비공개로 유지해야 합니다. 927</p> 928<h3>권한 구현</h3> 929<p> 930 기본 데이터가 비공개라고 하더라도 모든 애플리케이션이 제공자를 읽고 제공자에 쓸 수 있습니다. 931기본적으로 제공자에는 권한이 설정되어 있지 않기 때문입니다. 이를 변경하려면, 932<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 933 <provider></a></code> 요소의 속성이나 하위 요소를 사용하여 934매니페스트 파일에 있는 제공자의 권한을 설정합니다. 권한은 제공자 전체에 적용되도록 설정할 수도 있고, 935특정 테이블에, 또는 심지어 특정 레코드에 적용되게 할 수도 있고 세 가지 모두를 택할 수도 있습니다. 936</p> 937<p> 938 제공자에 대한 권한은 매니페스트 파일에 있는 하나 이상의 939<code><a href="{@docRoot}guide/topics/manifest/permission-element.html"> 940 <permission></a></code> 요소로 정의합니다. 941제공자에 고유한 권한을 설정하려면 942<code><a href="{@docRoot}guide/topics/manifest/permission-element.html#nm"> 943 android:name</a></code> 속성에 Java 스타일 범위를 사용합니다. 예를 들어 읽기 권한의 이름을 944<code>com.example.app.provider.permission.READ_PROVIDER</code>로 지정합니다. 945 946</p> 947<p> 948 다음 목록은 제공자 권한의 범위를 설명한 것입니다. 949제공자 전체에 적용되는 권한부터 시작하여 점차 세분화된 권한이 됩니다. 950 보다 세부화된 권한이 범위가 큰 것보다 우선합니다. 951</p> 952<dl> 953 <dt> 954 단일 읽기-쓰기 제공자 수준 권한 955 </dt> 956 <dd> 957 제공자 전체로의 읽기와 쓰기 액세스 양쪽 모두를 제어하는 하나의 권한으로, 958<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 959 <provider></a></code> 요소의 960<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn"> 961 android:permission</a></code> 속성으로 지정됩니다. 962 </dd> 963 <dt> 964 별도의 읽기 및 쓰기 제공자 수준 권한 965 </dt> 966 <dd> 967 제공자 전체에 대한 읽기 권한과 쓰기 권한입니다. 이들 권한은 968<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 969 <provider></a></code> 요소의 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#rprmsn"> 970 android:readPermission</a></code>와 971<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#wprmsn"> 972 android:writePermission</a></code> 속성으로 973지정합니다. 이들이 974<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn"> 975 android:permission</a></code>에서 요구하는 권한보다 우선합니다. 976 </dd> 977 <dt> 978 경로 수준 권한 979 </dt> 980 <dd> 981 제공자의 콘텐츠 URI에 대한 읽기, 쓰기 또는 읽기/쓰기 권한입니다. 제어하고자 하는 각 URI를 직접 지정하되, 982이때 983<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 984 <provider></a></code> 요소의 985<code><a href="{@docRoot}guide/topics/manifest/path-permission-element.html"> 986 <path-permission></a></code> 하위 요소를 사용합니다. 지정하는 콘텐츠 URI마다 987읽기/쓰기 권한, 읽기 권한 또는 쓰기 권한을 하나씩 지정하거나 셋 모두를 지정할 수 있습니다. 988읽기 및 쓰기 권한이 읽기/쓰기 권한보다 우선합니다. 989또한, 경로 수준 권한이 제공자 수준 권한보다 우선합니다. 990 </dd> 991 <dt> 992 임시 권한 993 </dt> 994 <dd> 995 애플리케이션에 임시 액세스를 허용하는 권한 수준입니다. 996해당 애플리케이션에 일반적으로 요구되는 권한이 없더라도 무관합니다. 997임시 액세스 기능은 매니페스트에서 요청해야 하는 998권한과 애플리케이션 개수를 줄여줍니다. 임시 권한을 사용하는 경우, 999제공자에 대하여 "영구" 권한을 필요로하는 애플리케이션은 1000모든 데이터에 지속적으로 액세스하는 것들뿐입니다. 1001 <p> 1002 이메일 제공자와 앱을 구현할 때 필요한 권한을 예로 들어 보겠습니다. 1003외부 이미지 뷰어 애플리케이션으로 하여금 제공자에서 보낸 사진 첨부 파일을 1004표시하도록 허용하고자 한다고 가정합니다. 권한을 요구하지 않고 이미지 뷰어에 필수 액세스를 부여하려면, 1005사진에 대한 콘텐츠 URI에 해단되는 임시 권한을 설정하십시오. 1006사용자가 사진을 표시하기를 원할 때 앱이 사진의 콘텐츠 URI와 권한 플래그를 포함하는 인텐트를 1007이미지 뷰어에 보내도록 이메일 앱을 설계합니다. 그러면 해당 이미지 뷰어가 1008이메일 제공자에 사진 검색을 쿼리할 수 있으며, 이 뷰어에 제공자에 대한 정상적인 읽기 권한이 없더라도 무방합니다. 1009 1010 </p> 1011 <p> 1012 임시 권한을 사용하려면, 1013<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 1014 <provider></a></code> 요소의 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> 1015 android:grantUriPermissions</a></code> 속성을 설정하거나 1016하나 이상의 1017<code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"> 1018 <grant-uri-permission></a></code> 하위 요소를 1019<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 1020 <provider></a></code> 요소에 추가하면 됩니다. 임시 권한을 사용하는 경우, 1021제공자에서 콘텐츠 URI에 대한 지원을 제거할 때마다 {@link android.content.Context#revokeUriPermission(Uri, int) 1022 Context.revokeUriPermission()}을 호출해야 합니다. 1023그러면 콘텐츠 URI가 임시 권한과 연관됩니다. 1024 </p> 1025 <p> 1026 속성의 값에 따라 제공자에 액세스 가능한 정도가 결정됩니다. 1027 속성이 <code>true</code>로 설정되어 있는 경우라면 1028시스템이 제공자 전체에 임시 권한을 허용하며, 제공자 수준 또는 1029경로 수준 권한에서 요구하는 다른 모든 권한을 재정의합니다. 1030 </p> 1031 <p> 1032 이 플래그가 <code>false</code>로 설정되면, 반드시 1033<code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"> 1034 <grant-uri-permission></a></code> 하위 요소를 1035<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 1036 <provider></a></code> 요소에 추가해야 합니다. 각 하위 요소는 임시 권한을 허용한 1037콘텐츠 URI(하나 또는 여러 개)를 나타냅니다. 1038 </p> 1039 <p> 1040 애플리케이션에 임시 액세스를 위임하려면, 인텐트에 1041{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} 또는 1042{@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} 플래그, 또는 둘 모두가 들어 있어야 합니다. 이들은 1043{@link android.content.Intent#setFlags(int) setFlags()} 메서드로 설정됩니다. 1044 </p> 1045 <p> 1046 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> 1047 android:grantUriPermissions</a></code> 속성이 존재하지 않으면 1048<code>false</code>인 것으로 가정합니다. 1049 </p> 1050 </dd> 1051</dl> 1052 1053 1054 1055<!-- The Provider Element --> 1056<h2 id="ProviderElement"><provider> 요소</h2> 1057<p> 1058 {@link android.app.Activity}와 {@link android.app.Service} 구성 요소와 마찬가지로, 1059{@link android.content.ContentProvider}의 하위 클래스는 1060<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 1061 <provider></a></code> 요소를 사용하여 매니페스트 파일에서 1062애플리케이션에 대해 정의되어야 합니다. Android 시스템이 1063해당 요소에서 다음과 같은 정보를 가져옵니다. 1064<dl> 1065 <dt> 1066 권한 1067(<a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">{@code 1068android:authorities}</a>) 1069 </dt> 1070 <dd> 1071 시스템 내에서 제공자 전체를 식별하는 상징적 이름입니다. 이 속성은 1072 1073<a href="#ContentURI">콘텐츠 URI 설계</a> 섹션에서 자세히 설명되어 있습니다. 1074 </dd> 1075 <dt> 1076 제공자 클래스 이름 1077(<code> 1078<a href="{@docRoot}guide/topics/manifest/provider-element.html#nm">android:name</a> 1079 </code>) 1080 </dt> 1081 <dd> 1082 {@link android.content.ContentProvider}를 구현하는 클래스입니다. 이 클래스는 1083 1084<a href="#ContentProvider">ContentProvider 클래스 구현</a> 섹션에 자세히 설명되어 있습니다. 1085 </dd> 1086 <dt> 1087 권한 1088 </dt> 1089 <dd> 1090 제공자의 데이터에 액세스하기 위해 다른 애플리케이션이 1091반드시 가지고 있어야 하는 권한을 나타내는 속성입니다. 1092 <ul> 1093 <li> 1094 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> 1095 android:grantUriPermssions</a></code>: 임시 권한 플래그입니다. 1096 </li> 1097 <li> 1098 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn"> 1099 android:permission</a></code>: 단일 제공자 전범위 읽기/쓰기 권한입니다. 1100 </li> 1101 <li> 1102 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#rprmsn"> 1103 android:readPermission</a></code>: 제공자 전범위 읽기 권한입니다. 1104 </li> 1105 <li> 1106 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#wprmsn"> 1107 android:writePermission</a></code>: 제공자 전범위 쓰기 권한입니다. 1108 </li> 1109 </ul> 1110 <p> 1111 각종 권한과 그에 상응하는 속성은 1112 1113<a href="#Permissions">콘텐츠 제공자 권한 구현</a> 섹션에 자세히 설명되어 있습니다. 1114 </p> 1115 </dd> 1116 <dt> 1117 시작 및 제어 속성 1118 </dt> 1119 <dd> 1120 이와 같은 속성은 Android 시스템이 제공자를 시작하는 방법과 시점, 1121제공자의 프로세스 특징과 기타 런타임 설정 등을 결정합니다. 1122 <ul> 1123 <li> 1124 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#enabled"> 1125 android:enabled</a></code>: 시스템이 제공자를 시작할 수 있게 해주는 플래그입니다. 1126 </li> 1127 <li> 1128 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#exported"> 1129 android:exported</a></code>: 다른 애플리케이션이 이 제공자를 사용할 수 있게 해주는 플래그입니다. 1130 </li> 1131 <li> 1132 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#init"> 1133 android:initOrder</a></code>: 같은 프로세스 내의 다른 제공자와 비교하여 1134이 제공자가 시작되어야 하는 순서입니다. 1135 </li> 1136 <li> 1137 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#multi"> 1138 android:multiProcess</a></code>: 클라이언트를 호출하는 것과 1139같은 프로세스에서 시스템이 제공자를 시작할 수 있게 해주는 플래그입니다. 1140 </li> 1141 <li> 1142 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#proc"> 1143 android:process</a></code>: 제공자가 실행해야 하는 프로세스의 1144이름입니다. 1145 </li> 1146 <li> 1147 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#sync"> 1148 android:syncable</a></code>: 제공자의 데이터가 1149서버에 있는 데이터와 동기화될 예정임을 나타내는 플래그입니다. 1150 </li> 1151 </ul> 1152 <p> 1153 이 속성은 1154<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 1155 <provider></a></code> 1156요소에 대한 개발자 가이드 주제에 상세하게 기록되어 있습니다. 1157 </p> 1158 </dd> 1159 <dt> 1160 정보 속성 1161 </dt> 1162 <dd> 1163 제공자에 대한 선택 항목 아이콘 및 레이블입니다. 1164 <ul> 1165 <li> 1166 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#icon"> 1167 android:icon</a></code>: 제공자의 아이콘이 들어 있는 드로어블 리소스입니다. 1168 이 아이콘은 1169<em>설정</em> > <em>앱</em> > <em>모두</em>에 있는 앱 목록에서 제공자의 레이블 옆에 표시됩니다. 1170 </li> 1171 <li> 1172 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#label"> 1173 android:label</a></code>: 제공자 또는 그 데이터, 또는 둘 모두를 설명하는 정보 레이블입니다. 1174 이 레이블은 1175<em>설정</em> > <em>앱</em> > <em>모두</em>에 있는 앱 목록에 표시됩니다. 1176 </li> 1177 </ul> 1178 <p> 1179 이 속성은 1180<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> 1181 <provider></a></code>요소에 대한 개발자 가이드 주제에 상세하게 기록되어 있습니다. 1182 </p> 1183 </dd> 1184</dl> 1185 1186<!-- Intent Access --> 1187<h2 id="Intents">인텐트 및 데이터 액세스</h2> 1188<p> 1189 애플리케이션이 콘텐츠 제공자에 간접적으로 액세스하려면 {@link android.content.Intent}를 사용하면 됩니다. 1190 이 애플리케이션은 {@link android.content.ContentResolver} 또는 1191{@link android.content.ContentProvider}의 메서드 중 어느 하나도 호출하지 않습니다. 1192대신, 액티비티를 시작하는 인텐트를 전송합니다. 이 인텐트는 제공자가 소유한 애플리케이션의 일부인 경우가 많습니다. 1193대상 액티비티가 데이터를 자체 UI에서 검색하고 표시하는 역할을 맡습니다. 1194인텐트의 동작에 따라 대상 액티비티가 사용자에게 프롬프트를 표시하여 제공자의 데이터를 수정하도록 할 수도 있습니다. 1195 인텐트에는 대상 액티비티가 UI에 표시하는 "추가" 데이터가 들어 있을 수도 있습니다. 1196그러면 사용자에게 이 데이터를 변경할 수 있는 옵션이 주어지고, 그런 다음 이를 사용하여 1197제공자 내의 데이터를 수정할 수 있습니다. 1198</p> 1199<p> 1200 1201</p> 1202<p> 1203 데이터 무결성을 보장하는 데 유용한 것을 원하면 인텐트 액세스를 사용하는 것이 좋습니다. 1204엄격하게 정의된 비즈니스 논리에 따라 데이터가 삽입, 업데이트되고 삭제되는 것이 제공자를 크게 좌우할 수도 있습니다. 1205이런 경우에 해당되면, 다른 애플리케이션에 데이터를 직접 수정하도록 허용하면 데이터가 잘못되는 1206결과를 초래할 수 있습니다. 개발자들에게 인텐트 액세스 사용을 허용하려면, 그 내용을 철저히 기록해두어야 합니다. 1207 개발자들에게 자기 애플리케이션의 UI를 사용한 인텐트 액세스가 1208코드로 데이터를 수정하려 시도하는 것보다 나은 이유를 설명해주십시오. 1209</p> 1210<p> 1211 제공자의 데이터를 수정하고자 하는 수신되는 인텐트 처리도 다른 인텐트 처리와 다를 바가 없습니다. 1212 인텐트 사용에 대한 자세한 내용은 1213<a href="{@docRoot}guide/components/intents-filters.html">인텐트 및 인텐트 필터</a> 주제를 읽으면 확인할 수 있습니다. 1214</p> 1215