Android - 내용 제공자 이해하기

유의선·2023년 10월 29일
0
post-thumbnail

내용 제공자한 앱에서 관리하는 데이터를 다른 앱에서도 접근할 수 있도록 해준다.

내용 제공자가 필요한 이유는 앱의 보안때문이다.
보통 앱은 자신의 프로세스와 권한 안에서만 데이터에 접근 할 수 있도록 되어있다. 하지만 가끔은 서로 다른 앱의 데이터에 접근해야 하는 경우도 있는데, 그때 내용 제공자를 사용하면 된다.

내용 제공자에서 공유할 수 있는 데이터는
· 데이터베이스
· 파일
· SharedPreferences
가 있는데, 이 중 데이터베이스에 접근하는 것이 가장 일반적이다.

내용 제공자도 앱의 구성요소이기 때문에 매니페스트 파일에 등록해야 사용할 수 있다.

내용 제공자를 이용해 다른 앱의 파일에 접근할 때는 반드시 허용된 통로로만 접근해야 하며,
이 통로에 접근하려면 콘텐트 리졸버(ContentResolver) 객체가 필요하다.


내용 제공자 만들고 사용해보기

activity_main.xml 파일에 화면 레아이웃을 만들었다.
화면에는 insert, query, update, delete 네 개의 버튼과
ScrollView 안에 TextView 하나를 배치하였다.

그 후 MainActivity.java에 버튼을 클릭했을 때 이벤트 처리를 수행할 수 있는 기본 코드를 입력하였다.

package org.techtown.provider;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);

        Button buttonInsert = findViewById(R.id.buttonInsert);
        buttonInsert.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        Button buttonQuery = findViewById(R.id.buttonQuery);
        buttonQuery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        Button buttonUpdate = findViewById(R.id.buttonUpdate);
        buttonUpdate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        Button buttonDelete = findViewById(R.id.buttonDelete);
        buttonDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    }

    public void println(String data){
        textView.append(data + '\n');
    }
}

이 기능들은 뒤에서 추가할 것이다.



DatabaseHelper 클래스를 만들었다.

package org.techtown.provider;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "person.db";
    private static final int DATABASE_VERSION = 1;

    public static final String TABLE_NAME = "person";
    public static final String PERSON_ID = "_id";
    public static final String PERSON_NAME = "name";
    public static final String PERSON_AGE = "age";
    public static final String PERSON_MOBILE = "mobile";

    public static final String[] ALL_COLUMNS = {PERSON_ID, PERSON_NAME, PERSON_AGE, PERSON_MOBILE};

    private static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
            PERSON_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
            PERSON_NAME + " TEXT, " +
            PERSON_AGE + " INTEGER, " +
            PERSON_MOBILE + " TEXT" +
            ")";


    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
    }
}

DatabaseHelper 클래스는 SQLiteOpenHelper 클래스를 상속받고 있으며,
person.db 데이터베이스를 사용하며, 그 안에 person 테이블을 만들도록 하였다.



내용 제공자 클래스인 PersonProvider 클래스를 만들었다.

