본문 바로가기

Android/ContentProvider

Android ContentProvider example

안드로이드에서 ContentProvider를 이용하여 친구의 이름과 전화번호를 제공하는 예

참고: SQLite DB Browser 소개

AndroidManifest.xml (ContentProvider를 선언하여 authorities속성에 해당하는 값으로 호출되도록 함)

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.sqlliteapp" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name=".FriendsProvider" android:authorities="com.example.sqlliteapp.friendinfo"> </provider> </application> </manifest>



main.xml (메인화면의 레이아웃)

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="이름" /> <EditText android:id="@+id/editText1" android:layout_width="100dp" android:layout_height="wrap_content" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="전화" /> <EditText android:id="@+id/editText2" android:layout_width="100dp" android:layout_height="wrap_content" /> <Button android:id="@+id/btnSave" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="btnSave" android:text="저장" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="이름" /> <EditText android:id="@+id/editText3" android:layout_width="100dp" android:layout_height="wrap_content" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="전화" /> <EditText android:id="@+id/editText4" android:layout_width="100dp" android:layout_height="wrap_content" /> <Button android:id="@+id/btnUpdate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="btnUpdate" android:text="변경" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="번호" /> <EditText android:id="@+id/editText5" android:layout_width="100dp" android:layout_height="wrap_content" /> <Button android:id="@+id/btnDelete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="btnDelete" android:layout_weight="2" android:text="삭제" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="이름" /> <EditText android:id="@+id/editText6" android:layout_width="100dp" android:layout_height="wrap_content" /> <Button android:id="@+id/btnSearch" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="btnSearch" android:layout_weight="2" android:text="검색" /> </LinearLayout> <ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout>


row.xml (ListView의 한 행을 구성하는 레이아웃 파일)

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/mem_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="TextView" /> <TextView android:id="@+id/mem_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="TextView" /> <TextView android:id="@+id/mem_phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="TextView" /> </LinearLayout>


DatabaseHelper.java

