android contentProvider 踩坑日记

写此笔记原因

学习《第一行代码》到第8章节实现provider时踩了一些坑,因此记录下来给后来人和自己一个提示,仅此而已。

包含内容

  1. Sqlite数据库CURD内容
  2. provider界面
  3. provider项目中书籍管理
  4. provider实现逻辑
  5. 用adb shell确认provider
  6. contentResolver接收项目
  7. contentProvider权限
  8. 生成Uri方法

Sqlite数据库CURD内容

  1. Android studio 创建一个Empty的空项目
  2. 不需要等项目同步完成,直接取消同步。因为从官网下载Gradle和java库太慢。
  3. 修改Gradle和java库为国内镜像后,再点击同步。应当就能快速的实现依赖下载
bash 复制代码
    # settings.gradle.kts 
    pluginManagement {
    repositories {
        maven { setUrl("https://maven.aliyun.com/repository/central") }
        maven { setUrl("https://maven.aliyun.com/repository/jcenter") }
        maven { setUrl("https://maven.aliyun.com/repository/google") }
        maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") }
        maven { setUrl("https://maven.aliyun.com/repository/public") }
        maven { setUrl("https://jitpack.io") }
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
        }
    }
    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            maven { setUrl("https://maven.aliyun.com/repository/central") }
            maven { setUrl("https://maven.aliyun.com/repository/jcenter") }
            maven { setUrl("https://maven.aliyun.com/repository/google") }
            maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") }
            maven { setUrl("https://maven.aliyun.com/repository/public") }
            maven { setUrl("https://jitpack.io") }
            google()
            mavenCentral()
        }
    }

    rootProject.name = "BookProviderTest"
    include(":app")
bash 复制代码
	gradle/wrapper/gradle-wrapper.properties
	distributionBase=GRADLE_USER_HOME
	distributionPath=wrapper/dists
	# 此处gradle版本可以根据android相应的java版本自行修改,移植android项目也可以修改此处版本
	distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-all.zip
	zipStoreBase=GRADLE_USER_HOME
	zipStorePath=wrapper/dists
  1. 创建一个类BookDatabaseHelper
bash 复制代码
class BookDatabaseHelper(val context: Context, val name: String, version: Int) :
    SQLiteOpenHelper(context, name, null, version) {
        private val createBookCMD = "create table Book (" +
                " id integer primary key autoincrement, " +
                "author text not null," +
                "price real," +
                "pages integer," +
                "name text not null)"
        private val createCategoryCMD = "create table category (" +
                "id integer primary key autoincrement," +
                "category_name text not null," +
                "category_code integer)"

    override fun onCreate(db: SQLiteDatabase?) {
        db?.execSQL(createBookCMD)
        db?.execSQL(createCategoryCMD)
//        Toast.makeText(context, "创建 $name 成功", Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        db?.execSQL("drop table if exists Book")
        db?.execSQL("drop table if exists category")
        onCreate(db)
    }
}
  1. app/src/main/java/com.example.databasetest中创建一个空的MainActivity,同时选中"Generate a Layout File"和"Launcher Activity",就同时创建了MainActivity和res/layout/activity_main.xml
  2. 引入viewBinding
bash 复制代码
    # build.gradle.kts文件android的大括号中加入,再次同步
    buildFeatures {
        viewBinding = true
    }

provider界面