package org.techtown.provider;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class PersonProvider extends ContentProvider {
    private static final String AUTHORITY = "org.techtown.provider";
    private static final String BASE_PATH = "person";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);

    private static final int PERSON = 1;
    private static final int PERSON_ID = 2;
    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        uriMatcher.addURI(AUTHORITY, BASE_PATH, PERSON);
        uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", PERSON_ID);
    }

    private SQLiteDatabase database;

    @Override
    public boolean onCreate() {
        DatabaseHelper helper = new DatabaseHelper(getContext());
        database = helper.getWritableDatabase();

        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        Cursor cursor;
        switch (uriMatcher.match(uri)){
            case PERSON:
                cursor = database.query(DatabaseHelper.TABLE_NAME,
                        DatabaseHelper.ALL_COLUMNS, selection,
                        null, null, null, DatabaseHelper.PERSON_NAME + " ASC");
                break;
            default:
                throw new IllegalArgumentException("알 수 없는 URI " + uri);
        }

        return cursor;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        switch (uriMatcher.match(uri)){
            case PERSON:
                return "vnd.android.cursor.dir/persons";
            default:
                throw new IllegalArgumentException("알 수 없는 URI " + uri);
        }
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        long id = database.insert(DatabaseHelper.TABLE_NAME, null, values);

        if(id > 0){
            Uri _uri = ContentUris.withAppendedId(CONTENT_URI, id);
            getContext().getContentResolver().notifyChange(_uri, null);
            return _uri;
        }

        throw new SQLException("추가 실패-> URI : " + uri);
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int count = 0;
        switch (uriMatcher.match(uri)){
            case PERSON:
                count = database.delete(DatabaseHelper.TABLE_NAME, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("알 수 없는 URI " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);

        return count;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        int count = 0;
        switch (uriMatcher.match(uri)){
            case PERSON:
                count = database.update(DatabaseHelper.TABLE_NAME, values, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("알 수 없는 URI " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);

        return count;
    }
}

내용 제공자를 만들기 위해서는 고유한 값을 가진 content URI를 만들어야 한다. 여기서는 앱의 패키지 이름과 person 테이블의 이름을 합쳐 URI를 정의하였다.

    private static final String AUTHORITY = "org.techtown.provider";
    private static final String BASE_PATH = "person";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);

content URI를 정의하는 형식은 다음과 같다.

content://org.techtown.provider/person/1

content:// -> 내용 제공자에 의해 제어되는 데이터라는 의미로 항상 content://로 시작함
Authority -> org.techtown.provider 부분을 가리키며 특정 내용 제공자를 구분하는 고유한 값
Base Path -> person 부분을 가리키며 요청할 데이터의 자료형을 결정함(여기서는 테이블 이름)
ID -> 맨 뒤의 1과 같은 숫자를 가리키며 요청할 데이터 레코드를 지정함


UriMatcher 객체는 URI를 매칭하는데 사용된다.

    private static final int PERSON = 1;
    private static final int PERSON_ID = 2;
    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        uriMatcher.addURI(AUTHORITY, BASE_PATH, PERSON);
        uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", PERSON_ID);
    }

match 메소드를 호출하면 UriMatcher에 addURI 메소드를 이용해 추가된 URI 중에서 실행 가능한 것이 있는지 확인해준다.

        switch (uriMatcher.match(uri)){
            case PERSON:
            
            ...

PersonProvider 클래스에는 insert, query, update, delete 메소드가 정의되있다.

그리고 이 안에서는 내용 제공자에 접근하기 위하여 ContentResolver 객체도 사용한다.
액티비티에서 getContentResolver 메소드를 호출하면 ContentResolver 객체를 반환한다.
이 객체에는 query, insert, update, delete 등의 메소드가 정의되어 있어 내용 제공자의 URI를 파라미터로 전달하면서 데이터를 조회, 추가, 수정, 삭제하는 일이 가능하다.

            getContext().getContentResolver().notifyChange(_uri, null);

notifyChange 메소드는 레코드가 추가, 수정, 삭제되었을 때 변경이 일어났음을 알려주는 역할을 한다.


내용 제공자를 이용해 값을 조회하고 싶다면 다음과 같은 query 메소드를 사용한다.

Cursor query (
				Uri uri,
                String[] projection,
                String selection,
                String[] selectionArgs,
                String sortOrder
              )

첫 번째 파라미터는 URI 값이고,

두 번째 파라미터는 어떤 칼럼들을 조회할 것인지를 지정한다. 만약 null 값을 전달하면 모든 칼럼을 조회한다.

세 번째 파라미터는 SQL에서 where 절에 들어갈 조건을 지정한다. 만약 null 값을 지정하면 where 절이 없다고 생각할 수 있다.

네 번째 파라미터는 세 번째 파라미터 값이 있을 경우 그 안에 들어갈 조건 값을 대체하기 위해 사용된다.

다섯번째 파리미터는 정렬 칼럼을 지정하며 null 값이면 정렬이 적용되지 않는다.

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        Cursor cursor;
        switch (uriMatcher.match(uri)){
            case PERSON:
                cursor = database.query(DatabaseHelper.TABLE_NAME,
                        DatabaseHelper.ALL_COLUMNS, selection,
                        null, null, null, DatabaseHelper.PERSON_NAME + " ASC");
                break;
            default:
                throw new IllegalArgumentException("알 수 없는 URI " + uri);
        }

        return cursor;
    }

내용 제공자를 이용해 값을 추가하고 싶다면 다음과 같은 insert 메소드를 사용한다.

Uri insert (
			Uri uri
            ContentValues values
			)

첫 번째 파라미터는 URI이고,

두 번째 파라미터는 저장할 칼럼명과 값들이 들어간 ContentValues 객체다.

결과 값으로는 새로 추가된 값의 Uri 정보가 반환된다.

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        long id = database.insert(DatabaseHelper.TABLE_NAME, null, values);

        if(id > 0){
            Uri _uri = ContentUris.withAppendedId(CONTENT_URI, id);
            getContext().getContentResolver().notifyChange(_uri, null);
            return uri;
        }

        throw new SQLException("추가 실패-> URI : " + uri);
    }