package com.example.sqlliteapp; import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class DatabaseHelper extends SQLiteOpenHelper{ private static String DB_NAME = "MyFirstDB.db"; private static final String TABLE_CREATE = "create table MyTable (_id integer primary key autoincrement, " +"name text not null, phone text not null);"; public DatabaseHelper(Context context) { super(context, DB_NAME, null, 5); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(TABLE_CREATE); ContentValues initialValues = new ContentValues(); initialValues.put("name", "Scott"); initialValues.put("phone", "01036547895"); db.insert("MyTable", null, initialValues); initialValues = new ContentValues(); initialValues.put("name", "Smith"); initialValues.put("phone", "01056482658"); db.insert("MyTable", null, initialValues); // 주의: 여기서 db.close() 하면 안됨 } @Override public void onOpen(SQLiteDatabase db) { super.onOpen(db); Log.i("DB Open", "DB Open OK!"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w("DB Upgrade", "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS MyTable"); onCreate(db); } }


MainActivity.java (위에 선언된 DatabaseHelper를 이용하여 데이터베이스에 CRUD 작업을 수행하는 프로그램)

이 프로그램에서 생성하고 구축한 데이터베이스는 ContentProvider 를 이용하여 외부 프로그램에서 접근하게 된다.

package com.example.sqlliteapp; import android.os.Bundle; import android.app.Activity; import android.app.ListActivity; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.EditText; import android.widget.SimpleCursorAdapter; import android.widget.Toast; import android.support.v4.app.NavUtils; public class MainActivity extends ListActivity { EditText et1,et2,et3,et4,et5,et6; // 편의상 사용할 테이블 이름과 컬럼명을 상수로 선언함 private static final String _ID = "_id"; private static final String NAME = "name"; private static final String PHONE = "phone"; private static final String TABLE_NAME = "MyTable"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); et1 = (EditText) findViewById(R.id.editText1); et2 = (EditText) findViewById(R.id.editText2); et3 = (EditText) findViewById(R.id.editText3); et4 = (EditText) findViewById(R.id.editText4); et5 = (EditText) findViewById(R.id.editText5); et6 = (EditText) findViewById(R.id.editText6); } /* 테이블의 전체 레코드를 가져와서 ListView에 출력한다 */ public void btnShowAll(View v){ DatabaseHelper helper = new DatabaseHelper(this); SQLiteDatabase db = helper.getReadableDatabase(); Cursor cursor = db.query(TABLE_NAME, new String[]{_ID, NAME, PHONE}, null, null, null, null, null); SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.row, cursor, new String[]{_ID, NAME, PHONE}, new int[]{R.id.mem_id, R.id.mem_name, R.id.mem_phone}); setListAdapter(adapter); db.close(); } /* 한행을 테이블에 저장한다 */ public void btnSave(View v){ String name = et1.getText().toString(); String phone = et2.getText().toString(); DatabaseHelper helper = new DatabaseHelper(this); SQLiteDatabase db = helper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(NAME, name); values.put(PHONE, phone); int rows = (int)db.insert(TABLE_NAME, null, values); db.close(); String msg = ""; if(rows>0) msg = "저장 성공"; else msg = "저장실패"; Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); } /* 이름을 이용하여 한 행을 검색한다 */ public void btnSearch(View v){ String name = et6.getText().toString(); DatabaseHelper helper = new DatabaseHelper(this); SQLiteDatabase db = helper.getReadableDatabase(); Cursor cursor = db.query(TABLE_NAME, new String[]{_ID, NAME, PHONE}, "name=?", new String[]{name}, null, null, null); SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.row, cursor, new String[]{_ID, NAME, PHONE}, new int[]{R.id.mem_id, R.id.mem_name, R.id.mem_phone}); setListAdapter(adapter); db.close(); }

/* 입력된 내용으로 테이블의 한행을 갱신한다 */ public void btnUpdate(View v){ String name = et3.getText().toString(); String phone = et4.getText().toString(); DatabaseHelper helper = new DatabaseHelper(this); SQLiteDatabase db = helper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(PHONE, phone); int rows = (int)db.update(TABLE_NAME, values, "name=?", new String[]{name}); String msg = ""; if(rows>0) msg = "변경 성공"; else msg = "변경실패"; Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); db.close(); } /* 번호로 한행을 검색하여 삭제한다 */ public void btnDelete(View v){ String num = et5.getText().toString(); DatabaseHelper helper = new DatabaseHelper(this); SQLiteDatabase db = helper.getWritableDatabase(); int rows = (int)db.delete(TABLE_NAME, "_id=?", new String[]{num}); String msg = ""; if(rows>0) msg = "삭제 성공"; else msg = "삭제 실패"; Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); db.close(); } }


FriendsProvider.java(위에서 생성한 데이터베이스를 다른 프로그램에서 접근할 수 있도록 하는 ContentProvider 콤포넌트)

package com.example.sqlliteapp; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; import android.util.Log; public class FriendsProvider extends ContentProvider { private static final String AUTH = "com.example.sqlliteapp.friendinfo"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTH + "/Friends"); public static final String _ID = "_id"; public static final String NAME = "name"; public static final String PHONE = "phone"; private static final int ONE_FRIEND = 1; private static final int ALL_FRIENDS = 2;

/* 클라이언트가 요청한 Uri의 구성에 따라서 전체 레코드 선택인지 단일 행 선택인지를 정의함 * 이 ContentProvider를 사용할 클라이언트 프로그램은 반드시 아래의 정의에 따라서 Uri를 사용해야 한다 */

