안드로이드의 연락처 데이터베이스 구조는 상당히 복잡한 구조로 되어있습니다.

연락처 관련 어플을 만들기 위해서는 이러한 구조를 이해해야지만 데이터를 관리하는것이 가능합니다.

이번 포스트에서는 안드로이드에서 연락처데이터를 어떻게 관리하는지를 알아보도록 하겠습니다.

 

그럼 먼저 안드로이드 개발자사이트에 있는 연락처 데이터베이스의 구조 그림을 보도록 합시다.

 

contacts-2

 

저는 처음에 이 그림을 보고 “데이터베이스를 사용하지 않았나?” 라는 생각을 했습니다.

그러나 안드로이드 연락처의 기본 데이터는 sqllite 데이터베이스를 사용합니다.

 

사실 이것만 봐서는 감이 잘 안오는데 데이터베이스를 까보면 확실해 집니다.

그럼 안드로이드 연락처 데이터베이스 파일을 열어서 테이블 구조를 확인해 보도록 하겠습니다.

 

 

 

- 연락처 데이터베이스 구조

먼저 sqllite 데이터베이스를 좀 더 쉽게보기 위해서 아래 사이트에서 툴을 다운로드 하세요.

http://sqliteadmin.orbmu2k.de/

 

그다음 안드로이드의 DDMS 를 켠다음 에뮬레이터의 아래와같은 파일을 다운로드 받습니다.

/data/data/com.android.providers.contacts/databases/contacts2.db

 

이것이 바로 안드로이드의 연락처 sqllite 데이터베이스 파일이며 sqllite 어드민툴을 가지고 열어보면 아래와 같은 형태로 구조를 확인할 수 있습니다.

image

 

 

테이블은 여러가지가 있지만 여기서 중요하게 볼 것은 contacts, raw_contacts, data 테이블 입니다.

이 세 테이블에 연락처의 기본 데이터가 저장되게 됩니다.

 

 

 

 

- 접근방식

사실 sqllite 는 데이터베이스 파일에 접근 권한만 있다면 테이블의 모든 내용을 수정/삭제/업데이트 할 수 있습니다.

그래서 이러한 연락처 테이블은 각 어플에서 바로 접근 가능한 것이 아니라 Contacts provider 를 통해서 접근하게 됩니다.

 

image

 

위와 같이 어플들은 sqllite 데이터베이스 파일인 contacts2.db 파일에 바로 접근하지 못하고 provider 를 통해서만 접근할 수 있습니다.

사용자가 연락처 데이터베이스를 업데이트 하려고 요청을 하면 데이터베이스에 접근하기 전에 Contacts provider 가 먼저 접근권한을 판단해 줍니다.

 

참고로 안드로이드는 android.provider.ContactsContract 클래스의 하위 클래스로 테이블명과 동일한 이름의 클래스를 지원합니다.

예를들어 android.provider.ContactsContract.Contacts 클래스는 contacts 테이블과 매칭되며 다양한 접근 정보를 알 수 있습니다.

 

 

 

- 연락처 업데이트

안드로이드의 연락처 구조가 위와같은 형태라면 연락처를 추가하기 위해서는 가장먼저 contacts 테이블에 추가해야 하는것 처럼 보입니다.

그러나 안드로이드 에서는 contacts 테이블에 바로 연락처를 추가하는것을 제공하지 않습니다.

provider 에서 insert 를 제공하는 테이블은 raw_contacts 와 data 입니다.

 

그렇다면 어떻게 contacts 테이블에 연락처를 추가 할 수 있을까요?

바로 raw_contacts 테이블에 데이터를 추가하면 자동으로 contacts 테이블에 추가해 줍니다.

이것을 안드로이드에서는 “Auto aggregation” 이라고 부릅니다.

 

안드로이드는 “Auto aggreation” 을 수행하면서 contacts 테이블에 데이터를 추가할지 아니면 업데이트할지를 결정합니다.

만약 추가된 raw_contacts 를 기존의 contacts 에 업데이트 하려면 아래와 같은 조건을 검사합니다.

    - 이름이 같을때

    - 이름이 동일한 단어로 구성되어있지만 순서가 다를때

    - 전화번호나, 이메일 아니면 닉네임을 공유할때

 

이러한 조건에 의해 contacts 테이블이 자동으로 업데이트 됩니다.

 