내용 제공자를 통해 값을 수정하고 싶다면 다음과 같은 update 메소드를 사용한다.

int update (
			Uri uri
            ContentValues values,
            String[] selection,
            String[] selectionArgs
			)

첫 번째 파라미터는 URI이고,

두 번째 파라미터는 저장할 칼럼명과 값들이 들어간 ContentValues 객체이다. 두 번째 파라미터 값에 null이 들어가면 안된다.

세 번째 파리미터는 SQL에서 where 절에 들어갈 조건을 지정한다. null을 지정하면 where 절이 없다고 생각할 수 있다.

네 번째 파라미터는 세 번째 파리미터 값이 있을 경우 그 안에 들어갈 조건 값을 대체하기 위해 사용된다.

결과 값으로는 영향을 받은 레코드의 개수가 반환된다.

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        int count = 0;
        switch (uriMatcher.match(uri)){
            case PERSON:
                count = database.update(DatabaseHelper.TABLE_NAME, values, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("알 수 없는 URI " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);

        return count;
    }

내용 제공자를 이용해 값을 삭제하고 싶다면 다음과 같은 delete 메소드를 사용한다.

int delete (
			Uri uri,
            String selection,
            String[] selectionArgs
            )

첫 번째 파라미터는 URI이고,

두 번째 파리미터는 SQL에서 where 절에 들어갈 조건을 지정한다. null을 지정하면 where 절이 없다고 생각할 수 있다.

세 번째 파라미터는 세 번째 파리미터 값이 있을 경우 그 안에 들어갈 조건 값을 대체하기 위해 사용된다.

결과 값으로는 영향 받은 레코드의 개수가 반환된다.

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int count = 0;
        switch (uriMatcher.match(uri)){
            case PERSON:
                count = database.delete(DatabaseHelper.TABLE_NAME, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("알 수 없는 URI " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);

        return count;
    }

getType 메소드는 MIME 타입이 무엇인지 알고 싶을 때 사용한다.

String getType (
				Uri uri
                )

Uri 객체가 파리미터로 전달되며 결과 값으로 MIME 타입이 반환된다. 만약 MIME 타입을 알 수 없다면 null값이 반환된다.

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        switch (uriMatcher.match(uri)){
            case PERSON:
                return "vnd.android.cursor.dir/persons";
            default:
                throw new IllegalArgumentException("알 수 없는 URI " + uri);
        }
    }


이제 MainActivity.java에 추가한 각각의 버튼을 눌렀을 때 insert, query, update, delete 메소드를 호출하도록 하고 그 결과를 화면에 표시하도록 코드를 추가해보겠다.

Insert 버튼

        Button buttonInsert = findViewById(R.id.buttonInsert);
        buttonInsert.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                insertPerson();
            }
        });

       ...

    public void insertPerson() {
        println("insertPerson 호출됨.");

        String uriString = "content://org.techtown.provider/person";
        Uri uri = new Uri.Builder().build().parse(uriString);

        Cursor cursor = getContentResolver().query(uri, null, null, null, null);

        String[] columns = cursor.getColumnNames();
        println("columns count -> " + columns.length);
        for(int i = 0; i < columns.length; i++){
            println("#" + i + " : " + columns[i]);
        }

        ContentValues values = new ContentValues();
        values.put("name", "John");
        values.put("age", 20);
        values.put("mobile", "010-1000-1000");

        uri = getContentResolver().insert(uri, values);
        println("insert 결과 -> " + uri.toString());
    }

