问题背景
我们在安卓日常开发和学习过程中,contentProvider是个很常见的概念,毕竟是四大组件之一。不过可能工作和学习过程中涉及到contentProvider的场景有时候不是很多。本文先初步介绍下contentProvider的基本使用。
问题分析
话不多说,直接上代码,代码主要可以分为两大部分,一个是内容提供者,提供contentProvider给其他客户端访问,一个是内容访问者,通过访问第三方APP提供的contentProvider,去对第三方数据进行增删改查等各类操作。
一、contentProvider提供方
(1)借助SQLiteOpenHelper,新建数据库管理类,代码如下:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.Nullable;
/**
* 数据库管理类,创建和管理数据库
*
* @author baorant
*/
public class MySqlHelper extends SQLiteOpenHelper {
public MySqlHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
// 创建student数据表
sqLiteDatabase.execSQL("create table student ("+
"id integer primary key autoincrement,name varchar,age integer)");
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
(2)新建数据库增删改查具体操作类,暂时先实现查询和插入操作,代码如下:
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
/**
* 数据库操作类,增删改查操作
*
* @author baorant
*/
public class MyDataBase {
private Context context;
private SQLiteDatabase database;
private static String TAG = "TestResolver";
public MyDataBase(Context context){
this.context = context;
MySqlHelper dbHelper = new MySqlHelper(context,"db",null,1);
database = dbHelper.getWritableDatabase();
}
/**
* 数据插入操作
*/
public Uri insertDao(ContentValues contentValues){
Log.d(TAG, contentValues.toString());
long rowid = database.insert("student",null, contentValues);
Uri uri = Uri.parse("content://com.baorant.provider.MyContentProvider/student");
Uri inserturi = ContentUris.withAppendedId(uri,rowid);
context.getContentResolver().notifyChange(inserturi,null);
return inserturi;
}
/**
* 数据查询操作
*/
public Cursor queryDao(String[] whichone, String selection, String[] selectionArgs, String sortOrder) {
return database.query("student",whichone,selection,selectionArgs, null,null,sortOrder);
}
}
(3)新建自定义的ContentProvider类,代码如下:
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* MyContentProvider 自定义内容提供者类
*
* @author baorant
*/
public class MyContentProvider extends ContentProvider {
private MyDataBase myDataBase;
@Override
public String getType(Uri uri) {
return "1";
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return myDataBase.insertDao(values);
}
@Override
public boolean onCreate() {
myDataBase =new MyDataBase(this.getContext());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return myDataBase.queryDao(projection,selection,selectionArgs,sortOrder);
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
(4)manifest文件中配置自定义的contentProvider类,代码如下:
<provider
android:name=".MyContentProvider"
android:authorities="com.baorant.provider.MyContentProvider"
android:enabled="true"
android:exported="true">
</provider>
到此为止,contentProvider内容提供方的代码基本OK,我们下面一起看下内容访问者APP的代码。
二、contentResolver数据访问方
(1)manifest文件中对要访问的APP的包名进行一个query权限的配置,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<queries>
<package android:name="com.baorant.providerapplication"/>
</queries>
...
(2)新建activity执行contentProvider的访问操作,代码如下:
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
/**
* @author baorant
*/
public class MainActivity extends AppCompatActivity {
private final StringBuffer stringBuffer = new StringBuffer();
Button resovlerQueryBtn;
Button resovlerInsertBtn;
TextView resovlerText;
final int[] count = {0};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resovlerQueryBtn = findViewById(R.id.queryBtn);
resovlerInsertBtn = findViewById(R.id.insertBtn);
resovlerText = findViewById(R.id.text);
ContentResolver resolver = getContentResolver();
ContentValues values = new ContentValues();
values.put("name", "sb");
values.put("age", "20");
Uri uri = Uri.parse("content://com.baorant.provider.MyContentProvider/student");
resovlerQueryBtn.setOnClickListener(v -> {
Cursor cursor = resolver.query(uri, null, null, null, null);
// 清除之前的数据
stringBuffer.delete(0, stringBuffer.toString().length());
while (cursor.moveToNext()) {
@SuppressLint("Range") String name = cursor.getString(cursor.
getColumnIndex("name"));
@SuppressLint("Range") int age = cursor.getInt(cursor.
getColumnIndex("age"));
@SuppressLint("Range") int id = cursor.getInt(cursor.
getColumnIndex("id"));
Log.d("db", "id=" + id + "|name=" + name + "|age=" + age);
stringBuffer.append("id=").append(id).append("|name=").append(name).append("|age=").append(age);
}
resovlerText.setText(stringBuffer.toString());
}
);
ContentValues contentValues = new ContentValues();
resovlerInsertBtn.setOnClickListener(v -> {
// 先清空之前已经插入的数据
contentValues.clear();
contentValues.put("name", "name" + count[0]);
contentValues.put("age", count[0]);
count[0]++;
resolver.insert(uri, contentValues);
});
}
}
(3)activity对应的layout布局文件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<TextView
android:id="@+id/text"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="resolver text!"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/queryBtn"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="查询结果!"
app:layout_constraintTop_toBottomOf="@id/text" />
<Button
android:id="@+id/insertBtn"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="插入一条数据!"
app:layout_constraintTop_toBottomOf="@id/queryBtn" />
</androidx.constraintlayout.widget.ConstraintLayout>
到此,内容提供方APP和内容访问方APP的代码基本OK,现在先运行内容提供方APP的代码,让内容提供方APP先跑起来。然后运行内容访问方APP的代码。内容访问方页面提供了两个按钮,一个用来查询,一个用来插入数,还有一个textView显示内容查询结果。我们点击几次插入按钮后,再点击查询按钮进行结果的查询,运行结果如下所示:
问题总结
本文先初步介绍下contentProvider的基本使用,后面会继续介绍contentProvider的源码执行逻辑以及应用启动过程contentProvider的执行流程,有兴趣的同学可以进一步深入研究。