Hun's Blog

[Android] 안드로이드 4대 컴포넌트 - Content Provider 본문

Android

[Android] 안드로이드 4대 컴포넌트 - Content Provider

jhk-im 2020. 3. 22. 00:04


Content Provider

 


컨텐트 프로바이더는 어플리케이션 사이에서 데이터를 공유하는 통로 역할을 한다. 각종 설정값이나 DB에 접근하게 해 주며 결과를 반환하는 브릿지 역할은 컨텐트 리졸버(Content Resolver)가 한다. 컨텐트 리졸버는 컨텐트 프로바이더의 주소를 통해 데이터에 접근하여 결과를 가져온다.

App -ContentUri-> ContentResolver --> ContentProvider -->DBClass -->SQLite


안드로이드 시스템에서는 각 어플리케이션마다 각각의 DB를 가질 수 있다. 그리고 어플리케이션 내부의 DB는 해당 어플리케이션만 접근이 가능하다. 다른 어플리케이션에게 DB가 전부 공개된다면 보안에 취약해 질 수 있기 때문이다. 하지만, 어플리케이션 DB 접근을 완전히 막는 것도 문제가 될 수 있다. 어플리케이션 사이에 데이터를 공유할 수 없게되기 때문이다. 카카오톡 프로필사진을 변경하고 싶은데 카카오톡이 나의 갤러리 어플리케이션 DB에 접근할 수 없다면 프로필 사진을 업로드 할 수 없다.

*안드로이드 시스템은 어플리케이션 내부 DB에 대해 해당 어플리케이션만 접근 가능한 보안성과 다른 어플리케이션에서도 DB에 접근할 수 있도록 유연함을 제공하기 위해 컨텐트 프로바이더 클래스를 제공하는 것이다.

다시 정리해보면
외부 앱이 특정 앱의 DB 정보가 필요한 경우, 외부 앱은 컨텐트 리졸브를 통해 Uri를 해당 앱에 보내고, 해당 앱의 컨텐트 프로바이더가 Uri를 해석하여 필요한 DB작업을 하게 된다. 이때, Uri를 전달받은 컨텐트 프로바이더는 기본적은 CRUD 연산을 모두 처리 가능하다. 컨텐트 프로바이더는 필요한 작업을 모두 수행하고, 결과값을 컨텐트 리졸브에 반환한다.

*컨텐트 프로바이더와 컨텐트 리졸브를 통해 앱과 앱 사이에 데이터베이스를 공유하면서 직접적인 접근을 차단할 수 있다.

*안드로이드는 android.provider 패키지에 콘텐트 프로바이더가 나열되어있다.
- 연락처
- 전화기록
- 오디오
- 비디오
...


Content Resolver 객체 

ContentResolver 객체는 getContentResolver()를 통해 얻을 수 있다. ContentResolver 객체의 메소드를 통해 데이터를 생성, 검색, 업데이트, 삭제 등을 할 수 있다. 마치 데이터베이스에서 query를 하듯 ContentResolver 객체의 query() 메소드가 컨텐트 프로바이더로 부터 데이터와 관련된 작업을 하게된다.

https://choidev-1.tistory.com/56
해당링크의 학생DB 예제를 통해 콘텐트 프로바인더를 알아 보도록 하겠다. 이외의 자세한 설명은 해당 블로그에 자세히 정리되어있다.


예제


SQLite에 특정 학생의 정보를 추가하고 수정하고 삭제하고 쿼리를 확인한다.
콘텐트 프로바이더 인터페이스를 활용해서 해당 기능들을 구현한 예제이다.

SQLite 
db 를 생성한 앱 내부에서만 접근 가능한 데이터 베이스이다.
때문에 외부의 앱에서 접근하기 위해서는 콘텐트 프로바이더가 필요하다.



매니페스트 파일 설정 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

해당 앱이 외부 저장소에 접근하여 읽고 수정할 수 있는 권한을 부여한다.
특정 정보를 저장하게 되면 오직 해당 앱에서만 그 데이터를 다룰 수 있다.

1
2
3
4
<provider
    android:name=".StudentProvider"
    android:authorities="com.jroomstudio.myapplication.StudentProvider"
    android:exported="true"></provider>
 
 

StudentProvider 클래스는 ContentProvider를 상속받으므로 외부에서 해당 클래스로 접근하면 데이터베이스에 접근 할 수 있게된다.
StudentProvider 클래스의 프로젝트상 저장위치가 Uri처럼 사용된다.

1. db 생성 클래스 - StudentDBManager.java

