CSDN话题挑战赛第2期
参赛话题:面试宝典
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
安卓四大组件之一,平时用的不多,比较流行的用法是三方库的初始化,例如集成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"/>
其它应用操作本应用的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的用法大体如上
Android7.0新特性,对于7.0及以上的设备而言,Google官方禁止使用公开file://的形式对外暴露uri,否则会报FileUriExposedException异常,需要使用content://类型进行Uri的分享,并且需要提供临时的文件访问权限Intent.FLAG_GRANT_READ_URI_PERMISSION和Intent.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)