bash 复制代码
# 切换到图形界面,鼠标右键点击main,选择"Convert view",将布局换成LinearLayout
# 下面内容就是书籍管理界面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="书名"
            android:id="@+id/BookNameLabel"></TextView>
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:hint="书名"
            android:id="@+id/bookName" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="作者"
            android:id="@+id/BookAuthorLabel"></TextView>
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:hint="作者"
            android:id="@+id/bookAuthor" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="页数"
            android:id="@+id/BookPagesLabel"></TextView>
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:inputType="number"
            android:hint="页数"
            android:id="@+id/bookPages" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="售价"
            android:id="@+id/BookPriceLabel"></TextView>
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:hint="售价"
            android:inputType="number"
            android:id="@+id/bookPrice" />
    </LinearLayout>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:layout_weight="1"
            android:layout_gravity="center"
            android:id="@+id/bookSave"
            android:text="保存" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_margin="20dp"
            android:layout_weight="1"
            android:layout_gravity="center"
            android:id="@+id/bookUpdate"
            android:text="更新" />
    </LinearLayout>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:layout_weight="1"
            android:layout_gravity="center"
            android:id="@+id/bookDelete"
            android:text="删除" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_margin="20dp"
            android:layout_weight="1"
            android:layout_gravity="center"
            android:id="@+id/bookSearch"
            android:text="查询" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="20dp" >
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/createDatabase"
            android:layout_gravity="center"
            android:text="创建数据库" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:id="@+id/transictionTest"
            android:layout_gravity="center"
            android:text="事务测试" />
    </LinearLayout>
</LinearLayout>

provider项目中书籍管理

bash 复制代码
# MainActivity
class MainActivity : AppCompatActivity() {
    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        val dbHelper = BookDatabaseHelper(this, "Books.db", 2)

        /* 创建数据库 */
        binding.createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }

        /* 添加新书 */
        binding.bookSave.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues().apply {
                put("name", binding.bookName.text.toString())
                put("author", binding.bookAuthor.text.toString())
                put("pages", binding.bookPages.text.toString().toInt())
                put("price", binding.bookPrice.text.toString().toFloat())
            }
            db.insert("Book", null, values);
            binding.bookName.setText("")
            binding.bookAuthor.setText("")
            binding.bookPages.setText("")
            binding.bookPrice.setText("")
        }

        /* 更新书籍信息 */
        binding.bookUpdate.setOnClickListener {
            var db = dbHelper.writableDatabase
            val values = ContentValues().apply{
                put("name", binding.bookName.text.toString())
                put("author", binding.bookAuthor.text.toString())
                put("pages", binding.bookPages.text.toString().toInt())
                put("price", binding.bookPrice.text.toString().toFloat())
            }
            db.update("Book", values, "name = ?", arrayOf("Book1"))
            binding.bookName.setText("")
            binding.bookAuthor.setText("")
            binding.bookPages.setText("")
            binding.bookPrice.setText("")
        }

        /* 按书名删除书籍信息 */
        binding.bookDelete.setOnClickListener {
            var db = dbHelper.writableDatabase
            db.delete("Book", "name = ?", arrayOf(binding.bookName.text.toString()))
            binding.bookName.setText("")
            binding.bookAuthor.setText("")
            binding.bookPages.setText("")
            binding.bookPrice.setText("")
        }

        binding.bookSearch.setOnClickListener {
            val db = dbHelper.writableDatabase
            val queryBookName = binding.bookName.text.toString()
//            val cursor = db.rawQuery("select name,author,pages,price from Book where name = ?"  , arrayOf(queryBookName))
            val cursor = db.query("Book",arrayOf("name","author","pages","price"), "name = ?", arrayOf(queryBookName), null, null, null)
            if(cursor.count > 0)
            {
                cursor.moveToFirst()
                binding.bookName.setText(cursor.getString(cursor.getColumnIndex("name") as Int))
                binding.bookAuthor.setText(cursor.getString(cursor.getColumnIndex("author") as Int))
                binding.bookPages.setText(cursor.getString(cursor.getColumnIndex("pages") as Int))
                binding.bookPrice.setText(cursor.getString(cursor.getColumnIndex("price") as Int))
            }
        }

        binding.transictionTest.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.beginTransaction()
            try{
                db.delete("Book", null, null)
                if(true)
                {
//                    throw NullPointerException()
                }
                val values = ContentValues().apply {
                    put("name", "Game Of Thrones")
                    put("author", "George Martin")
                    put("pages", 720)
                    put("price", 20.85)
                }
                db.insert("Book", null, values)
                db.setTransactionSuccessful()   // 事务完成提交
            }
            catch (e: Exception)
            {
                e.printStackTrace()
            }
            finally {
                db.endTransaction()
            }
        }
    }
}