private static final UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTH, "Friends", ALL_FRIENDS); // 전체 레코드 선택으로 판단함 uriMatcher.addURI(AUTH, "Friends/#", ONE_FRIEND);// 단일 행 선택으로 판단함, #은 ID 숫자로 간주 } private static SQLiteDatabase db; private static final String TABLE_NAME = "MyTable"; @Override public boolean onCreate() { Context context = getContext(); DatabaseHelper dbHelper = new DatabaseHelper(context); db = dbHelper.getWritableDatabase(); return (db != null); } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case ALL_FRIENDS: return "vnd.android.cursor.dir/" + AUTH; case ONE_FRIEND: return "vnd.android.cursor.item/" + AUTH; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } } @Override public Uri insert(Uri uri, ContentValues values) { Log.d("추가 시작", "추가 시작"); long rowID = db.insert(TABLE_NAME, "", values); if (rowID > 0) { Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID); getContext().getContentResolver().notifyChange(_uri, null); Log.d("추가완료", "추가완료"); return _uri; } throw new SQLException("Failed to insert row into " + uri); } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.d("커리 실행", "커리 실행됨"); SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder(); sqlBuilder.setTables(TABLE_NAME); if (uriMatcher.match(uri) == ONE_FRIEND) { sqlBuilder.appendWhere( String.format("%s = %s", _ID, uri.getPathSegments().get(1)) ); } if (TextUtils.isEmpty(sortOrder)) { sortOrder = _ID + " DESC"; } Cursor c = sqlBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder); c.setNotificationUri(getContext().getContentResolver(), uri); Log.d("커리 종료", "커리 종료"); return c; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Log.d("업데이트", "업데이트 시작"); int count = 0; switch (uriMatcher.match(uri)) { case ALL_FRIENDS: count = db.update(TABLE_NAME, values, selection, selectionArgs); break; case ONE_FRIEND: String whereClause = String.format("%s = %s %s", _ID, uri.getPathSegments().get(1), (TextUtils.isEmpty(selection) ? "" : " AND (" + selection + ")") ); count = db.update(TABLE_NAME, values, whereClause, selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); Log.d("업데이트 완료", "업데이트 완료"); return count; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { Log.d("삭제 시작", "삭제 시작"); int count = 0; switch (uriMatcher.match(uri)) { case ALL_FRIENDS: count = db.delete(TABLE_NAME, selection, selectionArgs); break; case ONE_FRIEND: String whereClause = String.format("%s = %s %s", _ID, uri.getPathSegments().get(1), (TextUtils.isEmpty(selection) ? "" : " AND (" + selection + ")") ); count = db.delete(TABLE_NAME, whereClause, selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); Log.d("삭제 완료", "삭제 완료"); return count; } }



위에서 정의한 ContentProvider를 사용하여 다른 애플리케이션에서 데이터베이스에 접근하는 예

아래의 프로그램을 위의 프로그램과 별도로 외부에서 실행하려면 FriendsProvider 클래스에 직접 접근할 수는 없기 때문에 이 페이지의 하단과 같은 약간의 변경이 필요할 것이며 약간 변경하여 외부의 프로그램으로 실행해도 결과는 동일하게 출력되는 것을 확인할 수 있다.

package com.example.sqlliteapp; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.util.Log; public class ClientActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ContentResolver cr = getContentResolver();

// 한 행을 읽어오는 예 (SELECT one) Uri uri = Uri.withAppendedPath(FriendsProvider.CONTENT_URI, "1"); // id가 1인 행 Cursor cursor2 = cr.query(uri, null, null, null, null); if(cursor2.moveToNext()) { String record = String.format("%d, %s, %s\n", cursor2.getInt(0), cursor2.getString(1), cursor2.getString(2)); Log.d("한행을 가져옴", record); } // 테이블에 한 행을 저장하는 예 (INSERT) final ContentValues values = new ContentValues(); values.clear(); values.put(FriendsProvider.NAME, "홍범도"); values.put(FriendsProvider.PHONE, "634-4576-865"); getContentResolver().insert(FriendsProvider.CONTENT_URI, values); // 선택된 행의 특정 컬럼 값을 변경하는 예 (UPDATE multi rows) values.clear(); values.put("name", "홍길동"); cr.update(FriendsProvider.CONTENT_URI, values, "name=?", new String[]{"홍범도"}); // 테이블의 모든 내용을 가져오는 예 (SELECT all) Cursor cursor = cr.query(FriendsProvider.CONTENT_URI, null, null, null, null); int id_index = cursor.getColumnIndex(FriendsProvider._ID); int name_index = cursor.getColumnIndex(FriendsProvider.NAME); int phone_index = cursor.getColumnIndex(FriendsProvider.PHONE); while(cursor.moveToNext()){ int id = cursor.getInt(id_index); String name = cursor.getString(name_index); String phone = cursor.getString(phone_index); String record = String.format("%d, %s, %s ", id, name, phone); Log.d("커서 출력", record); } // 이름이 홍길동인 모든 행을 선택하여 삭제함 (DELETE multi rows) cr.delete(FriendsProvider.CONTENT_URI, "name=?", new String[]{"홍길동"}); // 삭제 되었는지 확인함 (SELECT all) cursor = cr.query(FriendsProvider.CONTENT_URI, null, null, null, "_id ASC"); while(cursor.moveToNext()){ int id = cursor.getInt(id_index); String name = cursor.getString(name_index); String phone = cursor.getString(phone_index); String record = String.format("%d, %s, %s ", id, name, phone); Log.d("커서 출력", record); }

