1.运行时权限
Android运行时的危险权限
在运行时申请权限代码:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
makeCall.setOnClickListener {
//获得运行时权限
if(ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,arrayOf(Manifest.permission.CALL_PHONE),1)
}else{
call()
}
}
}
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){
call()
}else{
Toast.makeText(this,"没有打电话的权限",Toast.LENGTH_SHORT).show()
}
}
}
}
private fun call(){
try {
val intent = Intent(Intent.ACTION_CALL)
intent.data = Uri.parse("tel:10086")
startActivity(intent)
}catch (e : Exception){
e.printStackTrace()
}
}
}
在按钮点击事件中,我们构建了一个隐式 Intent,Intent 的 action 指定为Intent.ACTION_CALL,这是系统内置的打电话的动作,然后在data部分制定了协议是tel,号码是10086。
第一步先判断用户是不是已经给过我们授权了,借助的是ContextCompat.checkSelfPermission()方法。checkSelfPermission()方法接收两个参数:第一个是Context,第二个参数是具体的权限名。然后我们使用返回值和PackageManager.PERMISSION_GRANTED做比较。相等就说明用户已经授权
我们将打电话的逻辑封装到了call()方法当中。如果没有授权的话,则需调用ActivityCompat.request.Permissions()方法向用户申请授权,requestPermissions()方法接收3个参数:第一个要求是Activity的实例,第二个参数是一个String数组,把申请的权限名放到数组里即可。第三个参数是请求码,只要是唯一值就可以,这里传入了1.
调用完requestPermissions()方法后,会弹出申请权限对话框。不论选择哪种结果,都会回调到onRequestPermissionsResult()方法中,授权结果会封装在grantResults参数中。
2.访问其他程序中的数据
ContentResolver 中提供了一系列方法对数据进行增删改查操作,但不同于SQLiteDatabase,ContentProvider中不接受表名,而是使用一个Uri参数代替。
内容URI最标准的格式如下:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
它主要由两部分组成:authority 和 path。authority用于对不同的应用程序做区分,会采用应用包名的方式进行命名。path则是对同一应用程序不同表做区分
1.将内容URI解析成Uri对象才可以作为参数传入。
val uri = Uri.parse(“content://com.example.app.provider/table1”)
2.使用这个Uri对象查询table1表中的数据
val cursor = contentResolver.query{
uri,
projection,
selection,
selectionArgs,
sortOrder
}
query()方法参数 | 对应SQL部分 | 描述
uri | from table_name | 指定查询某个应用程序下的某一张表
projection | selet column1 , colum2 | 指定查询的列名
selection | where column =value | 指定where的约束条件
selectionArgs | | 为where中的占位符提供具体的值
sortOrder | order by column1,column2 | 指定查询结果的排序方式
查询完成后返回仍然是一个Cursor对象
while(cursor.moveToNext()){
val column1 = cursor.getString(Cursoe.getColumnIndex("colum1"))
val column2 = cursor.getString(Cursoe.getColumnIndex("colum2"))
}
curosr.close()
向table1表添加一条数据
val values = contentValuesOf( "column1" to "text", "column2" to 1 )
contentResolver.insert(uri , values)
更新数据,将colum1的值清空
val values = contentValuesOf( "column1" to "" )
contentResolver.update(uri , values,"column2 = ?", arrayOf("1"))
调用ContentResolver的delete()方法将这条数据删除掉
contentResolver.deleter(uri , "colummn2 = ?", arrayOf("1"))
2.2实战读取系统联系人
修改activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<ListView
android:id="@+id/contactsView"
android:layout_width="match_parent"
android:layout_height="match_parent"
></ListView>
</androidx.appcompat.widget.LinearLayoutCompat>
修改MainActivity
class MainActivity : AppCompatActivity() {
private val contactsList = ArrayList<String>()
private lateinit var adapter: ArrayAdapter<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
adapter= ArrayAdapter(this,android.R.layout.simple_list_item_1,contactsList)
contactsView.adapter=adapter
//动态获取权限
if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, arrayOf( Manifest.permission.READ_CONTACTS),1)
}else{
readContacts()
}
}
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){
readContacts()
}else{
Toast.makeText(this,"没有权限的话,就不能这样做啦!",Toast.LENGTH_SHORT).show()
}
}
}
}
//读取联系人操作
private fun readContacts(){
//查询联系人数据
contentResolver.query(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))
contactsList.add("$displayName\n电话号码:$number")
}
adapter.notifyDataSetChanged()
close()
}
}
}
3.创建自己的ContentProvider
3.1创建 ContentProvider的步骤
ContentProvider类种有6个抽象方法
1)onCreate()初始化ContentProvider时调用。通常会在这里完成对数据库的创建和升级等操作,返回true表示ContentProvider初始化成功,返回false则表示失败
2)query()。从ContentProvider中查询数据。uri确定查询哪张表,projection确定查询那些列,selection和selectionArgs约束那些行,sortOrder对结果排序,查询结果存在Cursor对象中返回。
3)insert()。uri确定查询哪张表,待添加的数据保存在values参数中。完成添加后,返回一个表示这条新记录的URI
4)update()。uri确定查询哪张表,待添加的数据保存在values参数中,selection和selectionArgs约束那些行,受影响的行数作为返回值返回
5)delete()。uri确定查询哪张表,selection和selectionArgs约束那些行,被删除的行数作为返回值返回
6)getType()。根据传入的内容URI返回相应的MIME类型
标准URI格式写法content://com.example.app.provider/table1
还可以在URI后面加idcontent://com.example.app.provider/table1/1
一个能匹配任意表的内容URI可以写成content://com.example.app.provider/*
一个能匹配table1表中任意一行数据内容URI格式content://com.example.app.provider/#
getType()方法中,用于获取Uri对象所对应的MIMe类型。一个内容URI对应的MIME字符串主要有3部分组成,android对这3部分做了如下格式规定
1).必须以vnd开头。
2).如果内容URI以路径结尾,则后接android.cursor.dir/;如果内容URI以id结尾,则后接android.cursor.item/
3).最后接上vnd.<authority>.<path>
3.2实现跨程序数据共享
首先在DatabaseTest项目的基础上继续开发。右击 com.example.databasetest包->New->Other->Content Provider.
将 ContentProvider 命名 DatabaseProvider , 将authority 指定为com.example.databasetest.provider。
class DatabasesProvider : ContentProvider() {
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 : MyDatabaseHelper? = null
private val uriMatcher 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 deletedRows = when (uriMatcher.match(uri)){
bookDir -> db.delete("Book",selection,selectionArgs)
bookItem -> {
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 -> 0
}
deletedRows
} ?:0
override fun getType(uri: Uri) = when (uriMatcher.match(uri)) {
bookDir -> "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book"
bookItem -> "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book"
categoryDir ->"vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category"
categoryItem -> "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category"
else -> null
}
override fun insert(uri: Uri, values: ContentValues?) = dbHelper?.let{
//添加数据
val db = it.writableDatabase
val uriReturn = when(uriMatcher.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 =MyDatabaseHelper(it,"BookStore.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(uriMatcher.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("Caregory",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 updatedRows =when(uriMatcher.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 -> 0
}
updatedRows
} ?:0
}
创建新项目ProviderTest
class MainActivity : AppCompatActivity() {
var bookId : String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addData.setOnClickListener {
//添加数据
val uri = Uri.parse("content://com.example.databasetest.provider/book")
val values =contentValuesOf("name" to "A Clash of Kings","author" to "George Martin","pages" to 1040 ,"price" to 33.85)
val newUri =contentResolver.insert(uri,values)
bookId = newUri?.pathSegments?.get(1)
}
queryData.setOnClickListener {
//查询数据
val uri =Uri.parse("content://com.example.databasetest.provider/book")
contentResolver.query(uri,null,null,null,null)?.apply {
while(moveToNext()){
val name = getString(getColumnIndex("name"))
val author = getString(getColumnIndex("author"))
val pages = getInt(getColumnIndex("pages"))
Log.d("MainActivity","书名是 $name")
Log.d("MainActivity","作者是 $author")
Log.d("MainActivity","有 $pages 页")
}
close()
}
}
updateData.setOnClickListener {
//更新数据
bookId?.let {
val uri = Uri.parse("content://com.example.databasetest.provider/book/$it")
val values = contentValuesOf("name" to "A Storm of Swords","pages" to 1216)
contentResolver.update(uri,values,null,null)
}
}
deleteData.setOnClickListener {
//删除数据
bookId?.let {
val uri = Uri.parse("content://com.example.databasetest.provider/book/$it")
contentResolver.delete(uri,null,null)
}
}
}
}
4.Kotlin
4.1泛型基本用法
4.1.1定义一个泛型类
class MyClass<T> {
fun method(param : T) : T{
return param
}
}
调用方式
val myClass = MyClass<Int>()
val result = myClass.method(123)
4.1.2定义一个泛型方法
class MyClass {
fun <T> method (param : T) : T{
return param
}
}
调用方式
val myClass = MyClass()
val result = myClass.method<Int>(123)
由于kotlin有出色的类推导机制,这里可以简化成
val myClass = MyClass()
val result = myClass.method(123)
4.1.3Kotlin还允许对泛型的类型进行限制,可以将method方法的泛型指定成任意类型。
class MyClass {
fun <T : Number> method (param : T) : T{
return param
}
}
在默认情况下,所有泛型可指定可空类型。泛型上界默认是Any?。如果想让泛型的类型不可为空,只需要将泛型的上界手动指定为Any就可以了
4.1.4泛型实例
新建一个build.kt文件,并编写如下代码:
fun <T> T.build(block : T.() -> Unit) : T{
block()
return this
}
完成后就可以像使用apply函数一样去使用build
4.2类委托和委托属性
委托使用的关键字:by
4.2.1类委托
class MySet<T>(val helperSet : HashSet<T>) : Set<T> by helperSet{
}
借助委托的功能之后,只需要单独重写那一方法就可以了。如:
class MySet<T> (val helperSet : HashSet<T>) : Set<T> by helperSet {
fun helloWorld() = println("Hello World")
override fun isEmpty() = false
} ```
4.2.2属性委托
语法结构
class MyClass{
var p by Delegate()
}
这里使用by连接左边的p属性和右边Delegate实例
当调用p属性的时候会自动调用Delegate类的getValue()方法,当给p属性赋值的时候会自动调用Delegate类的setValue()的方法
同时我们还得对Delegate类进行具体操作
class Delegate {
var propValue : Any? = null
operator fun getValue(myClass : MyClass.prop : KProperty<*>) : Any?{
return propValue
}
operator fun settValue(myClass : MyClass.prop : KProperty<*>,vlalue : Any?) {
propValue = value
}
}
getValue()方法需要接收两个参数:第一个用于声明该Delegate类的委托可能在什么类中使用,这里写成MyClass表示仅可在MyClass类中使用;第二个参数KProperty<>是Kotlin中的一个属性操作类,可用于获取各种属性相关的值。<>这种泛型的写法表示你不知道或者不关心的泛型的具体类型。类似于java中<?>的写法。
setValue()方法啊相似。
4.2.3实现一个自己的lazy函数
工作原理解密
val p by lazy{ ...}
新建一个Later.kt文件
class Later<T>(val block : () -> T){
var value:Any? = null
operator fun getValue(any : Any?,prop : KProperty<*>) : T{
if ( value == null) {
value = block()
}
return value as T
}
}
为了是它的用法更加类似于lazy函数,最好再定义一个顶层函数
fun <T> later(block : () -> T) =Later(T)