欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android学习笔记:ContentProvider

程序员文章站 2022-07-14 20:54:27
...


一、内容提供器介绍

内容提供器(ContentProvider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。
内容提供器可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险


二、内容提供器构成

1.使用方法

  1. 使用现有的内容提供器来读取和操作相应程序中的数据
  2. 创建自己的内容提供器给我们程序的数据提供外部访问接口

2.ContentResolver

程序通过ContentResolver类来访问ContentProvider*享的数据,通过Context中getContentResolver()方法来获取该类的实例

  • insert() 用于添加数据
  • update() 用于更新数据
  • delete() 用于删除数据
  • query() 用于查询数据

3.URI

  • authority
    用于对不同的应用程序作区分,一般为了避免冲突,都会采用程序包名的方式来进行命名
  • path
    用于对同一应用程序中不同的表作区分,通常添加到authority后面

内容URI标准格式写法"content://" + authority + path:
content://com.example.app.provider/table1
content://com.example.app.provider/table2/1
以路径结尾就表示期望访问表中所有数据,id结尾则表示访问表中拥有相应id的数据

有了URI后还需要将其解析为Uri对象

Uri uri = Uri.parse("content://com.example.app.provider/table1")

我们也可以用通配符来匹配内容URI
*:表示匹配任意长度的任意字符
#:表示匹配任意长度的数字

我们可以借助UriMatcher来匹配内容URI功能(映射)

    public static final String AUTHORITY = "com.example.savetest.provider";
    
    public static final int BOOK_DIR = 0;
    public static final int BOOK_ITEM = 1;
    public static final int CATEGORY_DIR = 2;
    public static final int CATEGORY_ITEM = 3;
    
    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
        uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
        uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
        uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }
    
    ...

    public Cursor query(Uri uri, ...) {
        switch ((uriMatcher.match(uri))){
            case BOOK_DIR:
                // 查询Book表所有数据
                break;
            case BOOK_ITEM:
                // 查询Book表单条数据
                break;
            case CATEGORY_DIR:
                // 查询Category所有数据
                break;
            case CATEGORY_ITEM:
                // 查询Category单条数据
                break;
            default:
                break;
        }
        ...
    }

4.MIME格式

getType()方法用于获取Uri对象所对应的MIME类型,其中MIME格式有如下组成:

  1. 以vnd开头
  2. 如果URI以路径结尾,则接android.cursor.dir/ 如果以id结尾,则接 android.cursor.item/
  3. 最后接上vnd.< authority >.< path >
content://com.example.app.provider/table
对应MIME:vnd.android.cursor.dir/vnd.com.example.app.provider.table

5.ContentProvider

我们可以通过新建一个类去继承ContentProvider的方式来创建一个自己的内容提供器,我们需要重写以下6个方法

  • onCreate()
    初始化时调用,返回true表示初始化成功,返回false表示失败
  • query()
    查询数据,uri表示查询哪张表,projection表示查询哪些列,selection和selectionArgs用于约束查询哪些行,sortOrder用于对结果排序,查询结果存放在Cursor对象中返回
  • insert()
    添加数据,uri确定添加到哪张表,values用于保存待添加的数据。添加完成后,返回一个表示该数据的URI
  • update()
    更新已有的数据。uri确定更新的表,values保存新数据,selection和selectionArgs用于约束更新哪些行,受影响的行数作为返回值返回
  • delete()
    删除数据。uri确定删除的表,selection和selectionArgs用于约束删除哪些行,被删除的行数作为返回值返回
  • getType()
    根据传输的内容URI来返回相应的MIME类型

三、具体实现

ContentProvider:

package com.example.savetest;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

public class MyContentProvider extends ContentProvider {
    public static final int BOOK_DIR = 0;
    public static final int BOOK_ITEM = 1;
    public static final int CATEGORY_DIR = 2;
    public static final int CATEGORY_ITEM = 3;

    public static final String AUTHORITY = "com.example.savetest.provider";

    private static UriMatcher uriMatcher;

