安卓笔记(四)ContentProvider 与 运行时权限


在Android 13(T)官方的获取权限不见得比其他辅助的库更复杂,具体用法 这里没有更新,请自行查阅资料

运行时权限

 安卓权限组分普通权限危险权限,每组又有多个,只要授权某组的一个,该组就默认全都授权,但组规则会变,不要基于此实现任何功能逻辑。

拨号为例

class MainActivity : AppCompatActivity()
{
	override fun onCreate(savedInstanceState: Bundle?)
	{
		super.onCreate(savedInstanceState)
		val button = Button(this)
		button.setOnClickListener {
			callPhone()
		}
		setContentView(button)
	}

	private fun callPhone()
	{
		try
		{
			// ACTION_CALL 直接拨打权限,需要在AndroidManifest申明
			// <uses-permission android:name="android.permission.CALL_PHONE"/>
			Intent(Intent.ACTION_CALL).run {
				data = Uri.parse("tel:10086")
				startActivity(this)
			}
		} catch (e: SecurityException)
		{
			e.printStackTrace()
		}
	}
}

在安卓6.0版本之前,这是没有问题的,更高版本,需要运行时处理,修复如下:


class MainActivity : AppCompatActivity()
{
	override fun onCreate(savedInstanceState: Bundle?)
	{
		super.onCreate(savedInstanceState)
		val button = Button(this)
		button.setOnClickListener {
			checkPermissions()
		}
		setContentView(button)
	}

	private fun callPhone()
	{
		try
		{
			// ACTION_CALL 直接拨打权限,需要在AndroidManifest申明
			// <uses-permission android:name="android.permission.CALL_PHONE"/>
			Intent(Intent.ACTION_CALL).run {
				data = Uri.parse("tel:10086")
				startActivity(this)
			}
		} catch (e: SecurityException)
		{
			e.printStackTrace()
		}
	}

	private fun checkPermissions()
	{
		// 第一步,判断用户是否已经给与授权
		if(ContextCompat.checkSelfPermission(this,
			Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED)
		{
			// 请求授权,不论结果如何,都会调用onRequestPermissionsResult()方法
			ActivityCompat.requestPermissions(
					this,
					arrayOf(Manifest.permission.CALL_PHONE),
					1 // 请求码,用于返回匹配结果, 应该 >= 0
			)
		} else {
			callPhone()
		}
	}

	override fun onRequestPermissionsResult(
			requestCode: Int,
			permissions: Array<out String>,
			grantResults: IntArray
	)
	{
		super.onRequestPermissionsResult(requestCode, permissions, grantResults)
		when(requestCode)
		{
			// 自定义的请求码
			1 -> {
				if(grantResults.isNotEmpty() &&
					// 结果封装在内
						grantResults[0] == PackageManager.PERMISSION_GRANTED)
				{
					callPhone()
				} else {
					Toast.makeText(this, "你已拒绝", Toast.LENGTH_SHORT).show()
				}
			}
		}
	}
}

ContentProvider

确保安全性的同时,实现数据共享。用法有:

  1. 使用现有ContentProvider读取和操作应用程序数据
  2. 自定义ContentProvider ,给程序数据提供接口访问

使用现有ContentProvider

对于每个应用程序,访问ContentProvider需要借助ContentResolver,
在Kotlin中,直接引用contentResolver即可(语法糖)

ContentResolver提供的类似SQLiteDatabase的增删查改操作,不过较为容易。但都不再接收表名参数,而是使用Uri替代。标准格式如下:

content://authority.provider/path

authority:区分不同应用程序,一般为避免冲突,采用应用包名命名。
path:同一应用不同表名做区分

除此之外,还可以加上id

content://authority.provider/path/id

以id结尾可以有更大的拓展可能性,可以用通配符分别匹配这两种格式的内容URI,规则如下。

  1. * 表示 匹配任意长度的任意字符
  2. # 表示 匹配任意长度的梳子
    解析方法:
val uri = Uri.parse("content://authority.provider/path")

查询表

val cursor = contentResolver.query(
	uri,
	projection,
	selection,
	selectionArgs,
	sortOrder
)
参数对应SQL码描述
urifeom table_name指定查询某应用下的一个表
projectionselect column1, column2指定查询的列名
selectionwhere column = value指定where的约束条件
selectionArgs-为where的占位符提供具体描述
sortOrderorder by column1, column2指定查询结果的排序方式

cursor表量Cursor对象,这样就能读取数据,依然是遍历所有行

while(cursor.moveToNext())
{
	val column1 = cursor.getString(cursor.getColumnIndex("column1"))
	val column2 = cursor.getInt   (cursor.getColumnIndex("column2"))
}

增加数据

contentResolver.insert(
	uri,
	contentValuesOf(
		"colum1"  to "text",
		"column2" to 1
	)
)

更新数据
若更新如下数据,把column1值清空,可由update()实现

contentResolver.update(
	uri,
	contentValuesOf("colum1"  to ""),
	"column1 = ? and column2 = ?",
	arrayOf("text", "1")
)

上述代码使用了selectionselectionArgs参数对想要更新的数据进行约束,防止所有行为都受到影响

删除
可以如下删除这条数据

contentResolver.delete(
	uri,
	"column1 = ?",
	arrayOf("1")
)

读取系统联系人

首先还是申请运行时权限、AndroidManiFest.xml写入信息,检查是否授权READ_CONTACTS等等,这里不再赘述。
关键读取代码如下:

@SuppressLint("Range")
private fun readContacts()
	{
		// 查询联系人数据
		contentResolver.query(
				// 这里封装好了Uri
				ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
				null, null, null, null
		)?.apply {
			while (moveToNext())
			{
				// 联系人名字
				val displayName = getString(getColumnIndex(
						ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
				))
				val number = getString(getColumnIndex(
						ContactsContract.CommonDataKinds.Phone.NUMBER
				))
			}
			// 关闭流
			close()
		}
	}

自定义ContentResolver

自定义主要是继承contentResolver然后重写方法,需要结合UriMatcher和数据库来实现,现有资料很多,这里便不再赘述。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值