• 内容提供者


    CSDN话题挑战赛第2期
    参赛话题:面试宝典

    1. Uri

    Uri:统一资源定位符。url也是uri的一种

    [scheme:][//authority][path][?query][#fragment]
    ↓
    ↓
    [scheme:][//host:port][path][?query][#fragment]
    
    • path可以有多个,每个用/连接,比如
      scheme://authority/path1/path2/path3?query#fragment

    • query参数可以带有对应的值,也可以不带,如果带对应的值用=表示
      scheme://authority/path1/path2/path3?id = 1#fragment,这里有一个参数id,它的值是1

    • query参数可以有多个,每个用&连接

      scheme://authority/path1/path2/path3?id = 1&name = mingming&old#fragment

    • 在android中,除了scheme、authority是必须要有的,其它的几个path、query、fragment,它们每一个可以选择性的要或不要,但顺序不能变

    String str = "http://www.csdn.com:8080/path/filename.htm?netrow=10&path=32&id=4#monk";
    Uri uri = Uri.parse(str);
    // 获取path部分
    List<String> list = uri.getPathSegments();
    for(String item:list){
        L.i("tag","item:"+item);
    }
    /*
    item:path
    item:filename.htm
    */
    
    // 获取query部分
    String query1 = uri.getQueryParameter("netrow");// 10
    String query2 = uri.getQueryParameter("path")// 4
    

    2. 创建Provider

    安卓四大组件之一,平时用的不多,比较流行的用法是三方库的初始化,例如集成facebook

    其工作方式是通过uri来标识其它应用要访问的数据,通过ContentResolver实现增、删、改、查的操作,还可以通过ContentObserver监听数据的变化

    class MyContentProvider : ContentProvider() {
        companion object {
            private const val AUTHORITY = "com.demo.myprovider"
            private const val MATCH_CODE = 100
            private val NOTIFY_URI = Uri.parse("content://$AUTHORITY/student")
            
            private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
        }
        
        init {
            uriMatcher.addURI(AUTHORITY,"student", MATCH_CODE)
        }
    
        // SQLite数据库
        private var dao:Dao?=null
    
        override fun onCreate(): Boolean {
            dao = Dao.getInstance(context);
            return false;
        }
        override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? {
            val match = uriMatcher.match(uri)
            return if (match== MATCH_CODE) dao.queryStudent()else null
        }
        override fun getType(uri: Uri): String? {
            return null
        }
         override fun insert(uri: Uri, values: ContentValues?): Uri? {
            if (uriMatcher.match(uri) == MATCH_CODE) {
                dao.insertStudent(values)
                context?.contentResolver?.notifyChange(NOTIFY_URI, null)
            }
            return null
        }
        override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
            TODO("Not yet implemented")
        }
    
        override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int {
            TODO("Not yet implemented")
        }
    
    <provider
          android:authorities="com.demo.myprovider"
          android:name=".MyContentProvider"
          android:exported="true"/>
    
    

    3. 操作Provider

    其它应用操作本应用的ContentProvider

    companion object{
        private const val AUTHORITY = "com.demo.myprovider"
        private val NOTIFY_URI = Uri.parse("content://$AUTHORITY/student")
    }
    
    var handler :Handler = object : Handler(Looper.getMainLooper()){
        override fun handleMessage(msg: Message) {
    
        }
    }
    
    private fun test(){
        contentResolver.registerContentObserver(NOTIFY_URI,true, object :ContentObserver(handler){
            override fun onChange(selfChange: Boolean, uri: Uri?) {
                super.onChange(selfChange, uri)
                val msg = Message.obtain()
                msg.obj = uri
                handler.sendMessage(msg)
            }
        })
    }
    

    通过ContentProvider来操作数据

     fun onClick(v: View) {
         val id: Int = v.getId()
         if (id == R.id.btn_query) {
             val cursor: Cursor? = contentResolver.query(STUDENT_URI, null, null, null, null, null)
             if (cursor != null && cursor.getCount() > 0) {
                 while (cursor.moveToNext()) {
                     val name: String = cursor.getString(cursor.getColumnIndex("name"))
                     val age: Int = cursor.getInt(cursor.getColumnIndex("age"))
                     val desc: String = cursor.getString(cursor.getColumnIndex("desc"))
                     Log.e(javaClass.simpleName, "Student:name = $name,age = $age,desc = $desc")
                 }
                 cursor.close()
             }
         } else if (id == R.id.btn_insert) {
             val contentValues = ContentValues()
             contentValues.put("name", "insert")
             contentValues.put("age", "-100")
             contentValues.put("desc", "i am insert")
             contentResolver.insert(STUDENT_URI, contentValues)
         } else if (id == R.id.btn_del) {
             contentResolver.delete(STUDENT_URI, null, null)
         }
     }
    

    ContenProvider的用法大体如上

    4. FileProvider

    Android7.0新特性,对于7.0及以上的设备而言,Google官方禁止使用公开file://的形式对外暴露uri,否则会报FileUriExposedException异常,需要使用content://类型进行Uri的分享,并且需要提供临时的文件访问权限Intent.FLAG_GRANT_READ_URI_PERMISSIONIntent.FLAG_GRANT_WRITE_URI_PERMISSION。官方给出的方案是使用FileProvider进行分享

     <provider
          android:authorities="com.demo.myapplication.fileProvider"
          android:name="androidx.core.content.FileProvider"
          android:grantUriPermissions="true"
          android:exported="false" >
         <meta-data 
               android:name="android.support.FILE_PROVIDER_PATHS"
               android:resource="@xml/file_paths" />
    provider>
    

    authorities:签名认证,自定义,获取uri时保持一致即可

    name:使用官方提供的FileProvider,也可以自定义继承FileProvider

    grantUriPermissions:临时访问权限(READ 和 WRITE)

    meta-data:配置可以访问的文件的路径配置信息,需要使用xml文件配置。name固定写法。

    
    
    <resources>
        <paths>
            
            <files-path
                name="my_files"
                path="mazaiting/"/>
            
            <cache-path
                name="my_cache"
                path="mazaiting/"/>
            
            <external-files-path
                name="external-files-path"
                path="mazaiting/"/>
            
            <external-cache-path
                name="name"
                path="mazaiting/" />
            
            <external-path
                name="my_external_path"
                path="mazaiting/"/>
            
            <external-path
                name="files_root"
                path="Android/data/<包名>/"/>
            
            <external-path
                name="external_storage_root"
                path="."/>
        paths>
    resources>
    

    用安装apk举个FileProvider使用的例子

    val intent = Intent(Intent.ACTION_VIEW)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        val uri = FileProvider.getUriForFile(this,"${BuildConfig.APPLICATION_ID}.fileProvider",File(path))
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.setDataAndType(uri,"application/vnd.android.package-archive")
    }else{
        intent.setDataAndType(Uri.parse("file://$path"),"application/vnd.android.package-archive")
    }
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    startActivity(intent)
    
  • 相关阅读:
    基于jeecgboot流程管理平台的在线表单设计修改成formdesigner(一)
    混沌系统在图像加密中的应用(小波混沌神经网络)
    Linux高性能服务器I/0高级应用:非阻塞connect(15)
    ssl/tls 自签证书
    2020-10《信息资源管理 02378》真卷(独家文字版),圈定章节考点+统计真题分布
    存储 MD5 的值应该用 VARCHAR 还是 CHAR
    vlunhub靶场之EMPIRE: LUPINONE
    slam14讲总结(一)
    Linux无文件木马程序渗透测试复现
    产品经理撰写需求文档
  • 原文地址:https://blog.csdn.net/qq_37776700/article/details/127084877