跨程序共享数据

ContentProvider

ContentProvider主要用于在不同的应用程序之间实现数据共享的功能。ContentProvider可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。

运行时权限

Android现在将常用的权限大致归成了两类,一类是普通权限,一类是危险权限。

  • 普通权限指的是那些不会直接威胁到用户的安全和隐私的权限,对于这部分权限申请,系统会自动帮我们进行授权,不需要用户手动操作。
  • 危险权限则表示那些可能会触及用户隐私或者对设备安全性造成影响的权限,如获取设备联系人信息、定位设备的地理位置等,对于这部分权限申请,必须由用户手动授权才可以。

QQ_1737172894800

  1. 第一步先判断用户是不是已经给过我们授权了,借助的是ContextCompat.checkSelfPermission()方法;
  2. 如果没有授权的话,则需要调用ActivityCompat.requestPermissions()方法向用户申请授权;
  3. 重写回调onRequestPermissionsResult()函数,而授权的结果则会封装在grantResults参数当中。这里需要判断一下最后的授权结果写逻辑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
1 -> {
if (grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
call()
} else {
Toast.makeText(this, "You denied the permission",
Toast.LENGTH_SHORT).show()
}
}
}
}

private fun call() {
try {
val intent = Intent(Intent.ACTION_CALL)
intent.data = Uri.parse("tel:10086")
startActivity(intent)
} catch (e: SecurityException) {
e.printStackTrace()
}
}

}

访问其他程序中的数据

ContentProvider的用法一般有两种:一种是使用现有的ContentProvider读取和操作相应程序中的数据;另一种是创建自己的ContentProvider,给程序的数据提供外部访问接口。

如果想要访问ContentProvider中共享的数据,就一定要借助ContentResolver类,可以通过Context中的getContentResolver()方法获取该类的实例。ContentResolver中提供了一系列的方法用于对数据进行增删改查操作,其中insert()方法用于添加数据,update()方法用于更新数据,delete()方法用于删除数据,query()方法用于查询数据。

不同于SQLiteDatabase,ContentResolver中的增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI。内容URI给ContentProvider中的数据建立了唯一标识符,它主要由两部分组成:authority和path。我们还需要在内容URI字符串的头部加上协议声明content。

  • authority是用于对不同的应用程序做区分的,一般为了避免冲突,会采用应用包名的方式进行命名。
  • path则是用于对同一应用程序中不同的表做区分的,通常会添加到authority的后面。
1
2
content://com.example.app.provider/table1 
content://com.example.app.provider/table2

创建自己的ContentProvider

可以通过新建一个类去继承ContentProvider的方式来实现。ContentProvider类中有6个抽象方法,我们在使用子类继承它的时候,需要将这6个方法全部重写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyProvider : ContentProvider() { 
override fun onCreate(): Boolean {
return false
}
override fun query(uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
return null
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return null
}

override fun update(uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?): Int {
return 0
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
return 0
}
override fun getType(uri: Uri): String? {
return null
}
}

借助UriMatcher这个类就可以轻松地实现匹配内容URI的功能。

getType()方法。它是所有的ContentProvider都必须提供的一个方法,用于获取Uri对象所对应的MIME类型

MIME字符串主要由3部分组成,Android对这3个部分做了如下格式规定

  • 必须以vnd开头。
  • 如果内容URI以路径结尾,则后接android.cursor.dir/;如果内容URI以id结尾,则后接android.cursor.item/。
  • 最后接上vnd.<authority>.<path>。

注意,自己提供ContentProvider需要使用<provider>进行注册。


Kotlin课堂

泛型

泛型允许我们在不指定具体类型的情况下进行编程,这样编写出来的代码将会拥有更好的扩展性。

泛型主要有两种定义方式:一种是定义泛型类,另一种是定义泛型方法。

1
2
3
4
5
6
7
8
9
10
11
class MyClass<T> { 
fun method(param: T): T {
return param
}
}

class MyClass {
fun <T> method(param: T): T {
return param
}
}

Kotlin还拥有非常出色的类型推导机制,可以在函数内部进行推导,也可以根据传参进行推导,常常可以省略指定泛型类型。

Kotlin还允许我们对泛型的类型进行限制。比如将method()方法的泛型上界设置为Number类型,只能将method()方法的泛型指定成数字类型,比如Int、Float、Double等。但是如果你指定成字符串类型,就肯定会报错。

1
2
3
4
5
6
7
class MyClass { 

fun <T : Number> method(param: T): T {
return param
}

}

委托

委托是一种设计模式,它的基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。

Kotlin中支持委托功能的,并且将委托功能分为了两种:类委托和委托属性

类委托

类委托的核心思想是将一个类的具体实现委托给另一个类去完成。

我们让大部分的方法实现调用辅助对象中的方法,少部分的方法实现由自己来重写,甚至加入一些自己独有的方法,这就是委托模式的意义所在。(在一定程度上拓展函数也可以办到?)

Kotlin中委托使用的关键字是by:

1
2
class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet { 
}

属性委托

委托属性的核心思想是将一个属性(字段)的具体实现委托给另一个去完成。

注意都是委托给类完成!!!

1
2
3
class MyClass { 
var p by Delegate()
}

使用by关键字连接了左边的p属性和右边的Delegate实例,这种写法就代表着将p属性的具体实现委托给了Delegate类去完成。当调用p属性的时候会自动调用Delegate类的getValue()方法,当给p属性赋值的时候会自动调用Delegate类的setValue()方法。

在Delegate类中我们必须实现getValue()和setValue()这两个方法,并且都要使用operator关键字进行声明。

懒加载解密

对by lazy的工作原理进行解密了,它的基本语法结构如下:

1
val p by lazy { ... } 

lazy在这里只是一个高阶函数而已。在lazy函数中会创建并返回一个Delegate对象,当我们调用p属性的时候,其实调用的是Delegate对象的getValue()方法,因此只有在被调用时候才会加载。