至此就可以向Books.db中加入书籍的内容了,保存是添加新书,查询可以查出已经加入的书籍。《第一行代码》第7章还推荐了一个"Database Navigator"可以查看导出的sqlite数据库,可以试试。

provider实现逻辑

bash 复制代码
# 新建一个BookDatabaseProvider,最好使用File->New->Other->Content Provider,会自动在`AndroidManifest.xml`文件中自动加入新建的provider
class BookDatabaseProvider : ContentProvider() {

    /* 自定义uri编号 */
    private val bookDir = 0
    private val bookItem = 1
    private val categoryDir = 2
    private val categoryItem = 3
    private val authority = "com.example.databasetest.provider"
    private var dbHelper: BookDatabaseHelper? = null

    private val uirMatcher by lazy {
        val matcher = UriMatcher(UriMatcher.NO_MATCH)
        matcher.addURI(authority, "book", bookDir)
        matcher.addURI(authority, "book/#", bookItem)
        matcher.addURI(authority,"category", categoryDir)
        matcher.addURI(authority, "category/#", categoryItem)
        matcher
    }

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?) = dbHelper?.let {
        val db = it.writableDatabase
        val deleteRows = when(uirMatcher.match(uri)) {
            bookDir -> db.delete("Book",  selection, selectionArgs)
            bookItem -> {
                /* uri 最后一个字段 */
                val bookId = uri.pathSegments[1]
                db.delete("Book", "id = ?", arrayOf(bookId))
            }
            categoryDir -> db.delete("Category", selection, selectionArgs)
            categoryItem -> {
                val categoryId = uri.pathSegments[1]
                db.delete("Category", "id = ?",arrayOf(categoryId))
            }
            else -> null
        }
        deleteRows
    } ?: 0

    override fun getType(uri: Uri) = when(uirMatcher.match(uri)) {
        bookDir -> "vnd.android.cursor.dir/vnd.$authority.book"
        bookItem -> "vnd.android.cursor.item/vnd.$authority.book"
        categoryDir -> "vnd.android.cursor.dir/vnd.$authority.category"
        categoryItem -> "vnd.android.cursor.item/vnd.$authority.category"
        else -> null
    }

    override fun insert(uri: Uri, values: ContentValues?) = dbHelper?.let {
        val db = it.writableDatabase
        val uriReturn = when(uirMatcher.match(uri)) {
            bookDir, bookItem -> {
                val newBookId = db.insert("Book", null, values)
                Uri.parse("content://$authority/book/$newBookId")
            }
            categoryDir, categoryItem -> {
                val newCategoryId = db.insert("Category", null, values)
                Uri.parse("content://$authority/category/$newCategoryId")
            }
            else -> null
        }
        uriReturn
    }

    override fun onCreate() = context?.let {
        dbHelper = BookDatabaseHelper(it, "Books.db", 2)
        true
    } ?: false

    override fun query(
        uri: Uri, projection: Array<String>?, selection: String?,
        selectionArgs: Array<String>?, sortOrder: String?
    ) = dbHelper?.let{
        val db = it.readableDatabase
        val cursor = when(uirMatcher.match(uri)){
            bookDir -> db.query("Book", projection, selection, selectionArgs, null, null, sortOrder)
            bookItem -> {
                val bookId = uri.pathSegments[1]
                db.query("Book", projection, "id = ?", arrayOf(bookId), null, null, sortOrder)
            }
            categoryDir -> db.query("Category", projection, selection, selectionArgs, null, null, sortOrder)
            categoryItem -> {
                val categoryId = uri.pathSegments[1]
                db.query("Category", projection, "id = ?", arrayOf(categoryId), null, null, sortOrder)
            }
            else -> null
        }
        cursor
    }

    override fun update(
        uri: Uri, values: ContentValues?, selection: String?,
        selectionArgs: Array<String>?
    ) = dbHelper?.let {
        val db = it.writableDatabase
        val updateRows = when(uirMatcher.match(uri)) {
            bookDir -> db.update("Book", values, selection, selectionArgs)
            bookItem -> {
                val bookId = uri.pathSegments[1]
                db.update("Book", values, "id = ?", arrayOf(bookId))
            }
            categoryDir -> db.update("Category", values, selection, selectionArgs)
            categoryItem -> {
                val categoryId = uri.pathSegments[1]
                db.update("Category", values, "id = ?", arrayOf(categoryId))
            }
            else -> null
        }
        updateRows
    } ?: 0
}