    private MyDatabaseHelper dbHelper;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
        uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
        uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
        uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }

    public MyContentProvider() {

    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {

        SQLiteDatabase db = dbHelper.getReadableDatabase();
        int deletedRows = 0;
        switch ((uriMatcher.match(uri))){
            case BOOK_DIR:
                deletedRows = db.delete("Book", selection, selectionArgs);
                break;
            case BOOK_ITEM:
                deletedRows = db.delete("Book", "id = ?", selectionArgs);
                break;
            case CATEGORY_DIR:
                deletedRows = db.delete("Category", selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                deletedRows = db.delete("Category", "id = ?", selectionArgs);
                break;
            default:
                break;
        }
        return deletedRows;
    }

    @Override
    public String getType(Uri uri) {
        switch ((uriMatcher.match(uri))){
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.savetest.provider.Book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.savetest.provider.Book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.savetest.provider.Category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.savetest.provider.Category";
            default:
                break;
        }
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Uri uriReturn = null;
        switch ((uriMatcher.match(uri))){
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("Book", null, values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long newCategoryId = db.insert("CategoryId", null, values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newCategoryId);
                break;
            default:
                break;
        }
        return uriReturn;
    }

    @Override
    public boolean onCreate() {
        dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 1);
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, 
                        String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = null;
        switch ((uriMatcher.match(uri))){
            case BOOK_DIR:
                cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case BOOK_ITEM:
                cursor = db.query("Book", projection, "id = ?", selectionArgs, null, null, sortOrder);
                break;
            case CATEGORY_DIR:
                cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case CATEGORY_ITEM:
                cursor = db.query("Category", projection, "id = ?", selectionArgs, null, null, sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    @Override
    public int update(Uri uri, ContentValues values, 
                      String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        int updateRows = 0;
        switch ((uriMatcher.match(uri))){
            case BOOK_DIR:
                updateRows = db.update("Book", values, selection, selectionArgs);
                break;
            case BOOK_ITEM:
                updateRows = db.update("Book", values, "id = ?", selectionArgs);
                break;
            case CATEGORY_DIR:
                updateRows = db.update("Category", values, selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                updateRows = db.update("Book", values, "id = ?", selectionArgs);
                break;
            default:
                break;
        }
        return updateRows;
    }
}

DatabaseHelper:

package com.example.savetest;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;

public class MyDatabaseHelper extends SQLiteOpenHelper {
    public static final String CREATE_BOOK = "create table Book (" +
            "id integer primary key autoincrement, " +
            "author text, " +
            "price real, " +
            "pages integer, " +
            "name text)";

    public static final String CREATE_CATEGORY = "create table Category (" +
            "id integer primary key autoincrement, " +
            "category_name text, " +
            "category_code integer)";

    private Context mContext;

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version){
        super(context, name, factory, version);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
        Toast.makeText(mContext, "创建数据库表", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

MainActivity:

package com.example.savetest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private MyDatabaseHelper dbHelper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        inputText = (EditText) findViewById(R.id.input_text);
        showText = (TextView) findViewById(R.id.show_text);

        // 创建数据库表
        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
                sqlSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLInsertData();
            }
        });
        sqlLoad.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SQLSelectData();
            }
        });
    }
        // 使用SQL添加数据
    public void SQLInsertData(){
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("name", "The Da Vinci Code");
        values.put("author", "Dan Brown");
        values.put("pages", 454);
        values.put("price", 16.96);
        db.insert("Book", null, values);
        values.clear();

        values.put("name", "The Lost Symbol");
        values.put("author", "Dan Brown");
        values.put("pages", 510);
        values.put("price", 19.95);
        db.insert("Book", null, values);
    }

    // 使用SQL读取数据
    public void SQLSelectData(){
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Cursor cursor = db.query("Book", null, null, null, null, null, null);
        Log.d(TAG, "SQLSelectData: ");
        if (cursor.moveToFirst()){
            do {
                // 遍历Cursor对象,取出所有数据打印
                String name = cursor.getString(cursor.getColumnIndex("name"));
                String author = cursor.getString(cursor.getColumnIndex("author"));
                int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                double price = cursor.getDouble(cursor.getColumnIndex("price"));
                Log.d(TAG, "name: " + name);
                Log.d(TAG, "author: " + author);
                Log.d(TAG, "pages: " + pages);
                Log.d(TAG, "price: " + price);
            } while(cursor.moveToNext());
        }
        cursor.close();
    }
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.savetest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <provider
            android:name=".MyContentProvider"
            android:authorities="com.example.savetest.provider"
            android:enabled="true"
            android:exported="true"></provider>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

四、个人理解

  1. 将数据private化,让自己成为getter()、setter(),提供ContentResolver的CRUD方法给其他程序来读取数据
  2. 其他程序通过Uri访问数据库,本质是ContentResolver提供方法,然后通过ContentProvider去访问数据库