연락처를 추가하기 위해서는 아래와 같은 코드를 사용합니다.

ContentValues values = new ContentValues();
values.put(RawContacts.SOURCE_ID, sourceId);
values.put(RawContacts.ACCOUNT_NAME, account.name);
values.put(RawContacts.ACCOUNT_TYPE, account.type);
Uri uri = resolver.insert(RawContacts.CONTENT_URI, values);

SOURCE_ID 는 해당 계정에서 유일한 계정의 id 를 말합니다.

 

 

- 연락처 삭제

데이터베이스를 까보면 테이블의 삭제에 trigger 가 걸려있는것을 볼 수 있습니다.

그래서 삭제시 연관된 다른 테이블의 데이터를 같이 삭제하게 됩니다.

 

예를들어 raw_contacts 를 삭제하면 종속된 data 는 자동으로 삭제됩니다.

contacts 를 지우면 거기에 종속된 raw_contacts 역시 자동으로 삭제됩니다.

 

그러나 안드로이드에서 contacts provider 를 이용해서 raw_contacts 를 삭제하면 데이터베이스 에서 바로 지워지지 않는것을 알 수 있습니다.

이것은 실제 삭제는 하지않고 raw_contacts 테이블의 deleted 필드를 1로 셋팅합니다.

 

이렇게 셋팅된 데이터는 SyncAdapter 에 의해서 실제로 삭제가 이루어지게 됩니다.

그렇다면 SyncAdapter 에서는 어떻게 실 삭제를 할까요?

그것은 아래와 같은 코드로 이루어지게 됩니다.

resolver.delete(
RawContacts.CONTENT_URI
.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.build(),
RawContacts.ACCOUNT_NAME +"=? AND " + RawContacts.ACCOUNT_TYPE+"=? AND "+RawContacts.DELETED+"=1",
new String[]{account.name,account.type}
);

즉 실제로 삭제를 하기위해서는 contacts provider 에 CALLER_IS_SYNCADAPTER 파라메터에 “true” 를 심어서 넘겨주면 됩니다.

 

 

 

 

- 데이터 추가

데이터는 이름, 전화번호, 이메일 등 연락처에 표시하는 모든 내용을 저장 하는 테이블 입니다.

데이터는 raw_contacts 에 종속된 테이블 입니다.

그래서 데이터를 생성할 때에는 RAW_CONTACT_ID 를 꼭 써 주어야 합니다.

 

예를들어 아래와 같은 형태로 데이터를 생성합니다.

ContentValues values = new ContentValues();
values.put(Phone.RAW_CONTACT_ID,rawId);
values.put(Phone.NUMBER, phone);
values.put(Phone.TYPE, phoneType);
values.put(Phone.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
Uri uri = resolver.insert(Data.CONTENT_URI, values);

 

데이터는 MIMETYPE 에 따라 구조가 모두 다릅니다.

위의 예제는 전화번호를 등록하는 과정이며 Phone.CONTENT_ITEM_TYPE 에 정의되어있는 String 형태의 mime type 을 같이 넘기는것을 볼 수 있습니다.

 

그럼 이름데이터를 저장하는 예제를 보면 아래와 같습니다.

ContentValues values = new ContentValues();
values.put(StructuredName.RAW_CONTACT_ID, rawId);
values.put(StructuredName.GIVEN_NAME, firstName);
values.put(StructuredName.FAMILY_NAME, lastName);
values.put(StructuredName.DISPLAY_NAME, firstName + " " + lastName);
values.put(StructuredName.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
Uri uri = resolver.insert(Data.CONTENT_URI, values);

이것역시 RAW_CONTACT_ID 와 MIMETYPE 을 저장하지만 나머지는 모두 달라진것을 볼수 있습니다.

 

이러한 가변적인 데이터 구조중 많이 쓰는 데이터구조를 모아서 API 로 만들어 놨는데 예를들어 전화번호, 이름, 이메일 등이 있습니다.

이것은 “android.provider.ContactsContact.CommonDataKinds” 클래스의 하위클래스로서 들어있습니다.

위의 예제들 역시 이 클래스의 하위클래스인 Phone 과 StructuredName 을 이용해서 만들었습니다.

 

이처럼 안드로이드에서는 가변적인 DATA 의 구조를 택함으로써 무한에 가까운 필드확장을 할 수 있게 만들어 놨습니다.

by cranix 2010.12.27 17:01