完成后就可以连接手机或模拟器点击运行安装了,加入些书籍信息

用adb shell确认provider

bash 复制代码
$ adb shell content query --uri content://com.example.databasetest.provider/book
Row: 0 id=1, author=Author1, price=1.0, pages=1, name=Book1
Row: 1 id=2, author=Author2, price=2.0, pages=2, name=Book2
# 可以看到输出了2本刚加入的书籍信息,这里还没有加入权限说明adb shell可以直接读取书籍信息
adb shell content query --uri content://com.example.databasetest.provider/book --where "name=\'Book1\'"
# 这里如果要找特定行内容需要以\' xxx \'这样的形式,什么name:s:xxx "name='xxx'" 都会报错

contentResolver接收项目界面

同样的方法再建立一个访问provider的项目"BookProviderTest",建立一个新的Activity,和activity_main.xml布局。

bash 复制代码
# 布局和上面的书籍管理类似
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="书名"
            android:id="@+id/BookNameLabel"></TextView>
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:hint="书名"
            android:id="@+id/bookName" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="作者"
            android:id="@+id/BookAuthorLabel"></TextView>
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:hint="作者"
            android:id="@+id/bookAuthor" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="页数"
            android:id="@+id/BookPagesLabel"></TextView>
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:inputType="number"
            android:hint="页数"
            android:id="@+id/bookPages" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="售价"
            android:id="@+id/BookPriceLabel"></TextView>
        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:hint="售价"
            android:inputType="number"
            android:id="@+id/bookPrice" />
    </LinearLayout>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:layout_weight="1"
            android:layout_gravity="center"
            android:id="@+id/bookSave"
            android:text="保存" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_margin="20dp"
            android:layout_weight="1"
            android:layout_gravity="center"
            android:id="@+id/bookUpdate"
            android:text="更新" />
    </LinearLayout>
    <LinearLayout
        android:orientation="horizontal"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:layout_weight="1"
            android:layout_gravity="center"
            android:id="@+id/bookDelete"
            android:text="删除" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_margin="20dp"
            android:layout_weight="1"
            android:layout_gravity="center"
            android:id="@+id/bookSearch"
            android:text="查询" />
    </LinearLayout>
</LinearLayout>
bash 复制代码
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    var bookId: String? = null
    val providerUri = "content://com.example.databasetest.provider"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        supportActionBar?.hide()

        binding.bookSave.setOnClickListener {
            var uri = Uri.parse("$providerUri/book")
            val values = contentValuesOf("name" to binding.bookName.text.toString(),
                "author" to binding.bookAuthor.text.toString(),
                "pages" to binding.bookPages.text.toString().toInt(),
                "price" to binding.bookPrice.text.toString().toDouble())
            val newUri = contentResolver.insert(uri, values)
            bookId = newUri?.pathSegments?.get(1)
        }
        binding.bookSearch.setOnClickListener {
            val uri = Uri.parse("content://com.example.databasetest.provider/book")
            val bookName = binding.bookName.text.toString()
            val cursor = contentResolver.query(uri, arrayOf("name","author","pages","price"), "name = ?", arrayOf(bookName), null)
                cursor?.apply{
                while(moveToNext())
                {
                    val nameId = getColumnIndex("name")
                    val name = getString(nameId)
                    val authorId = getColumnIndex("author")
                    val author = getString(authorId)
                    val pagesId = getColumnIndex("pages")
                    val pages = getInt(pagesId)
                    val priceId = getColumnIndex("price")
                    val price = getDouble(priceId)

                    binding.bookName.setText(name)
                    binding.bookAuthor.setText(author)
                    binding.bookPages.setText(pages.toString())
                    binding.bookPrice.setText(price.toString())
                }
                close()
            }
        }
        binding.bookUpdate.setOnClickListener {
            val uri = Uri.parse("$providerUri/book")
            val values = contentValuesOf("name" to binding.bookName.text.toString(),
                "author" to binding.bookAuthor.text.toString(),
                "pages" to binding.bookPages.text.toString().toInt(),
                "price" to binding.bookPrice.text.toString().toDouble())
            contentResolver.update(uri, values, "name = ?", arrayOf(binding.bookName.text.toString()))
        }
        binding.bookDelete.setOnClickListener {
            val uri = Uri.parse("$providerUri/book")
            contentResolver.delete(uri, "name = ?", arrayOf(binding.bookName.text.toString()))
        }
    }
}

