简介
ContentProvider 是 Android 的四大组件之一,有时候我们需要操作其他应用程序的一些数据,就会用到 ContentProvider,ContentProvider 本质上是一个中间者,真正 存储和操作数据 的数据源还是原来存储数据的方式,如数据库、文件或网络等。
ContentProvider 以相对安全的方式封装了数据并提供简易的处理机制和统一的访问接口供其他程序调用。它的底层采用了 Binder 机制来实现,ContentProvider 为应用间的数据交互提供了一个安全的环境:允许把自己的应用数据根据需求开放给 其他应用进行增删改查,而不用担心因为直接开放数据库权限而带来的安全问题。当然,ContentProvider不仅可以实现跨进程通信,也可实现进程内的通信。
在 Android 中,为一些常见的数据提供了默认的 ContentProvider,如通讯录等。
1.我们首先需要创建一个provider
这个uri填写你创建的那个文件夹的目录
代码演示
步骤一:创建数据库类,并建立一个student表
package com.example.provider;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Button;
import androidx.annotation.Nullable;
public class MyDBhelper extends SQLiteOpenHelper {
public MyDBhelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context,name ,null, version);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL("create table student(" +
"id integer primary key autoincrement," +
"name varchar(20)," +
"age interger)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
步骤二:创建一个 MyContentProvider ,继承自 ContentProvider 抽象类。其中,在 onCreate() 方法中,先对数据库初始化,并往数据库中的student表中添加两条数据。
package com.example.provider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
public class MyContentProvider extends ContentProvider {
public MyContentProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
System.out.println(uri+"====insert方法被调用====");
System.out.println("values参数为:"+values);
return null;
}
@Override
public boolean onCreate() {
System.out.println("====onCreate方法被调用====");
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
System.out.println(uri+"===query方法被调用===");
System.out.println("where参数为:"+"where");
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
System.out.println(uri+"===update方法被调用===");
System.out.println("where参数为:“where+”,values参数为:"+values);
return 0;
}
}
步骤三:注册(一般建立的时候会自动注册,不过还是最好瞅一下有没有)
<provider
android:name=".MyContentProvider"
android:authorities="com.example.provider"
android:enabled="true"
android:exported="true" >
</provider>
步骤四:进程内使用 ContentResolver 操作 ContentProvider。
private fun operateData() {
val student = Uri.parse("content://com.example.provider/student")
// 向 student 表插入一条数据
contentResolver.insert(userUri, ContentValues().apply {
put("id", 3)
put("name", "chen")
put("age","18")
})
// 查询 student 表数据
val cursor = contentResolver.query(userUri, arrayOf("id", "name","age"), null, null, null)
while (cursor!!.moveToNext()) {
println("query user:" + cursor.getInt(0) + " " + cursor.getString(1))
}
// 主动关闭游标
cursor.close()
}
上面是一个app的操作,下面是两个app的跨进程使用
步骤一:创建数据库类,建表
class DBHelper(
context: Context
) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
override fun onCreate(db: SQLiteDatabase) {
// 创建两个表格:用户表 和 职业表
db.execSQL("CREATE TABLE IF NOT EXISTS $USER_TABLE_NAME(_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)")
db.execSQL("CREATE TABLE IF NOT EXISTS $JOB_TABLE_NAME(_id INTEGER PRIMARY KEY AUTOINCREMENT, job TEXT)")
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {}
companion object {
// 数据库名
private const val DATABASE_NAME = "finch.db"
// 表名
const val USER_TABLE_NAME = "user"
const val JOB_TABLE_NAME = "job"
//数据库版本号
private const val DATABASE_VERSION = 1
}
}
步骤二:创建一个 MyContentProvider ,继承自 ContentProvider 抽象类。其中,在 onCreate() 方法中,先对数据库初始化,并往数据库中的 user 表和 job 表分别添加两条数据。
class MyContentProvider : ContentProvider() {
private lateinit var mDbHelper: DBHelper
private lateinit var db: SQLiteDatabase
private val mMatcher: UriMatcher by lazy {
val matcher = UriMatcher(UriMatcher.NO_MATCH)
matcher.addURI(AUTHORITY, "user", User_Code)
matcher.addURI(AUTHORITY, "job", Job_Code)
matcher
}
override fun onCreate(): Boolean {
mDbHelper = DBHelper(context!!)
db = mDbHelper.writableDatabase
// 初始化两个表的数据 (先清空两个表,再各加入一个记录)
db.execSQL("delete from user")
db.execSQL("insert into user values(1,'yang');")
db.execSQL("insert into user values(2,'zhang');")
db.execSQL("delete from job")
db.execSQL("insert into job values(1,'Android');")
db.execSQL("insert into job values(2,'iOS');")
return true
}
override fun insert(uri: Uri, values: ContentValues?): Uri {
val table = getTableName(uri)
db.insert(table, null, values)
// 通知外部调用者数据发生变化
context?.contentResolver?.notifyChange(uri, null)
return uri
}
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
): Cursor? {
val table = getTableName(uri)
return db.query(table, projection, selection, selectionArgs, null, null, sortOrder, 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
}
private fun getTableName(uri: Uri): String? {
var tableName: String? = null
when (mMatcher.match(uri)) {
User_Code -> tableName = DBHelper.USER_TABLE_NAME
Job_Code -> tableName = DBHelper.JOB_TABLE_NAME
}
return tableName
}
companion object {
private const val AUTHORITY = "com.yang.provider.myprovider"
const val User_Code = 1
const val Job_Code = 2
}
}
步骤三:在 androidManifest.xml 中注册 MyContentProvider
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xxx.xxxx">
<!--声明使用 MyContentProvider 的通信的权限 (开放所有权限) -->
<permission
android:name="com.yang.provider.myprovider.PROVIDER"
android:protectionLevel="normal" />
<!--声明使用 MyContentProvider 的通信的权限 (开放读权限) -->
<permission
android:name="com.yang.provider.myprovider.Read"
android:protectionLevel="normal" />
<!--声明使用 MyContentProvider 的通信的权限 (开放写权限) -->
<permission
android:name="com.yang.provider.myprovider.Write"
android:protectionLevel="normal" />
<application>
<!--通过 android:permission、readPermission、writePermission 等属性定义 MyContentProvider 的通信权限-->
<provider
android:name=".MyContentProvider"
android:authorities="com.yang.provider.myprovider"
android:exported="true"
android:permission="com.yang.provider.myprovider.PROVIDER"
// android:readPermission="com.yang.provider.myprovider.Read"
// android:writePermission="com.yang.provider.myprovider.Write"
/>
</application>
</manifest>
第二个app中
步骤一:声明访问进行 1 中的 ContentProvider 所需要的权限
<uses-permission android:name="com.yang.provider.myprovider.PROVIDER" />
步骤二:使用 ContentResolver 操作 进程 1 的 ContentProvider。
private fun operateData() {
val userUri = Uri.parse("content://com.yang.provider.myprovider/user")
val jobUri = Uri.parse("content://com.yang.provider.myprovider/job")
// 对 user 表进行操作
contentResolver.insert(userUri, ContentValues().apply {
put("_id", 4)
put("name", "huang")
})
val cursor = contentResolver.query(userUri, arrayOf("_id", "name"), null, null, null)
while (cursor!!.moveToNext()) {
println("ipc query user:" + cursor.getInt(0) + " " + cursor.getString(1))
}
cursor.close()
// 对 job 表进行操作
contentResolver.insert(jobUri, ContentValues().apply {
put("_id", 4)
put("job", "algorithm")
})
val cursor2 = contentResolver.query(jobUri, arrayOf("_id", "job"), null, null, null)
while (cursor2!!.moveToNext()) {
println("ipc query job:" + cursor2.getInt(0) + " " + cursor2.getString(1))
}
cursor2.close()
}
// 执行 operateData() 方法,输出结果如下:
I/System.out: ipc query user:1 yang
I/System.out: ipc query user:2 zhang
I/System.out: ipc query user:4 huang
I/System.out: ipc query job:1 Android
I/System.out: ipc query job:2 iOS
I/System.out: ipc query job:4 algorithm