移动开发笔记(十) 跨程序共享数据 运行时权限 Kotlin

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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一天发火两次

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值