첫 번째 버튼을 누르면 insertPerson 메소드가 호출된다.

        buttonInsert.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                insertPerson();
            }
        });

insertPerson 메소드 안에서는 먼저 Uri 객체를 만들고 ContentResolver 객체의 query 메소드를 호출하면서 Uri 객체를 파라미터로 전달한다.

문자열에서 Uri 객체를 만들 때는 new 연산자를 이용해 Uri.Builder 객체를 먼저 만든 후 build와 parse 메소드를 호출하면서 문자열을 파라미터로 전달한다.

ContentResolver 객체는 getContentResolver 메소드를 호출하면 참조할 수 있으며 query 메소드를 호출하면 그 결과 값으로 Cursor 객체가 반환된다.

        String uriString = "content://org.techtown.provider/person";
        Uri uri = new Uri.Builder().build().parse(uriString);

        Cursor cursor = getContentResolver().query(uri, null, null, null, null);

Cursor 객체를 이용해 결과 값을 조회할 수 있는데 결과 레코드에 들어가 있는 칼럼의 이름을 조회하고 싶다면 getColumnNames 메소드를 사용할 수 있다.

getColumnNames 메소드 이용해 알아낸 칼럼 이름을 화면에 출력하여 확인하였다.

        String[] columns = cursor.getColumnNames();
        println("columns count -> " + columns.length);
        for(int i = 0; i < columns.length; i++){
            println("#" + i + " : " + columns[i]);
        }

레코드를 추가할 때는 ContentValues 객체가 사용되는데 getColumnNames 메소드를 이용해 알아낸 칼럼 이름을 사용할 수도 있고 직접 칼럼 이름을 지정할 수도 있다.

여기에서는 칼럼 이름을 직접 지정하였다.

        ContentValues values = new ContentValues();
        values.put("name", "John");
        values.put("age", 20);
        values.put("mobile", "010-1000-1000");

ContentResolver의 insert 메소드를 호출하여 레코드를 추가할 때는 Uri 객체와 함께 ContentValues 객체를 파라미터로 전달한다.

        uri = getContentResolver().insert(uri, values);
        println("insert 결과 -> " + uri.toString());

Query 버튼

        Button buttonQuery = findViewById(R.id.buttonQuery);
        buttonQuery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                queryPerson();
            }
        });
        
        ...

    public void queryPerson() {
        try{
            String uriString = "content://org.techtown.provider/person";
            Uri uri = new Uri.Builder().build().parse(uriString);

            String[] columns = new String[] {"name", "age", "mobile"};
            Cursor cursor = getContentResolver().query(uri, columns, null, null, "name ASC");
            println("query 결과 : " + cursor.getCount());

            int index = 0;
            while (cursor.moveToNext()){
                //String name = cursor.getString(cursor.getColumnIndex(columns[0]));
                int name_index = cursor.getColumnIndex(columns[0]);
                String name = cursor.getString(name_index);
                //int age = cursor.getInt(cursor.getColumnIndex(columns[1]));
                int age_index = cursor.getColumnIndex(columns[1]);
                int age = cursor.getInt(age_index);
                //String mobile = cursor.getString(cursor.getColumnIndex(columns[2]));
                int mobile_index = cursor.getColumnIndex(columns[2]);
                String mobile = cursor.getString(mobile_index);

                println("#" + index + " -> " + name + ", " + age + ", " + mobile);
                index += 1;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

두 번째 버튼을 누르면 queryPerson 메소드가 호출된다.

        Button buttonQuery = findViewById(R.id.buttonQuery);
        buttonQuery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                queryPerson();
            }
        });

