Understanding Android ContentProvider 자세히 보기

6063 단어
1.ContentProvider가 무엇인지, 즉 내용 제공자인지, 모든 데이터에 대한 접근을 추상적으로 하고 데이터 접근에 통일된 인터페이스를 제공한다.다음과 같은 이점이 있습니다.
a. 데이터에 대한 추상적이고 모든 구성 요소에 통일된 접근 데이터를 제공하여 구성 요소가 구체적인 데이터의 표현 형식에 관심을 가지지 않도록 한다(파일 or 데이터베이스).데이터도 사용자의 방문 문제에 관여하지 않고 자신의 관리에만 관심을 기울일 수 있다.이렇게 하면 아주 좋은 포장에 도달할 수 있다.b. 인터페이스가 더욱 편리하고 구성 요소 간 전송 데이터인 Content Provider의 접근 표지는 Uri이고 통일된 Content Resolver를 통해 접근할 수 있으며 Content Resolver와 Uri와 Application의 상하문인 Context와 구성 요소 간의 정보 전송 도구인 Intent는 틈새 없이 연결된다.이것은 구성 요소 간에 데이터 공유와 데이터 전달을 더욱 편리하고 신속하게 한다.
따라서 ContentProvider의 가장 큰 장점은 서로 다른 구성 요소 사이에서 쉽게 공유할 수 있다는 것이다.따라서 만약에 응용 프로그램에서 사용하는 데이터가 서로 다른 구성 요소 사이에서 공유되어야 한다면 콘텐츠 Provider를 실현하는 것이 최선의 방안일 것이다.
2. 실현 방식인ContentProvider의 실현 방식은 매우 간단하다. 수요에 따라 일부 인터페이스를 실현하면 된다. 예를 들어query, insert, delete, update,openFile 등이다.그러나 구체적인 데이터의 표현 형식은 서로 다른 목적에 따라 자유롭게 선택하는 것이다. 예를 들어 구조화된 데이터에 대해 SQLiteDatabase를 선택하는 것이 비교적 좋은 방안일 수 있고 대량의 바이트 흐름이 파일을 우선적으로 선택할 수 있다.
주의해야 할 것은 안드로이드의 90퍼센트의 ContentProvider 내부는 모두 SQLiteDatabase로 구조화된 데이터를 저장하지만, 이것은 ContentProvider가 SQLiteDatabase에서만 데이터를 관리할 수 있다는 것을 의미하지는 않는다는 것이다.Content Provider는 인터페이스를 정의합니다. 정확한 데이터를 되돌려주기만 하면 되고, 구체적인 실현 방식은 자유롭게 선택할 수 있습니다.
예를 들어 Contacts의Content Provider는 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를 직접 조작하여 데이터 관리를 실현하거나 데이터베이스를 사용하지 않고 파일을 직접 사용하여 데이터를 관리한다.
이런 방식은 실현하기에 더욱 간단할 수 있다. 수요가 크지 않고 데이터량이 크지 않으며 단일 구성 요소만 사용하는 상황에서 이런 방식을 충분히 사용할 수 있다.그러나 구성 요소 간에 전달이 매우 번거롭고 심지어 구성 요소 간에 공유할 수 없다는 단점도 뚜렷하다.공유를 위해서는 데이터 층을 추상화하여 그 어떤Activity와도 독립시켜 서로 다른 구성 요소를 만족시키고 데이터를 읽기와 쓰기를 해야 한다. 그러나 이렇게 하면 하나의 콘텐츠 Provider를 실현하는 것과 다를 것이 없고 차라리 하나의 콘텐츠 Provider를 실현하는 것이 편리하다.
따라서 일부 데이터가 한 Activity에서만 사용된다면 콘텐츠 Provider를 만들 필요가 없고 파일을 직접 사용하거나 Database를 직접 조작하면 목적을 달성할 수 있다.그러나 다른 구성 요소와 데이터를 공유하고 전달해야 할 경우 ContentProvider를 사용해야 합니다.
또한ContentProvider가 있으면 다른 응용 프로그램과 상호작용을 하여 데이터를 다른 응용 프로그램의 구성 요소에 전달할 수 있다.
SQLiteOpenHelper를 사용할 때 반드시 스레드 동기화 문제에 주의하고 모든 SQLiteDatabase 방법(예를 들어execSQL)의 스레드 안전성을 확보해야 한다. 그렇지 않으면 매우 보기 드문 이상을 일으킬 수 있다.SQLite Statement에서 보고한 NPE(Null Pointer Exception)를 만난 적이 있다. 여러 개의 라인이 같은 SQLite OpenHelper를 조작하고 동기화되지 않았기 때문이다.

좋은 웹페이지 즐겨찾기