1
2
3
4
5
static final String DB_STUDENTS="Students.db";
static final String TABLE_STUDENT = "Students";
static final int DB_VERSION = 2;
Context context = null;
private static StudentDBManager dbManager = null;
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs


SQLiteOpenHelper를 상속받아 저장할 데이터베이스를 생성하는 클래스이다.
데이터베이스에 대한 자세한 내용은 다루지 않겠다.

2. db에 접근하는 프로바이더 클래스 - StudentProvider.java
public StudentDBManager dbManager = null;

위에서 생성한 데이터베이스와 외부 앱사이에서 데이터의 흐름을 관리하는 콘텐트 프로바이더 클래스이다.

데이터베이스 객체 생성

1
2
3
4
5
@Override
public boolean onCreate() {
    dbManager = StudentDBManager.getInstance(getContext());
    return true;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs


 

insertAll

1
2
3
4
@Override
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
    return dbManager.insertAll(values);
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs


query

1
2
3
4
5
@Override
public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {
    return dbManager.query(projection,selection,selectionArgs,null,null,sortOrder);
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs


delete

1
2
3
4
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs


insert

1
2
3
4
5
@Override
public Uri insert(Uri uri, ContentValues values) {
    long rowid = dbManager.insert(values);
    return null;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

update

1
2
3
4
5
@Override
public int update(Uri uri, ContentValues values, String selection,
                  String[] selectionArgs) {
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs


CRUD 의 기능이 구현되어있다.  이 프로바이더 클래스가 위에서 생성한 SQLite 데이터베이스와 연결되어 관리되기 때문에 외부앱은 해당 클래스를 찾아올 수 있는 Uri를 타고 들어와 해당 앱의 주인이 지정해 놓은 권한에 따라 데이터베이스에 접근하여 데이터를 다룰 수 있게 되는 것이다.

3. MainActivity.java  -- onClick 메소드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public void onClick(View view) {
    switch (view.getId()){
        case R.id.btn_insert: {
            ContentValues addRowValue = new ContentValues();
            addRowValue.put("number","200106504");
            addRowValue.put("name","홍길동");
            addRowValue.put("department","컴퓨터");
            addRowValue.put("age","18");
            addRowValue.put("grade",3);
            getContentResolver().insert(Uri.parse(PROVIDER_URI),addRowValue);
            editText.setText("recode add");
            break;
        }
        case R.id.btn_query:{
            String[] columns = new String[]{"_id","number","name","department","age","grade"};
            Cursor c = getContentResolver().query(
                    Uri.parse(PROVIDER_URI),
                    columns,null,null,null,null);
            if(c != null){
                editText.setText("");
                while (c.moveToNext()){
                    int id = c.getInt(0);
                    String number = c.getString(1);
                    String name = c.getString(2);
                    String department = c.getString(3);
                    String age = c.getString(4);
                    int grade = c.getInt(5);
                    editText.append(
                            "id : "+id+"\n"
                            +"number : "+number+"\n"
                            +"name : "+name+"\n"
                            +"department : "+department+"\n"
                            +"age : "+age+"\n"
                            +"grade : "+grade+"\n");
                }
                editText.append("\n Total : " + c.getCount());
                c.close();
            }
            break;
        }
        case R.id.btn_update:{
            ContentValues updateRowValue = new ContentValues();
            updateRowValue.put("name","고길동");
            int updateRecordCnt = getContentResolver().update(
                    Uri.parse(PROVIDER_URI),
                    updateRowValue,
                    "number=200106504",null);
            editText.setText("갱신 : " + updateRecordCnt);
            break;
        }
        case R.id.btn_delete:{
            int deleteRecordCnt = getContentResolver().delete(
                    Uri.parse(PROVIDER_URI),null,null);
            editText.setText("삭제 : " + deleteRecordCnt);
            break;
        }
    }
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

 

데이터베이스 접근을 위해 getContentResolver() 가 반복적으로 사용되는 것을 기억해두자.



정리 -

대부분의 앱들은 필요에 따라 로컬 데이터베이스를 구현하여 디바이스 내부의 저장소를 활용한다. 사진 앱으로 사진을 찍고 갤러리라는 앱에 저장되는 것도 내부 저장소를 활용한 것이다. 디바이스에 설치된 수많은 앱들이 각각의 데이터베이스 메모리를 할당받아서 사용하는데 안드로이드에선 특정 앱에서 할당받아 사용하는 데이터베이스에 대해서 다른앱이 접근하지 못하도록 철저하게 관리한다. 

생각해보자. 식단관리 앱을 깔고 나의 신체정보와 식사기록들을 저장하여 사용하고 있었다. 어느날 뜬금없이 페이스북에서 "오늘 많이드셨네요? 친구들에게 알려서 반성좀하세요" 라는 알림메세지가 왔다. 상상도 할 수 없는일이다. 만약 안드로이드에서 앱의 로컬데이터베이스에 대하여 관대하다면 저런일이 일어 날 수 있게된다. 

하지만 너무 막아두는것도 문제가 되는것이 페이스북 프로필을 변경하려 갤러리앱으로 들어가려하는데 "접근불가 - 페이스북에서 직접 찍어서 올리세요" 라는 경고창이 뜬다고 생각해보자. 이 또한 말도 안되는 일이다. 

이러한 문제를 적절히 해결하는 컴포넌트가 콘탠트 프로바이더 이다. 

내가만든 앱에 데이터를 다른 앱에서도 접근해서 사용하도록 하고싶다.
1. 데이터 베이스를 만들어 데이터를 저장한다.
2. 콘탠트 프로바이더클래스를 생성한다.
3. 프로바이더 클래스로 찾아올 수 있도록 uri도 알려준다. 
4. 콘탠트 리졸브를 생성하여 crud 기능을 구현한다. 

A(APP)라는 사람이 큰 창고(데이터베이스)를 가지고 있는데 식량(데이터)을 많이 비축해 두었다.  몇 개의 식량은 마을주민( 다른 APP)들에게 나눠주고 싶다.  그냥 창고문을 개방하자니 값비싼 식량들이 많아서 불안하다. 그래서 관리인(리졸브) 을 고용하고 창구(프로바이더) 를 만들어서 필요한 주민(다른APP)이 찾아가도록 하였다. 
- 주민들은 창고에 직접 들어갈 수 없고 창구로 찾아가 관리 인에게 요청을한다. 
- 관리인은 창구에서 정해진 규칙에 따라서 물건을 가져다 준다.



참고
https://choidev-1.tistory.com/56
https://bitsoul.tistory.com/155
https://salix97.tistory.com/11
https://mainia.tistory.com/4924
https://developer.android.com/guide/topics/providers/content-provider-basics?hl=ko

 

(안드로이드) 콘텐트 프로바이더(ConentProvider)를 학생 DB 예제 (ContentProvider)

콘텐트 프로바이더(ContentProvider) 학생DB 예제 안녕하세요 초이 입니다~ 저번 게시물인 콘텐츠 제공자의 개념에 콘텐츠 제공자의 사용예제를 살펴 볼까합니다. 혹시 저번 게시물이 필요하신 분은 아래 링크를..

choidev-1.tistory.com

 

안드로이드 : 컨텐트 제공자 (Content Provider) 예제 - 연락처 데이터

안드로이드 : 컨텐트 제공자 (Content Provider) 예제 - 연락처 데이터 안드로이드 4대 컴포넌트 중 하나인 콘텐트 제공자 (Content Provider) 는 어플리케이션 내에서 사용할수 있는 데이터를 '공유'하기 위한 컴..

bitsoul.tistory.com

 

[Android] 안드로이드 - Content Provider (컨텐트 프로바이더)

안드로이드의 4대 컴포넌트 중에는 '컨텐트 프로바이더'라는 것이 있다. Content Provider content는 내용물, provider는 제공자라는 뜻이다. 좀 더 자세하게 단어의 뜻을 알아보자면, content : the things that..

salix97.tistory.com

 

안드로이드 개발 ContentProvider, ContentResolver 이용해서 연락처 가져오는 방법

안드로이드 개발 ContentProvider, ContentResolver 이용해서 연락처 가져오는 방법 환경: Android Studio 안드로이드 앱을 구성하는 네 가지의 기본 요소는 Activity, Service, Broadcast Receiver, Content Prov..

mainia.tistory.com

 

콘텐츠 제공자 기본 사항  |  Android 개발자  |  Android Developers

콘텐츠 제공자는 중앙 리포지토리로의 데이터 액세스를 관리합니다. 제공자는 Android 애플리케이션의 일부이며, 이는 흔히 데이터 작업을 위한 고유의 UI를 제공합니다. 그러나 콘텐츠 제공자는 기본적으로 다른 애플리케이션이 사용하도록 설계되었습니다. 이들은 제공자 클라이언트 객체를 사용하여 제공자에 액세스합니다. 제공자와 제공자 클라이언트가 결합되면 데이터에 하나의 일관적인 표준 인터페이스를 제공하여 이것이 프로세스 간 통신과 보안 데이터 액세스도 처리합

developer.android.com