첫 번째 버튼을 눌러 레코드를 추가할 때와 유사하다.
먼저 Uri 객체를 만들고 CotentResolver 객체의 query 메소드를 호출하여 Uri 객체를 전달하여 Cursor 객체를 반환한다.

            String uriString = "content://org.techtown.provider/person";
            Uri uri = new Uri.Builder().build().parse(uriString);

            String[] columns = new String[] {"name", "age", "mobile"};
            Cursor cursor = getContentResolver().query(uri, columns, null, null, "name ASC");
            println("query 결과 : " + cursor.getCount());

query 메소드를 호출할 때 Uri 객체 외에도 columns도 전달하고 있는데 여기엔 조회할 칼럼의 이름이 들어가 있다. 이렇게 하면 지정한 칼럼만 조회할 수 있다.

Cursor 객체가 반환되면 각 칼럼 이름에 해당하는 칼럼 인덱스 값을 확인한 후 칼럼 값을 조회한다.

            int index = 0;
            while (cursor.moveToNext()){
                //String name = cursor.getString(cursor.getColumnIndex(columns[0]));
                int name_index = cursor.getColumnIndex(columns[0]);
                String name = cursor.getString(name_index);
                //int age = cursor.getInt(cursor.getColumnIndex(columns[1]));
                int age_index = cursor.getColumnIndex(columns[1]);
                int age = cursor.getInt(age_index);
                //String mobile = cursor.getString(cursor.getColumnIndex(columns[2]));
                int mobile_index = cursor.getColumnIndex(columns[2]);
                String mobile = cursor.getString(mobile_index);

                println("#" + index + " -> " + name + ", " + age + ", " + mobile);
                index += 1;
            }

Update 버튼

        Button buttonUpdate = findViewById(R.id.buttonUpdate);
        buttonUpdate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                updatePerson();
            }
        });

		...

    public void updatePerson() {
        String uriString = "content://org.techtown.provider/person";
        Uri uri = new Uri.Builder().build().parse(uriString);

        String selection = "mobile = ?";
        String[] selectionArgs = new String[] {"010-1000-1000"};
        ContentValues updateValue = new ContentValues();
        updateValue.put("mobile", "010-2000-2000");
        int count = getContentResolver().update(uri, updateValue, selection, selectionArgs);
        println("update 결과 : " + count);
    }

세 번째 버튼을 눌렀을 때는 updatePerson 메소드를 호출한다.

        Button buttonUpdate = findViewById(R.id.buttonUpdate);
        buttonUpdate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                updatePerson();
            }
        });

updatePerson 메소드 안에서는 mobile 칼럼의 값이 010-1000-1000인 레코드만 010-2000-2000으로 수정하도록 한다.

        String uriString = "content://org.techtown.provider/person";
        Uri uri = new Uri.Builder().build().parse(uriString);
        
        String selection = "mobile = ?";
        String[] selectionArgs = new String[] {"010-1000-1000"};
        ContentValues updateValue = new ContentValues();
        updateValue.put("mobile", "010-2000-2000");
        int count = getContentResolver().update(uri, updateValue, selection, selectionArgs);
        println("update 결과 : " + count);

