安卓开发四大组件——contentProvider基本使用介绍(1)

时间:2023-01-24 00:40:41

问题背景

我们在安卓日常开发和学习过程中,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基本使用介绍(1)

问题总结

本文先初步介绍下contentProvider的基本使用,后面会继续介绍contentProvider的源码执行逻辑以及应用启动过程contentProvider的执行流程,有兴趣的同学可以进一步深入研究。