此项目直接运行是不能获取到provider项目的书籍信息的,因为provider没有指定权限,此项目还没有权限读写provider内容。因此接下来就是要给provider项目和本项目添加权限说明

contentProvider权限

  1. 打开第一个项目的AndroidManifest.xml
bash 复制代码
# 加入权限声名
    <permission android:name="com.example.databasetest.READ_PERMISSION"
        android:protectionLevel="normal" /  >
    <permission android:name="com.example.databasetest.WRITE_PERMISSION"
        android:protectionLevel="normal" />
# provider信息改成如下内容
        <provider
            android:name=".BookDatabaseProvider"
            android:authorities="com.example.databasetest.provider"
            android:enabled="true"
            android:readPermission="com.example.databasetest.READ_PERMISSION"
            android:writePermission="com.example.databasetest.WRITE_PERMISSION"
            android:exported="true">
        </provider>
# 删除应用后重新安装,安装后就不能使用adb shell content获取内容了
  1. 修改第二个项目的AndroidManifest.xml
bash 复制代码
    <uses-permission android:name="com.example.databasetest.READ_PERMISSION" />
    <uses-permission android:name="com.example.databasetest.WRITE_PERMISSION" />

再次运行就可以在第二个项目中获取和编辑第一个项目的provider内容了

生成Uri方法

刚开始没有发现是权限问题,怎么都不能获取到内容,还以为是因为Uri不正确。因此发现Uri生成方式有几种,这里也记录一下。

bash 复制代码
val uri = Uri.parse("content://authority/path")
# 发现此方法生成的Uri,authority和path没有解析到相应字段中,但程序还是可以正常运行,获取到内容
val uri = Uri.Builder().apply{
    scheme("content")
    authority("com.example.authority")
    path("path")
}.build()
# 使用此方法,authority和path都能正确解析到相应字段中,后者应当是比较推荐的方法,就是有点啰嗦
# 系统自带的联系人等provider的uri也和后者生成的uri基本一样
相关推荐
飞猿_SIR24 分钟前
Android Exoplayer 实现多个音视频文件混合播放以及音轨切换
android·音视频
HumoChen991 小时前
GZip+Base64压缩字符串在ios上解压报错问题解决(安卓、PC模拟器正常)
android·小程序·uniapp·base64·gzip
沙振宇5 小时前
【HarmonyOS】ArkTS开发应用的横竖屏切换
android·华为·harmonyos
橙子199110167 小时前
Kotlin 中的作用域函数
android·开发语言·kotlin
zimoyin7 小时前
Kotlin 懒初始化值
android·开发语言·kotlin
枣伊吕波7 小时前
第六节第二部分:抽象类的应用-模板方法设计模式
android·java·设计模式
萧然CS8 小时前
使用ADB命令操作Android的apk/aab包
android·adb
_extraordinary_12 小时前
MySQL 事务(二)
android·数据库·mysql
鸿蒙布道师16 小时前
鸿蒙NEXT开发动画案例5
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
橙子1991101621 小时前
在 Kotlin 中什么是委托属性,简要说说其使用场景和原理
android·开发语言·kotlin