수정할 때는 ContentResolver의 update 메소드를 호출하면서 Uri 객체, ContentValues 객체, where 조건, where 조건의 ? 기호를 대체할 값을 차례로 넣어준다.

where 조건의 문자열에 'mobile = ?' 를 넣었으므로 ? 기호가 하나 들어있다. 이 기호는 selectonArgs 배열 변수의 첫 번째 원소로 대체된다. 따라서 where 조건은 'mobile = 010-2000-2000'이 된다.


Delete 버튼

        Button buttonDelete = findViewById(R.id.buttonDelete);
        buttonDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                deletePerson();
            }
        });
    }

    ...

    public void deletePerson() {
        String uriString = "content://org.techtown.provider/person";
        Uri uri = new Uri.Builder().build().parse(uriString);

        String selection= "name = ?";
        String[] selectionArgs = new String[] {"John"};

        int count = getContentResolver().delete(uri, selection, selectionArgs);
        println("delete 결과 : " + count);
    }

네 번째 버튼을 눌렀을 때는 deletePerson 메소드를 호출한다.

        Button buttonDelete = findViewById(R.id.buttonDelete);
        buttonDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                deletePerson();
            }
        });

deletePerson 메소드 안에는 ContentResolver의 delete 메소드를 호출하면서 Uri 객체, where 조건, where 조건의 ? 기호를 대체할 값을 차례로 넣어준다.

    public void deletePerson() {
        String uriString = "content://org.techtown.provider/person";
        Uri uri = new Uri.Builder().build().parse(uriString);

        String selection= "name = ?";
        String[] selectionArgs = new String[] {"John"};

        int count = getContentResolver().delete(uri, selection, selectionArgs);
        println("delete 결과 : " + count);
    }

마지막으로 메니페스트에 내용 제공자를 등록한다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.techtown.provider">

    <permission android:name="org.techtown.provider.READ_DATABASE" android:protectionLevel="normal"/>
    <permission android:name="org.techtown.provider.WRITE_DATABASE" android:protectionLevel="normal"/>

    <application
       
                 ...
        
        <provider
            android:authorities="org.techtown.provider"
            android:name=".PersonProvider"
            android:exported="true"
            android:readPermission="org.techtown.provider.READ_DATABASE"
            android:writePermission="org.techtown.provider.WRITE_DATABASE"
            />
        
    </application>
</manifest>

permission 태그로 두 개의 권한을 새로 정의하였다.

    <permission android:name="org.techtown.provider.READ_DATABASE" android:protectionLevel="normal"/>
    <permission android:name="org.techtown.provider.WRITE_DATABASE" android:protectionLevel="normal"/>

name 속성 값으로는 "org.techtown.provider.READ_DATABASE"와 "org.techtown.provider.WRITE_DATABASE"가 설정되었으며, protectionLevel 속성 값으로는 "normal"이 설정되었다.

application 태그 안에는 provider 태그를 추가하고 authorities와 name, readPermission, writePermission 등의 속성을 추가하였다.

        <provider
            android:authorities="org.techtown.provider"
            android:name=".PersonProvider"
            android:exported="true"
            android:readPermission="org.techtown.provider.READ_DATABASE"
            android:writePermission="org.techtown.provider.WRITE_DATABASE"
            />

authorities 속성은 내용 제공자를 정의할 때 설정한 authorities 값과 동일하게 넣어준다.
name 속성으로는 내용 제공자 클래스인 PersonProvider 클래스를 설정한다.
readPermission 속성으로는 위에서 정의한 "org.techtown.provider.READ_DATABASE" 권한을 지정하고
writePermisson 속성으로는 위에서 정의한 "org.techtown.provider.WRITE_DATABASE" 권한을 지정한다.





여기서는 내용 제공자를 정의한 앱에서 데이터를 추가하고 조회했지만, 다른 앱에서도 내용 제공자를 이용하면 이 앱에서 관리하는 데이터를 조회할 수 있게 된다.

0개의 댓글