// _id가 1인 한 행의 phone 컬럼 값을 01011112222로 변경하는 예(UPDATE one row) values.clear(); values.put("phone", "01011112222"); uri = Uri.withAppendedPath(FriendsProvider.CONTENT_URI, "1"); cr.update(uri, values, null, null); // 데이터가 변경되었는지 확인함 (SELECT all) cursor = cr.query(FriendsProvider.CONTENT_URI, null, null, null, "_id ASC"); while(cursor.moveToNext()){ int id = cursor.getInt(id_index); String name = cursor.getString(name_index); String phone = cursor.getString(phone_index); String record = String.format("%d, %s, %s ", id, name, phone); Log.d("커서 출력", record); } } }



외부의 프로젝트에서 FriendsProvider 를 사용하는 예

package kr.co.micropilot.android.provider.client.test; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.util.Log; public class ClientForProviderActivity extends Activity { private static final String AUTH = "com.example.sqlliteapp.friendinfo"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTH + "/Friends"); public static final String _ID = "_id"; public static final String NAME = "name"; public static final String PHONE = "phone"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ContentResolver cr = getContentResolver(); // 한 행을 읽어오는 예 Uri uri = Uri.withAppendedPath(CONTENT_URI, "1"); Cursor cursor2 = cr.query(uri, null, null, null, null); if(cursor2.moveToNext()) { String record = String.format("%d, %s, %s\n", cursor2.getInt(0), cursor2.getString(1), cursor2.getString(2)); Log.d("한행을 가져옴", record); } // 테이블에 한 행을 저장하는 예 final ContentValues values = new ContentValues(); values.clear(); values.put(NAME, "홍범도"); values.put(PHONE, "634-4576-865"); getContentResolver().insert(CONTENT_URI, values); // 이름이 홍범도인 모든 행의 name 컬럼 값을 변경하는 예 values.clear(); values.put(NAME, "홍길동"); cr.update(CONTENT_URI, values, "name=?", new String[]{"홍범도"}); // 테이블의 모든 내용을 가져오는 예 Cursor cursor = cr.query(CONTENT_URI, null, null, null, null); int id_index = cursor.getColumnIndex(_ID); int name_index = cursor.getColumnIndex(NAME); int phone_index = cursor.getColumnIndex(PHONE); while(cursor.moveToNext()){ int id = cursor.getInt(id_index); String name = cursor.getString(name_index); String phone = cursor.getString(phone_index); String record = String.format("%d, %s, %s ", id, name, phone); Log.d("커서 출력", record); } // 이름이 홍길동인 모든 행을 선택하여 삭제함 cr.delete(CONTENT_URI, "name=?", new String[]{"홍길동"}); // 삭제 되었는지 확인함 cursor = cr.query(CONTENT_URI, null, null, null, "_id ASC"); while(cursor.moveToNext()){ int id = cursor.getInt(id_index); String name = cursor.getString(name_index); String phone = cursor.getString(phone_index); String record = String.format("%d, %s, %s ", id, name, phone); Log.d("커서 출력", record); } // _id가 1인 한 행의 phone 컬럼 값을 01011112222로 변경하는 예 values.clear(); values.put(PHONE, "01011112222"); uri = Uri.withAppendedPath(CONTENT_URI, "1"); cr.update(uri, values, null, null); // 데이터가 변경되었는지 확인함 cursor = cr.query(CONTENT_URI, null, null, null, "_id ASC"); while(cursor.moveToNext()){ int id = cursor.getInt(id_index); String name = cursor.getString(name_index); String phone = cursor.getString(phone_index); String record = String.format("%d, %s, %s ", id, name, phone); Log.d("커서 출력", record); } } }