ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Android] ContentProvider ๊ตฌํ˜„ ๋ฐ ์‚ฌ์šฉ๋ฒ•
    Android 2022. 2. 5. 21:40
    ๋ฐ˜์‘ํ˜•

    ContentProvider๋ž€?

     ContentProvider๋Š” Activity, BroadcastReceiver, Service์™€ ๋™์ผํ•˜๊ฒŒ ์•ˆ๋“œ๋กœ์ด๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ์„ฑํ•˜๋Š” 4๋Œ€ ์š”์†Œ ์ค‘ ํ•˜๋‚˜๋กœ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผ์ด ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐ ์•ฑ์€ ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค๋กœ ์‹คํ–‰๋˜๋ฉฐ ์ž์‹ ์˜ ํ”„๋กœ์„ธ์Šค์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ์ž์‹ ๋งŒ ์ ‘๊ทผ๊ฐ€๋Šฅํ•˜๋„๋ก ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์‚ฌ์ง„์ฒฉ์— ์žˆ๋Š” ์‚ฌ์ง„๋“ค์„ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ์—ฐ๋ฝ์ฒ˜์— ์žˆ๋Š” ์—ฐ๋ฝ์ฒ˜ ์ •๋ณด๋“ค์„ ๊ฐ€์ ธ์™€์•ผํ•  ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ContentProvider๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‚˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ ‘๊ทผํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ํ†ต๋กœ ์—ญํ• ์„ ์ œ๊ณตํ•ด์ค๋‹ˆ๋‹ค.  ๊ทธ๋ฆฌ๊ณ  ์•ฑ์˜ ๋ณด์•ˆ์„ ์œ„ํ•ด์„œ ์ƒ๊ฒจ๋‚œ ContentProvider ์ปดํฌ๋„ŒํŠธ๋„ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ตฌ์„ฑ์š”์†Œ์ด๊ธฐ ๋•Œ๋ฌธ์— ์‹œ์Šคํ…œ์—์„œ ๊ด€๋ฆฌํ•˜๋ฉฐ, Manifest ํŒŒ์ผ์— ๋ช…์‹œํ•ด์ค˜์•ผ ์‹œ์Šคํ…œ์—์„œ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

     

     

    ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‘ ๊ฐ€์ง€ ๊ฒฝ์šฐ์—์„œ ์ฃผ๋กœ ContetProvider๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

     

    • ๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ContentProvider์— ์•ก์„ธ์Šค ํ•˜๊ธฐ ์œ„ํ•ด ์ฝ”๋“œ ๊ตฌํ˜„
    • ๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ContentProvider๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋ฐ์ดํ„ฐ ๊ณต์œ 

      ContentProvider๊ฐ€ ์ƒ์„ฑ๋œ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Context์— ์žˆ๋Š” ContentResolver ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ContentProvider์™€ ์„œ๋ฒ„-ํด๋ผ์ด์–ธํŠธ ๊ตฌ์กฐ๋กœ ํ†ต์‹ ์„ ์ฃผ๊ณ  ๋ฐ›์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ContentResolver ๊ฐ์ฒด๊ฐ€ ContentProvider์— ๋ฐ์ดํ„ฐ ์š”์ฒญ์„ ํ•˜๊ฒŒ ๋˜๊ณ  ContentProvider๋Š” ์š”์ฒญ๋œ ์ž‘์—…์„ ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜๋˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. 

     

    https://developer.android.com/guide/topics/providers/content-provider-basics?hl=ko

      ContentProvider์—์„œ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ํŒŒ์ผ, SharedPreference 3๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ContentProvider๋Š” CRUD ๋™์ž‘์„ ๊ธฐ๋ณธ์œผ๋กœ ํ•˜๊ณ  ์žˆ๊ธฐ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ฃผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์œ„์˜ ๊ทธ๋ฆผ์—์„œ๋Š” Activity๋‚˜ Fragment์—์„œ CursorLoader๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ContentResolver๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ContentProvider๋ฅผ ๊ฐ€์ ธ์˜ค์ง€๋งŒ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๊ตณ์ด ์ฟผ๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š๊ธฐ์— CursorLoader๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์‹ค์Šต์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์›๋ž˜๋Š” ๋‘ ๊ฐ€์ง€์˜ ์•ฑ์„ ๋งŒ๋“ค์–ด์„œ ํ•˜๋‚˜์˜ ์•ฑ์—์„œ Content Provider ์ƒ์„ฑํ•˜๊ณ  ๋‹ค๋ฅธ ์•ฑ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋„๋ก ํ•˜๋Š”๊ฒŒ ๋งž์ง€๋งŒ, ๊ตฌํ˜„์ƒ ํŽธ๋ฆฌํ•จ์„ ์œ„ํ•ด ํ•˜๋‚˜์˜ ์•ฑ์—์„œ ํ…Œ์ŠคํŠธ ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

     

     

     

    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌํ˜„

     ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ ์Šคํ‚ค๋งˆ๋ฅผ ์ž˜ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ Contract ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  BaseColumns ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ๊ธฐ๋ณธํ‚ค ํ•„๋“œ๋ฅผ ์ƒ์†ํ•˜๋„๋ก ๊ฐ€์ด๋“œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

    object PersonContract {
        object PersonEntry : BaseColumns{
            const val TABLE_NAME = "person"
            const val PERSON_NAME = "name"
            const val PERSON_AGE = "age"
            const val PERSON_MOBILE = "mobile"
        }
    }

     PersonContract ๊ฐ์ฒด์—์„œ PersonEntry๋ฅผ ์ •์˜ํ•˜์—ฌ ํ•ด๋‹น ํ…Œ์ด๋ธ”์— ํ•„์š”ํ•œ ํ…Œ์ด๋ธ” ๋ช…, ์ปฌ๋Ÿผ ๋ช…์„ ์ •์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. 

    class DatabaseHelper private constructor(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
    
        override fun onCreate(db: SQLiteDatabase?) {
            db?.execSQL(CREATE_TABLE)
        }
    
        override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
            TODO("Not yet implemented")
        }
    
        companion object{
            const val DATABASE_NAME = "person.db"
            const val DATABASE_VERSION = 1
            var instance : DatabaseHelper? = null
    
            val ALL_COLUMNS = arrayOf(BaseColumns._ID, PERSON_NAME, PERSON_AGE, PERSON_MOBILE)
            val CREATE_TABLE =
                "CREATE TABLE $TABLE_NAME (${BaseColumns._ID} INTEGER PRIMARY KEY AUTOINCREMENT, " +
                        "$PERSON_NAME TEXT, $PERSON_AGE INTEGER, $PERSON_MOBILE TEXT)"
    
            fun getInstance(context: Context): DatabaseHelper =
                if(instance == null) DatabaseHelper(context.applicationContext) else instance!!
        }
    }

    DatabaseHelper๋Š” SQLiteOpenHelper ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›๊ณ  ์žˆ์œผ๋ฉฐ, person.db ํŒŒ์ผ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. DatabaseHelper ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ onCreate ์ฝœ๋ฐฑ์ด ํ˜ธ์ถœ๋˜๊ณ  PersonContract ์— ์ •์˜ํ•œ person ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.  SQLiteOpenHelperํด๋ž˜์Šค๋Š” DB ์ƒ์„ฑ๊ณผ ๋ฒ„์ „๊ด€๋ฆฌ๋ฅผ ๋„์™€์ฃผ๋Š” ํ—ฌํผ ํด๋ž˜์Šค๋กœ ์ž์„ธํ•˜๊ฒŒ ์•Œ๊ณ  ์‹ถ์œผ๋ฉด ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

    https://math-coding.tistory.com/246

     

    [Android] SQLite, SQLiteOpenHelper, Local DB ์ดํ•ด

    SQLite๋ž€? Android ๊ฐœ๋ฐœ์„ ํ•˜๋ฉด์„œ ์•ฑ์„ ์‚ฌ์šฉํ•˜๊ณ  ์ข…๋ฃŒํ•˜๋”๋ผ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ์ €์žฅ๋˜์–ด์•ผํ•  ํ•„์š”์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด SharedPreference๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๊ฒ ์ง€๋งŒ, key์™€ value์˜

    math-coding.tistory.com

     

     

    ContentProvider ํด๋ž˜์Šค ๊ตฌํ˜„

     PersonProvider๋ผ๋Š” ์ด๋ฆ„์˜ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ContentProvider ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. 

    class PersonProvider : ContentProvider() {
        private lateinit var database : SQLiteDatabase
    
        override fun onCreate(): Boolean {
            if(context == null) return false
            database = DatabaseHelper.getInstance(context!!).writableDatabase
    
            return true
        }
    
        override fun query(
            uri: Uri,
            projcetion: Array<out String>?,
            selection: String?,
            selctionArgs: Array<out String>?,
            sortOrder: String?
        ): Cursor? {
            var cursor: Cursor? = null
            when(uriMatcher.match(uri)){
                PERSONS -> {
                    cursor = database.query(TABLE_NAME, projcetion, selection, selctionArgs, null, null, sortOrder)
                }
                else -> throw IllegalArgumentException("์•Œ ์ˆ˜ ์—†๋Š” URI : $uri")
            }
    
            cursor.setNotificationUri(context?.contentResolver, uri)
            return cursor
        }
    
        override fun getType(uri: Uri): String? {
            when(uriMatcher.match(uri)){
                PERSONS -> return "vnd.android.cursor.dir/persons"
                else -> throw IllegalArgumentException("์•Œ ์ˆ˜ ์—†๋Š” URI : $uri")
            }
        }
    
        override fun insert(uri: Uri, values: ContentValues?): Uri? {
            val id = database.insert(TABLE_NAME, null, values)
    
            if(id > 0){
                val uri = ContentUris.withAppendedId(CONTENT_URI, id)
                context?.contentResolver?.notifyChange(uri, null)
                return uri
            }
    
            throw SQLException("์ถ”๊ฐ€ ์‹คํŒจ URI : $uri")
        }
    
        override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
            var count = 0
            when(uriMatcher.match(uri)){
                PERSONS -> count = database.delete(TABLE_NAME, selection, selectionArgs)
                else -> throw IllegalArgumentException("์•Œ ์ˆ˜ ์—†๋Š” URI : $uri")
            }
            context?.contentResolver?.notifyChange(uri, null)
            return count
        }
    
        override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int {
            var count = 0
            when(uriMatcher.match(uri)){
                PERSONS -> count = database.update(TABLE_NAME, values, selection, selectionArgs)
                else -> throw IllegalArgumentException("์•Œ ์ˆ˜ ์—†๋Š” URI : $uri")
            }
            context?.contentResolver?.notifyChange(uri, null)
            return count
        }
    
        companion object{
            const val AUTHORITY = "com.example.contentprovider"
            const val BASE_PATH = "person"
            val CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH)
    
            const val PERSONS = 1
            const val PERSON_ID = 2
    
            val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
                addURI(AUTHORITY, BASE_PATH, PERSONS)
                addURI(AUTHORITY, BASE_PATH + "/#", PERSON_ID)
            }
        }
    }

     ์ถ”์ƒ ํด๋ž˜์Šค์ธ ContentProvider๋Š” onCreate, query, getType, insert, delete, update 6๊ฐ€์ง€ ํ•„์ˆ˜ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ•์ œ์ ์œผ๋กœ ๊ตฌํ˜„ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. onCreate ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ์™ธํ•˜๊ณ ๋Š” ContentResolver์— ๋™์ผํ•œ ๋ฉ”์†Œ๋“œ ๋ช…์ด ์ •์˜๋˜์–ด ์žˆ์œผ๋ฉฐ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ContentProvider์™€ ํ†ต์‹ ํ•˜์—ฌ ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.  ์ „์ฒด ์ฝ”๋“œ๋ฅผ ํ•œ ๋ฒˆ์— ๋ณด๋ฉด ์–ด๋ ค์šฐ๋‹ˆ ์ชผ๊ฐœ์„œ ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ด…์‹œ๋‹ค.

     

        companion object{
            const val AUTHORITY = "com.example.contentprovider"
            const val BASE_PATH = "person"
            val CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH)
    
            const val PERSONS = 1
            const val PERSON_ID = 2
    
            val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
                addURI(AUTHORITY, BASE_PATH, PERSONS)
                addURI(AUTHORITY, BASE_PATH + "/#", PERSON_ID)
            }
        }

      Content Provider๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ณ ์œ ํ•œ ๊ฐ’์„ ๊ฐ€์ง„ Content URI๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Content URI๋Š” Provider์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์‹๋ณ„ํ•˜๋Š” URI๋กœ Provider์˜ ๊ถŒํ•œ๊ณผ ํ…Œ์ด๋ธ” ๋˜๋Š” ํŒŒ์ผ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ด๋ฆ„์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ID ๋ถ€๋ถ„์€ ํ…Œ์ด๋ธ” ๋‚ด ๊ฐœ๋ณ„์ ์ธ ํ–‰์„ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.  ์ด Content URI๊ฐ€ ContentProvider์˜ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ์— ํ•„์ˆ˜ ์ธ์ž๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์•ก์„ธ์Šคํ•  ํ…Œ์ด๋ธ”, ํ–‰ ๋˜๋Š” ํŒŒ์ผ์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

     

    • content://com.example.contentprovider/person/1
    • content:// : ContentProvider์— ์ œ์–ด๋˜๋Š” ๋ฐ์ดํ„ฐ๋ผ๋Š” ์˜๋ฏธ๋กœ ํ•ญ์ƒ content://๋กœ ์‹œ์ž‘๋จ
    • Authority : com.example.contentprovider๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฉฐ, ContentProvider๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ณ ์œ ์˜ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ๋จ
    • Base Path : person์„ ๊ฐ€๋ฆฌํ‚ค๊ณ  ํ…Œ์ด๋ธ” ๋˜๋Š” ํŒŒ์ผ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ด๋ฆ„
    • ID : ๋งˆ์ง€๋ง‰ ์ˆซ์ž๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฉฐ ํ…Œ์ด๋ธ” ๋‚ด ํ–‰(๋ ˆ์ฝ”๋“œ)

     

       Content URI ํ˜•์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์œผ๋ฉฐ ์œ„์˜ ์˜ˆ์‹œ ์ฝ”๋“œ์—์„œ๋Š” Authority๋Š” ํŒจํ‚ค์ง€๋ช…์œผ๋กœ ์ง€์ •ํ•˜์˜€๊ณ , Base Path๋Š” PersonContract์— ์ •์˜ํ•œ ํ…Œ์ด๋ธ” ๋ช…๊ณผ ๋™์ผํ•˜๊ฒŒ person์œผ๋กœ ์ง€์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ ์™ธ์— UriMatcher ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๊ณ  addURI ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด URI๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์—ฌ์ง‘๋‹ˆ๋‹ค. UriMatcher ํด๋ž˜์Šค๋Š” URI๋ฅผ ๋งค์นญํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํด๋ž˜์Šค๋กœ ํŠน์ • URI๊ฐ€ addURI ๋ฉ”์†Œ๋“œ๋กœ ๋“ฑ๋ก๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. addURI ๋ฉ”์†Œ๋“œ์˜ ์ธ์ž๋กœ Authority, Base Path, Code๋ฅผ ํ•„์š”๋กœ ํ•˜๋ฉฐ match ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๋งค์นญ๋œ Authority์™€ Base Path๋กœ ๊ตฌ์„ฑ๋œ URI๊ฐ€ ์ผ์น˜ํ•œ๋‹ค๋ฉด Code๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด -1์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

     

        override fun query(
            uri: Uri,
            projcetion: Array<out String>?,
            selection: String?,
            selctionArgs: Array<out String>?,
            sortOrder: String?
        ): Cursor? {
            var cursor: Cursor? = null
            when(uriMatcher.match(uri)){
                PERSONS -> {
                    cursor = database.query(TABLE_NAME, projcetion, selection, selctionArgs, null, null, sortOrder)
                }
                else -> throw IllegalArgumentException("์•Œ ์ˆ˜ ์—†๋Š” URI : $uri")
            }
    
            cursor.setNotificationUri(context?.contentResolver, uri)
            return cursor
        }

      query ๋ฉ”์†Œ๋“œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด์ „์— uriMatcher์— ๋“ฑ๋กํ•œ URI์™€ ์ผ์น˜ํ•œ URI๋ผ๋ฉด SQLiteDatabase query ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๊ณ  ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด IllegalArgumentException์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ฐ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” SQLiteDatabase์˜ query์™€ ๊ฐ™์€ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋“ค์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ ์„ค๋ช…์€ ํ•˜์ง€ ์•Š๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. cursor.setNotificationUri ๋ฉ”์†Œ๋“œ๋Š” ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์ „์— ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ๋งŒ์•ฝ ๋น„๋™๊ธฐ๋กœ ์ฟผ๋ฆฌ ์ค‘ ์‚ฝ์ž…์ด ๋  ๊ฒฝ์šฐ์— ์—…๋ฐ์ดํŠธ ๋˜๋Š” ์ •๋ณด๋Š” ์•Œ์•„์„œ ์•Œ ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค. query ๋ฉ”์†Œ๋“œ์˜ ๊ตฌํ˜„์— ๋Œ€ํ•ด ์ž˜ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด ๊ณต์‹๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ด๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

     

        override fun insert(uri: Uri, values: ContentValues?): Uri? {
            val id = database.insert(TABLE_NAME, null, values)
    
            if(id > 0){
                val uri = ContentUris.withAppendedId(CONTENT_URI, id)
                context?.contentResolver?.notifyChange(uri, null)
                return uri
            }
    
            throw SQLException("์ถ”๊ฐ€ ์‹คํŒจ URI : $uri")
        }

     insert๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜์ธ ContentValues ๊ฐ์ฒด์—๋Š” ์ €์žฅํ•  ์ปฌ๋Ÿผ๋ช…๊ณผ ๊ฐ’๋“ค์ด ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. SQLiteDatabase์˜ insert ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฝ์ž…๋  row ID๋ฅผ ๋ฐ˜ํ™˜๋ฐ›์€ ํ›„ ContentUris์˜ withAppendedId ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ํ…Œ์ด๋ธ”์˜ Content URI์— row ID๊ฐ€ ํ•ฉ์ณ์ง„ URI๋ฅผ ๋ฐ˜ํ™˜๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ notifyChange ๋ฉ”์†Œ๋“œ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์ถ”๊ฐ€, ์ˆ˜์ •, ์‚ญ์ œ๋˜์—ˆ์„ ๋•Œ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚ฌ์Œ์„ ์•Œ๋ ค์ฃผ๋Š” ์—ญํ• ์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ๋ชจ๋“  ์ฟผ๋ฆฌ๋Š” ๋น„๋™๊ธฐ๋กœ ์š”์ฒญ์„ ํ•˜๊ธฐ์— notifyChange ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ์›ํ™œํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹ ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

     

        override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
            var count = 0
            when(uriMatcher.match(uri)){
                PERSONS -> count = database.delete(TABLE_NAME, selection, selectionArgs)
                else -> throw IllegalArgumentException("์•Œ ์ˆ˜ ์—†๋Š” URI : $uri")
            }
            context?.contentResolver?.notifyChange(uri, null)
            return count
        }

      delete ๋ฉ”์†Œ๋“œ๋Š” where์ ˆ์— ํ•ด๋‹นํ•˜๋Š” selection ๋งค๊ฐœ๋ณ€์ˆ˜์™€ where์ ˆ ๋‚ด์˜ ?์— ํ•ด๋‹น๋˜๋Š” selectionArgs ๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. uriMatcher๋กœ URI ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•œ ํ›„ SQLiteDatabase์˜ delete ๋ฉ”์†Œ๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋™์ผํ•˜๊ฒŒ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์•Œ๋ฆฌ๊ธฐ ์œ„ํ•ด notifyChange ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

     

        override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int {
            var count = 0
            when(uriMatcher.match(uri)){
                PERSONS -> count = database.update(TABLE_NAME, values, selection, selectionArgs)
                else -> throw IllegalArgumentException("์•Œ ์ˆ˜ ์—†๋Š” URI : $uri")
            }
            context?.contentResolver?.notifyChange(uri, null)
            return count
        }

      update ๋ฉ”์†Œ๋“œ๋„ ํฐ ์ฐจ์ด๊ฐ€ ์—†์–ด์„œ ์„ค๋ช…์€ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค.

     

        override fun getType(uri: Uri): String? {
            when(uriMatcher.match(uri)){
                PERSONS -> return "vnd.android.cursor.dir/persons"
                else -> throw IllegalArgumentException("์•Œ ์ˆ˜ ์—†๋Š” URI : $uri")
            }
        }

      getType ๋ฉ”์†Œ๋“œ๋Š” Content Uri๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ ์œ ํ˜•(MIME)์„ ์•Œ๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค. ํ…์ŠคํŠธ , HTML ๋˜๋Š” JPEG์™€ ๊ฐ™์€ ๋ณดํŽธ์ ์ธ ์œ ํ˜•์˜ ๋ฐ์ดํ„ฐ๋ผ๋ฉด getType ์ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ํ‘œ์ค€ MIME ์œ ํ˜•์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๊ณ , ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ” ๋˜๋Š” ํ…Œ์ด๋ธ”์˜ ํ–‰์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค๋ฉด Android ๊ณต๊ธ‰์—…์ฒด๋ณ„ MIME ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜๋˜๋Š” ์œ ํ˜•๋ณ„ MIME ํ˜•์‹์€ ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

    https://developer.android.com/guide/topics/providers/content-provider-creating?hl=ko#MIMETypes 

     

    ์ฝ˜ํ…์ธ  ์ œ๊ณต์ž ์ƒ์„ฑ  |  Android ๊ฐœ๋ฐœ์ž  |  Android Developers

    ์ฝ˜ํ…์ธ  ์ œ๊ณต์ž ์ƒ์„ฑ ์ฝ˜ํ…์ธ  ์ œ๊ณต์ž๋Š” ์ค‘์•™ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋กœ์˜ ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. Android ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์ œ๊ณต์ž๋ฅผ ํ•˜๋‚˜ ์ด์ƒ์˜ ํด๋ž˜์Šค๋กœ, ๋งค๋‹ˆํŽ˜์ŠคํŠธ ํŒŒ์ผ์— ์žˆ๋Š” ์š”์†Œ์™€ ํ•จ๊ป˜ ๊ตฌํ˜„

    developer.android.com

     

    ๋ฐ˜์‘ํ˜•

     

     

     

    ContentProvider๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค

      ContentProvider๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋‚ด ์•ฑ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฅธ ์•ฑ์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋‹ค๋ฅธ ์•ฑ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์•ก์„ธ์Šคํ•˜๋Š” ์‹ค์Šต ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „์— ์„ค๋ช…ํ•˜์˜€๋“ฏ์ด ContentProvider๊ฐ€ ์ƒ์„ฑ๋œ ์•ฑ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํด๋ผ์ด์–ธํŠธ ์—ญํ• ํ•˜๋Š” ContentResolver๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

        private fun insertData() {
            println("insertData๊ฐ€ ํ˜ธ์ถœ๋จ")
            var uri = Uri.parse("content://com.example.contentprovider/person")
            val values = ContentValues().apply {
                put(PERSON_NAME, "ows")
                put(PERSON_AGE, 28)
                put(PERSON_MOBILE, "010-0000-0000")
            }
    
            uri = contentResolver.insert(uri, values)
            println("insertDatat ๊ฒฐ๊ณผ : $uri")
        }

     ContentProvider์—์„œ ์ •์˜ํ•œ Content Uri๋ฅผ ์„ ์–ธํ•œ ๋’ค ContentResolver์˜ insert ๋ฉ”์†Œ๋“œ์— Uri์™€ ContentValues์— ์ €์žฅํ•œ ์ถ”๊ฐ€ํ•  ๋ฐ์ดํ„ฐ๋“ค์„ ๋Œ€์ž…ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ์—์„œ๋Š” name์€ ows, age๋Š” 28, mobile์€ 010-0000-0000์œผ๋กœ ์„ค์ • ํ›„ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

     

        @SuppressLint("Range")
        private fun queryData() {
            val uri = Uri.parse("content://com.example.contentprovider/person")
            val columns = arrayOf(PERSON_NAME, PERSON_AGE, PERSON_MOBILE)
            val cursor = contentResolver.query(uri, columns, null, null,"name ASC")
            println("queryData ๊ฒฐ๊ณผ ${cursor?.count}")
    
            cursor?.let { cursor ->
                var index = 0
                while(cursor.moveToNext()){
                    val name = cursor.getString(cursor.getColumnIndex(columns.get(0)))
                    val age = cursor.getInt(cursor.getColumnIndex(columns.get(1)))
                    val mobile = cursor.getString(cursor.getColumnIndex(columns.get(2)))
    
                    println("#${index} -> ${name}, ${age}, ${mobile}")
                    index++
                }
            }
        }

      ๋‹ค๋ฅธ ์•ฑ ๋ฐ์ดํ„ฐ๋ฅผ ์ฟผ๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์กฐํšŒํ•˜๊ณ  ์‹ถ์€ ์ปฌ๋Ÿผ ๋ช…๊ณผ where ์ ˆ์— ํ•„์š”ํ•œ ๋ณ€์ˆ˜๋“ค, ์˜ค๋ฆ„์ฐจ์ˆœ์— ๋Œ€ํ•œ ๋ฌธ์ž์—ด์„ ContentResolver์˜ query ๋ฉ”์†Œ๋“œ์— ๋Œ€์ž…์„ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. query ๋ฉ”์†Œ๋“œ์˜ ๊ฒฐ๊ณผ๋กœ Cursor ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋˜๊ฒŒ ๋˜๋Š”๋ฐ, Cursor ๊ฐ์ฒด๋Š” ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์—ญํ• ์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Cursor์˜ moveToNext ๋ฉ”์†Œ๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์”ฉ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ปฌ๋Ÿผ๋ช…์— ํ•ด๋‹นํ•˜๋Š” ์ธ๋ฑ์Šค๋ฅผ ์•Œ๊ณ  ์‹ถ์œผ๋ฉด getColumnIndex ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ์— ๋“ค์–ด๊ฐ„ ๋ชจ๋“  ์ปฌ๋Ÿผ๋ช…๋“ค์„ ํ™•์ธํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋Š” getColumnNames ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. 

     

        private fun updateDate() {
            val uri = Uri.parse("content://com.example.contentprovider/person")
            val selection = "mobile = ?"
            val selectionArgs = arrayOf("010-0000-0000")
    
            val values = ContentValues().apply {
                put("mobile","010-1000-1000")
            }
    
            val count = contentResolver.update(uri, values, selection, selectionArgs)
            println("updateData ๊ฒฐ๊ณผ ${count}")
        }
        
        private fun deleteData() {
            val uri = Uri.parse("content://com.example.contentprovider/person")
            val selection = "name = ?"
            val selectionArgs = arrayOf("ows")
    
            val count = contentResolver.delete(uri, selection, selectionArgs)
            println("deleteData ๊ฒฐ๊ณผ ${count}")
        }
    
        private fun println(str: String) = with(binding){
            resultTextView.append("$str\n")
        }

      update, delete ๋ช…๋ น์–ด๋„ ํฌ๊ฒŒ ์ฐจ์ด๋‚˜์ง€ ์•Š๊ณ  ContentProvider์—์„œ ์ •์˜ํ•œ Content Uri๊ฐ€ ํ•„์ˆ˜์ ์œผ๋กœ ํ•„์š”๋กœ ํ•˜๊ณ  ๊ทธ์™ธ wherer์ ˆ์— ํ•„์š”ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. update ๋ฉ”์†Œ๋“œ์—์„œ mobile ์ •๋ณด๊ฐ€ 010-0000-0000์œผ๋กœ ํ•ด๋‹น๋˜๋Š” ๋ฐ์ดํ„ฐ๋“ค์„ ๋ชจ๋‘ 010-1000-1000์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋„๋ก ๊ตฌํ˜„ํ•˜์˜€๊ณ , delete ๋ฉ”์†Œ๋“œ์—์„œ๋Š” name ์ •๋ณด๊ฐ€ ows๋กœ ์ง€์ •๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ์‚ญ์ œํ•˜๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

     

     

    ContentProvider Manifest ๋“ฑ๋ก

      ์œ„์˜ ์ฝ”๋“œ๋งŒ ์ž‘์„ฑํ•œ ํ›„ ์‹คํ–‰์„ ํ•˜๊ฒŒ ๋˜๋ฉด ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ContentProvider๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์„ฑ์š”์†Œ๋กœ ์‹œ์Šคํ…œ์— ๋“ฑ๋กํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. Manifest์— ContentProvider๋ฅผ ๋ช…์‹œํ•˜๊ฒŒ ๋˜๋ฉด ์•ˆ๋“œ๋กœ์ด๋“œ ์‹œ์Šคํ…œ์—์„œ ํ•ด๋‹น ์ œ๊ณต์ž์˜ ์ •๋ณด๋ฅผ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

     

            <provider
                android:authorities="com.example.contentprovider"
                android:name=".PersonProvider"
                android:exported="true"
                android:readPermission="com.example.contentprovider.READ_DATABASE"
                android:writePermission="com.example.contentprovider.WRITE_DATABASE"/>

      application ํƒœ๊ทธ ์•ˆ์— provider ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  authorities์— ContentProvider๋ฅผ ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉํ•œ Authority ์ •๋ณด์™€ ๋™์ผํ•˜๊ฒŒ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค. name ์†์„ฑ ๊ฐ’์œผ๋กœ ContentProvider๋ฅผ ์ƒ์†ํ•œ ํด๋ž˜์Šค ๋ช…์„ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ•ด๋‹น ContentProvider๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” exported ๊ฐ’์„ true๋กœ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ํด๋ผ์ด์–ธํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ContentProvider์— ์ฟผ๋ฆฌ๋ฅผ ํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ์ปค์Šคํ…€ ๊ถŒํ•œ์ธ readPermission๊ณผ ContentProvider์—์„œ ์ œ๊ณตํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•œ ๊ถŒํ•œ์ธ writePermission ๊ถŒํ•œ์„ ์ง€์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์ •๋ณด๋Š” ์•„๋ž˜ ๊ณต์‹๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

    https://developer.android.com/guide/topics/manifest/provider-element?hl=ko#rprmsn 

        <permission android:name="com.android.contentprovider.READ_DATABASE" android:protectionLevel="normal"/>
        <permission android:name="com.android.contentprovider.WRITE_DATABASE" android:protectionLevel="normal"/>

     ContentProvider๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด permission ํƒœ๊ทธ๋ฅผ ํ†ตํ•ด ๊ถŒํ•œ์„ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. <permission> ํƒœ๊ทธ๋Š” ๊ถŒํ•œ์„ ์ƒˆ๋กœ ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ํƒœ๊ทธ๋กœ Provider์˜ readPemission, writePermission์— ์ •์˜๋œ ๊ถŒํ•œ์„ ๋ชจ๋‘ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ๊ฒฐ๊ณผํ™”๋ฉด

     

    ์ •๋ฆฌ

     Do It ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ฑ…์—์„œ ContentProvider๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  kotlin์œผ๋กœ ๋ฐ”๊พธ์–ด ์‹ค์Šต์„ ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ContentProvider๋Š” ๋‹ค๋ฅธ ์•ฑ๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ContentResolver๋ฅผ ํ†ตํ•ด์„œ ContentProvider์™€ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

     

    • ์ฐธ๊ณ (Do it ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ p552~p575
    • https://developer.android.com/guide/topics/providers/content-provider-basics?hl=ko/
    • https://codechacha.com/ko/android-contentprovider/
     

    Android - ContentProvider ๊ตฌํ˜„ ๋ฐ ์˜ˆ์ œ

    ๋งŒ์•ฝ ๋‚ด ์•ฑ์—์„œ ContentProvider๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์ œ๊ณตํ•œ๋‹ค๋ฉด, ๋‹ค๋ฅธ ์•ฑ๋“ค์€ ContentResolver๋ฅผ ํ†ตํ•ด์„œ ๋‚ด ์•ฑ์— ๊ตฌํ˜„๋œ ContentProvider์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ContentProvider๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์œ ์‚ฌํ•˜๊ฒŒ query, insert, upd

    codechacha.com

     

    ๋ฐ˜์‘ํ˜•
Designed by Tistory.