Android ContentProvider 자세히 알아보기

1. ContentProvider, 즉 콘텐츠 제공자는 모든 데이터 접근에 대한 추상적인 것으로 데이터 접근에 통일된 인터페이스를 제공한다.이것은 다음과 같은 장점이 있다. a. 데이터에 대한 추상성은 모든 구성 요소에 통일된 접근 데이터 방식을 제공하여 구성 요소가 구체적인 데이터의 표현 형식(파일 or 데이터베이스)에 관심을 갖지 않도록 한다.데이터도 사용자의 방문 문제를 상관하지 않고 자신의 관리에만 관심을 가질 수 있다.이렇게 하면 아주 좋은 포장에 도달할 수 있다.b. 인터페이스가 더욱 편리하고 편리하여 구성 요소 간에 데이터를 전송할 수 있다. ContentProvider의 접근 표지는 Uri이고 통일된 ContentResolver를 통해 접근할 수 있다. 또한 ContentResolver와 Uri와 응용 프로그램의 상하문인 Context와 구성 요소 간의 정보 전송 도구인 Intent는 빈틈없이 연결되어 구성 요소 간에 데이터 공유와 데이터 전달을 더욱 편리하고 신속하게 할 수 있다.따라서 ContentProvider의 가장 큰 장점은 서로 다른 구성 요소 간에 편리하게 공유할 수 있다는 것이다.따라서 만약에 응용 프로그램에서 사용하는 데이터가 서로 다른 구성 요소 간에 공유되어야 한다면 ContentProvider를 실현하는 것이 최선의 방안이다.2. 실현 방식인 ContentProvider의 실현 방식은 매우 간단하다. 수요에 따라 일부 인터페이스만 실현하면 된다. 예를 들어query,insert,delete,update,openFile 등이다.그러나 구체적인 데이터의 표현 형식은 서로 다른 목적에 따라 자유롭게 선택하는 것이다. 예를 들어 구조화된 데이터에 대해 SQLiteDatabase를 선택하는 것이 비교적 좋은 방안일 수도 있고 대량의 바이트 흐름 가능한 파일이 우선순위일 수도 있다.주의해야 할 것은 안드로이드의 90퍼센트의 Content Provider 내부는 모두 SQLite Database로 구조화된 데이터를 저장하지만, 이것은 Content Provider가 SQLite Database에서만 데이터를 관리할 수 있다는 것을 의미하지는 않는다.ContentProvider는 일부 인터페이스를 정의했습니다. 필요에 따라 정확한 데이터를 되돌려주면 됩니다. 구체적인 실현 방식은 자유롭게 선택하십시오.예를 들어 Contacts의 ContentProvider는 vCard 방식으로 출력할 수 있다. 즉, vCard의uri를 읽을 때 이 흐름은 vCard 형식의 파일 흐름이고 실현된 사고방식은 이렇다

Cursor query(Uri, ....) {
   if (uri is for vCard) {
       query the Contact's infomation
       create a cursor with two columns name and size
       put contact's name into cursor
       sum all Contact's field  and get size
       put that size into cursor
       return the cursor
   }
}
이렇게 하면 Query를 통해 이 vCard와 관련된 정보 파일의 이름과 크기를 얻을 수 있고 OpenInputStream을 통해 이 vCard 파일 흐름을 읽을 수 있다. 그러나 실제로 ContentProvider는 vCard 형식의 데이터도 없고 vCard 파일도 하나도 없다. 이것은 OpenFile을 할 때 vCard의uri를 식별하고 Contact 데이터를 vCard 형식으로 바꾸어 출력 흐름에 쓴다

ParcelFileDescriptor openFile(Uri...) {
    if (uri is for vcard) {
       generate vcard with VCardComposer
       write to output stream
    }
}
3. 다른 대체 방안인 ContentProvider는 필수가 아닙니다. 모든 응용 프로그램은 반드시 데이터에 사용되지만 ContentProvider를 만들어서 관리할 수도 있고 파일이나 데이터베이스를 직접 사용할 수도 있습니다. 예를 들어 다음과 같습니다

package com.android.effective;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.os.Bundle;
import android.util.Log;
public class SQLiteDatabaseDemo extends Activity {
    private static final String TAG = "SQLiteDatabaseDemo";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyDatabase db = new MyDatabase(this);

        int id = db.setName("Michael Jordan");
        Log.e(TAG, "id of " + id + " is " + db.getName(id));
    }

    private class MyDatabase {
        private static final String name = "demo.db";
        private static final String table = "demo";
        private final String[] projection = new String[] {"_id", "name" };
        private MyDatabaseHelper helper;

        public MyDatabase(Context context) {
            helper = new MyDatabaseHelper(context, name, null, 1);
        }

        public String getName(int id) {
            final Cursor c = helper.getReadableDatabase().query("demo", projection, "_id=" + id,
                    null, null, null, null);
            if (c == null || !c.moveToFirst()) {
                return null;
            }
            return c.getString(1);
        }

        public int setName(String name) {
            ContentValues cv = new ContentValues();
            cv.put("name", name);
            return (int) helper.getWritableDatabase().insert(table, "name", cv);
        }
    }

    private class MyDatabaseHelper extends SQLiteOpenHelper {
        public MyDatabaseHelper(Context context, String name,
                CursorFactory factory, int version) {
            super(context, name, factory, version);
        }
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE demo (_id INTEGER PRIMARY KEY, name TEXT);");
        }
        @Override
        public void onUpgrade(SQLiteDatabase db, int old, int newver) {

        }
    }
}
이 예에서 ContentProvider를 사용하지 않고 Activity가 SQLiteDatabase를 직접 조작하여 데이터의 관리를 실현하거나 데이터베이스를 사용하지 않고 파일을 사용하여 데이터를 관리한다.이런 방식은 실현하기에 더욱 간단할 수 있다. 수요가 크지 않고 데이터의 양이 크지 않으며 단일 구성 요소만 사용하는 상황에서 이런 방식을 충분히 사용할 수 있다.그러나 그 단점도 뚜렷하다. 바로 구성 요소 사이에서 전달하는 것이 매우 번거롭고 심지어는 구성 요소 사이에서 공유할 수 없다는 것이다.공유를 위해서는 데이터 층을 추상적으로 하고 그 어떠한 활동에도 독립시켜 서로 다른 구성 요소가 데이터에 대해 읽기와 쓰기를 만족시켜야 한다. 그러나 이렇게 하면 하나의 ContentProvider를 실현하는 것과 차이가 없고 차라리 하나의 ContentProvider를 실현하는 것이 편리하다.따라서 규칙은 만약에 어떤 데이터가 하나의 Activity에서만 사용된다면 ContentProvider를 만들 필요가 없고 파일을 직접 사용하거나 Database를 직접 조작하면 목적을 달성할 수 있다는 것이다.그러나 다른 구성 요소와 데이터를 공유하고 전달해야 한다면 ContentProvider를 사용해야 한다.또한 ContentProvider가 있으면 다른 응용 프로그램과 상호작용을 하고 데이터를 다른 응용 프로그램의 구성 요소에 전달할 수 있다.SQLiteOpenHelper를 사용할 때 반드시 스레드 동기화 문제를 주의하고 모든 SQLiteDatabase 방법(예를 들어 execSQL)의 스레드 안전성을 확보해야 한다. 그렇지 않으면 매우 보기 드문 이상을 일으킬 수 있다.SQLiteStatement에서 보고한 NPE(Null Pointer Exception)를 만났습니다. 여러 개의 스레드가 같은 SQLite OpenHelper를 조작하고 동기화되지 않았기 때문입니다.

좋은 웹페이지 즐겨찾기