Content Provider

6 분 소요

Content Provider란?

Content Provider는 말그대로 데이터를 제공해 주는 클래스이다. 이를테면, 내가 만든 앱이 주소록에 접근해야 하는데, 기본 앱이나 다른 어떤 종류의 앱들 역시 주소록에 접근해야 하는 경우가 있다. 이 서로 다른 앱들이 하나의 데이터 소스에 접근해야 할 때, 바로 Content Provider가 그 사이에서 데이터를 제공해 주는 것이다.

Content Provider를 사용하는 이유

  1. 기저에 있는 data source를 쉽게 바꿀 수 있다.
  2. Loader나 Cursor, Adaptor를 이용하려면 써야만 한다.
  3. (주요 이유) 다른 앱들로 하여금 보안된 환경에서 당신의 data source에 접근할 수 있도록 한다.

개념도

관계도

순서는 아래와 같다.

  • 외부의 앱 –(Uri)–> ContentResolver –> ContentProvider –> Datasource

이후에는

  • 외부의 앱 <–(Cursor)– ContentResolver

DroidTermsExample & QuizApp

본레슨에서는 DroidTermsExample이라는 예시 앱(완성품-안드로이드에서 자주 쓰이는 용어와 그 정의를 갖고 있는 앱)에서 사용하는 data source에 Content Provider를 이용해 접근하는 Quiz App을 만들어보면서 Content Provider 사용법을 연습한다.

ContentProvider사용의 일반적 순서

  1. ContentProvider 사용권한 획득
  2. ContentResolver Get
  3. 4가지 기본 동작 중 선택(query, insert, update, delete)
  4. 어떤 종류의 데이타를 읽기를(고치기를)원하는지 정하고 URI생성
  5. 데이터를 읽어온경우 UI에 데이타 뿌리기

하나하나씩 진행해 보자.

1. 사용권한 획득하기

어떤 앱에서 다른 앱의 ContentProvider를 사용하고자 한다면, 먼저 권한신청을 해야 한다. 우리가 quizexam 앱에서 droidtermsexample앱의 ContentProvider를 사용하고자 하니, quizexam앱의 menifesto에 아래와 같이 선언.

<uses-permission android:name="com.example.udacity.droidtermsexample.TERMS_READ" />

읽기 권한만 요청한 상태다.

2. ContentProvider 얻기

먼저 ContentProvider가 왜 필요한지 생각해 보자. 안드로이드 앱들은 각각의 Datasource가 있을 수 있고, 이 말은 DataSource의 개수만큼 ContentProvider가 있다는 뜻이다. 또한 각각의 ContentProvider에 요청을 보내는 앱은 우리 말고도 더 있을 수 있다. 이럴 경우 상당히 많은 양의 통신이 발생할 수 있는데..이를 처리해 주는 중개자 역할을 하는 것이 ContentResolver다.

!(ContentResolver개념)[https://image.slidesharecdn.com/week6-retroonlesson4b-150304024004-conversion-gate01/95/android-jam-contentproviders-udacity-lesson-4b-9-638.jpg?cb=1426598732]

아래 명령으로 간단히 얻을 수 있다.

ContentResolver resolver = getContentResolver();

3. 4가지 기본 동작 명령어

아래의 명령어를 ContentResolver.명령어()를 통해 ContentProvider에게 전달한다.

  • 데이타 읽기 : query()
  • Row 추가하기 : insert()
  • 데이타 업데이트 : update()
  • Row 삭제하기 : delete()

우리는 데이타를 읽어오고 싶기 때문에 아래 쿼리 메소드를 통해 cursor 객체에 저장할 수 있다.

Cursor cursor = Resolver.query(DroidTermsExampleContract.CONTENT_URI, 
    null,
    null,
    null,
    null);

여기서 잠시, DroidTermsExampleContract.CONTENT_URI는 어디서 나온것이란 말인가? 대부분의 ContentProvider들은 그에 해당하는 Contract Class를 갖고 있다. Contract클래스에서는 해당 ContentProvider에 접근할 때 사용할 다양한 상수들을 갖고 있는데, URI도 그 중 하나이다. 즉, DroidTermsExampleContractClass에 있는 CONTENT_URI상수는 특정 값을 갖고 있으며, 이를 이용해서 일일이 우리가 URI를 생성하지 않아도 된다. 여기서 해당 값은

content://com.example.udacitiy.droidtermsexample/terms

이다. 여기서, content://는 scheme이다. 보통 ContentProvider의 Prefix로 쓰인다. com.example.udacitiy.droidtermsexample는 Content Authority다. terms는 우리가 찾고자 하는 특정 데이터이다.

다시한번 공홈 링크의 내용을 참조해 보자 클릭

.query()에 들어가는 다른 argument에 대해서 알아보면, SQL문의 SELECT문과 흡사하다.

.query(<URI>, <projection>, <selection>, <selection args>, <sort order>);
  1. URI: 위에서 설명
  2. projection: Columne 선택
  3. selection: row 선택
  4. selection args: row에 있는 값을 기준으로 row를 선택. 예를 들면..값이 a로 시작하는 row를 선택한다든지..
  5. sort order: 정렬 방법 선택

만약 null로 입력할 경우, 모든 값을 불러온다.

또한 Cursor instance를 생성하는 (query명령어의) 경우, background Thread에서 수행해야 한다. 예제에서는 AsyncTask를 써서 백그라운드에서 cursor생산, onPostExcute에서 Cursor에서 데이터 뽑아내기를 했다.

Cursor 명령어

(현재까지 이해한 바에 따르면) Cursor 객체는 RDB의 SELECT문을 이용해 하나의 Tabel을 불러온 것이다. 그럼 몇 번째 컬럼 몇 번째 로우에 어떤 데이터가 있을지 알아봐야 할 시간.. 실제적으로 그 Table안에 있는 데이터와 소통하는 방법을 알아보자.

대표적으로 쓰이는 Cursor 메쏘드는 다음과 같다.

  1. moveToNext(): row 단위로 소통하는 Cursor로서는 보통 첫번쨰 row에서 단계별로 내려가면서 데이터를 훑게 된다. 포커스를 한 줄 아래로 내리라는 뜻. 내려가는대 성공하면 true를, 맨 마지막줄이어서 내려갈 수 없을 때는 false를 반환. (참고로 Cursor를 처음 부른 상태에서는 Cursor의 위치는 -1이다(아무데도아닌곳))
  2. moveToFirst(): 맨 첫번째 row로 커서 이동
  3. getColumnIndex(String heading): 불러온 테이블의 Column에 Index가 순서대로 부여된다. 헤딩에 있는 String값을 넣으면 몇 번째 Column인지 반환한다. 헤딩은 Contents Provider Contract 클래스에 모두 정의되어 있으니 갖다쓴다.
  4. get<Type>(int ColumnIndex): 드디어 value를 반환한다. 타입을 명시해야 하며, 현재 Cursor가 위치한 row에서 입력한 column index 에 따라 반환.
  5. getCount(): cursor의 row수를 반환한다.
  6. close():커서와 소통이 끝나면 항상 close해야 메모리 leak을 방지할 수 있다.

이후 커서에서 Data를 가져오는 데는 마음껏 하면 되지만 전형적인 시작코드는 아래와 같다.

    mData = cursor 
    if (mData != null) {
        if (!mData.moveToNext()) {
            mData.moveToFirst();
        }
        //do whatever
    }

댓글남기기