우선 아래 링크의 글을 먼저 보실 것을 권합니다.
http://www.androidpub.com/48937
안드로이드는 SDK의 문서만으로 도저히 이해하기 힘든 것이 너무 많은 듯합니다.
다행히 소스가 공개되어 있어서 framework 쪽 소스까지 뒤져 보면서 동작을 확인해야,
SDK를 이용해서 프로그램을 짤 때 도움이 될듯 하네요.
이 글을 쓰게 된 이유는
---------- 이 궁금증에서 시작을 하였습니다. ----------------
Intent에서 Uri를 사용하여 새로운 activity 띄울 때가 좀 아리송하네요.
Uri uri = Uri.parse("content://media/internal/images/media");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
위와 같이 코딩하고 실행하면,
Gallery의 두번째 화면 ( 폴더들어가서 폴더 내용 보여주는 것 같은 화면) 이 뜹니다.
------------------------------------------------------------
보통 안드로이드 책이나 강좌에서는 Intent를 띄울 때 명시적으로 콤포넌트 이름을 정하던 class 를 사용하던지 해서 호출하는 것은 많이 나옵니다.
그런데, 위 궁금증처럼 URI 를 사용한 경우는 도대체 어느 쪽으로 가는지 알 수가 없더군요.
동작을 하는데 말이죠.
content://media/internal/images/media
우선 이 URI를 분석해 봅니다.
http://developer.android.com/reference/android/content/Intent.html
위 링크의 글을 보면 "Intent Resolution"에는 명시적인것과 암묵적인 것이 있는데,
암묵적인 것에 해당 한다는 이야기가 있구요.
http://developer.android.com/guide/topics/providers/content-providers.html
컨텐츠 프로바이더 설명 링크에 가보면,
Content URI Summary
Here is a recap of the important parts of a content URI:
- Standard prefix indicating that the data is controlled by a content provider. It's never modified.
The authority part of the URI; it identifies the content provider. For third-party applications, this should be a fully-qualified class name (reduced to lowercase) to ensure uniqueness. The authority is declared in the
<provider>
element'sauthorities
attribute:<provider name=".TransportationProvider"
authorities="com.example.transportationprovider"
. . . >The path that the content provider uses to determine what kind of data is being requested. This can be zero or more segments long. If the content provider exposes only one type of data (only trains, for example), it can be absent. If the provider exposes several types, including subtypes, it can be several segments long — for example, "
land/bus
", "land/train
", "sea/ship
", and "sea/submarine
" to give four possibilities.The ID of the specific record being requested, if any. This is the
_ID
value of the requested record. If the request is not limited to a single record, this segment and the trailing slash are omitted:content://com.example.transportationprovider/trains
이런 설명이 나오는 데요.
위 설명대로면
content://media/internal/images/media
여기서 authority 값으로 Provider 를 구분하는 값이라고 합니다.
그래서 안드로이드 OS 소스에서 찾아 보았더니
OS소스/packages/providers/MediaProvider
에서 관련 부분을 찾았답니다.
위 폴더의 AndroidManifest.xml 파일을 보면,
<application android:process="android.process.media"
android:label="@string/app_label">
<provider android:name="MediaProvider"
android:authorities="media"
android:multiprocess="false" />
이렇게 되어 있습니다.
즉, content://media/ 는 MediaProvider 프로젝트의 MediaProvider.java 가 담당하는 것이더군요.
다음으로
content://media/internal/images/media
internal 부분을 MediaProvider.java 찾아보면,
String volume = srcuri.toString().substring(16, 24); // extract internal/external
Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
getContext().getContentResolver().notifyChange(uri, null);
이런 코드가 나옵니다.
internal부분은 volume 값으로 단말기의 내부인지 외부인지를 구분하는 용도더군요.
DatabaseHelper db;
if (INTERNAL_VOLUME.equals(volume)) {
db = new DatabaseHelper(getContext(), INTERNAL_DATABASE_NAME, true);
} else if (EXTERNAL_VOLUME.equals(volume)) {
String path = Environment.getExternalStorageDirectory().getPath();
int volumeID = FileUtils.getFatVolumeId(path);
if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
// generate database name based on volume ID
String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
db = new DatabaseHelper(getContext(), dbName, false);
} else {
throw new IllegalArgumentException("There is no volume named " + volume);
}
internal 인 경우, "internal.db" 파일을 열고,
external 인경우, external-volumeID.db 파일을 열더군요.
db 파일들은
# pwd
pwd
/data/data/com.android.providers.media/databases
# ls
ls
external-12e23719.db
external-a0e3d1c.db
internal.db
#
위와 같이 들어 있더군요.
다음으로
content://media/internal/images/media
녹색 부분도 소스에 보면 무슨 의미인지 알수 있는 소스가 있는데요.
static
{
URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
... 중략.....
이런식으로 URI의 뒷부분을 비교하여 실제로 어느 테이블에 데이터를 가져올지를 구분하더군요.
switch (table) {
case IMAGES_MEDIA:
qb.setTables("images");
if (uri.getQueryParameter("distinct") != null)
qb.setDistinct(true);
break;
이렇게 실제로는 images 테이블을 가져 오고 있습니다.
수고하세요 ^^