Android四大组件之ContentProvider(一)

1.什么是ContentProvider?

内容提供程序管理对数据结构化数据集的访问。它们封装数据,并提供用于定义数据安全性的机制。内容提供者是连接一个进程中的数据与另一个进程中运行的代码的标准界面。
是不同应用程序之间进行数据交换的标准API,以某种Uri的形式对外提供数据,允许其他应用访问或修改数据;其他程序使用ContentResolver根据Uri去访问操作指定的数据。

是IPC通信的一种。

2.ContentProvider和ContentResolver
  1. 创建ContentProvider的派生类,重写必要的方法,方法有:(ContentResolver的方法与它类似)
  • onCreate():当其他程序第一个访问ContentProvider时回调此方法。
  • String getType(Uri uri):用于返回当前Uri所代表的MIME类型,如果包含多条数据则返回“vnd.android.cursor.dir/开头”,单条数据则返回“vnd.android.cursor.item/开头”,开头部分格式:vnd.<authority>.<path>
  • insert(Uri uri,ContentValue values):根据该uri插入对应的value值
  • update(Uri uri,…):根据uri修改对应参数条件的所有值
  • delete(Uri uri):根据uri删除符合参数条件的所有值
  • query(Uri uri):根据uri查询符合条件的所有值
  1. AndroidManifest.xml文件中为其注册
 <provider
            android:name=".contentProvider.MyContentProvider"
            android:authorities="org.wdl.book"
            android:enabled="true"
            android:exported="true"
           />
属性意义
android:name类名
android:authorities指定相应的域名
android:exported是否提供给外部程序使用
  1. 客户端程序通过ContentResolver的一系列方法调用ContentProvider提供的API进行数据的CRUD

相对应的流程如图:
在这里插入图片描述
ContentResolver.query()。 query() 方法会某某提供程序所定义的 ContentProvider.query() 方法。 以下代码行显示了 ContentResolver.query() 调用:(其他方法类似)

// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    mProjection,                        // The columns to return for each row
    mSelectionClause                    // Selection criteria
    mSelectionArgs,                     // Selection criteria
    mSortOrder);                        // The sort order for the returned rows

其中参数与之前SQLite存储提到的类似,详见](https://blog.csdn.net/qq_34341338/article/details/84069391)

由上图可以看出URI在这数据交互的过程中起到了关键性(进行数据交换的标识)的作用,因此,该URI所对应的要求如下:`

  1. content://开头,固定不变的
  2. 中间部分为之前xml文件中配置的android:authorities,固定不变的
  3. 第三部分代表资源部份。这个部分是动态改变,根据访问者的需求更改
  4. content://authorities/source/#,#代表通配符,可以为1,2,3等;content://authorities/*,*代表任意长度字符,表示所有表

分析:
例:
content://org.wdl.provider/books
意味访问books数据
content://org.wdl.provider/book/2
意味访问books数据中ID为2的数据
content://org.wdl.provider/book/2/name
意味访问books数据中ID为2的数据中的name字段

Uri工具类提供了parse静态方法,用于将字符串转为Uri

  1. ContentResolver调用方法中传递的Uri必须能够与ContentProvider中所暴露Uri的相匹配,否则会抛出异常。为了确保正常进行数据CRUD,Android提供了UriMatcher工具类:
    void addURI(String authority,String path,int code):向UriMatcher中注册Uri,authority+path构成Uri,code表示标识码,根据它来判断是否能够进行数据操作
    int match(Uri uri):根据注册的Uri判断指定Uri对应的标识码,匹配不到返回-1
    例:
		matcher = new UriMatcher(UriMatcher.NO_MATCH);
        matcher.addURI(Books.AUTHORITY, "books", MyContentProvider.BOOKS);
        matcher.addURI(Books.AUTHORITY, "book/#", MyContentProvider.BOOK);
		code = matcher.match(uri)

假如现在我有一个需求:
查询Books中id为2的数据,需对原始的Uri进行拼接,Android为此提供了另一个工具类ContentUris,主要方法如下:

  • withAppendedId(Uri uri,int id):在路径上拼接ID部分
  • parseId(Uri uri):解析出指定Uri所包含的ID值

例:

ContentUris.withAppendedId(Books.Book.BOOK_CONTENT_URI,1)
拼接后的uri为:content://org.wdl.provider/book/1
 long id = ContentUris.parseId(uri);
 id值为:1
3.详细使用

MyContentProvider.class

package com.wdl.crazyandroiddemo.contentProvider;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;

import com.wdl.crazyandroiddemo.MySQLiteHelper;

import java.util.Objects;

public class MyContentProvider extends ContentProvider {
    private MySQLiteHelper helper;
    private static UriMatcher matcher;
    private static final int BOOKS = 1;
    private static final int BOOK = 2;

    static {
        matcher = new UriMatcher(UriMatcher.NO_MATCH);
        matcher.addURI(Books.AUTHORITY, "books", MyContentProvider.BOOKS);
        matcher.addURI(Books.AUTHORITY, "book/#", MyContentProvider.BOOK);
    }

    public MyContentProvider() {
    }

    @Override
    public boolean onCreate() {
        Log.e("wdl", "----------onCreate----------");
        helper = new MySQLiteHelper(getContext(), "demo.db", null, 3);
        return true;
    }

    @Override
    public int delete(@NonNull Uri uri, String where, String[] whereArgs) {
        Log.e("wdl", "----------delete----------");
        SQLiteDatabase db = helper.getReadableDatabase();
        int num = 0;
        switch (matcher.match(uri)) {
            case BOOKS:
                num = db.delete("book", where, whereArgs);
                break;
            case BOOK:
                long id = ContentUris.parseId(uri);
                String whereClause = Books.Book.ID + "=" + id;
                if (!TextUtils.isEmpty(where)) {
                    whereClause = whereClause + " and " + where;
                }
                num = db.delete("book", whereClause, whereArgs);
                break;
            default:
                throw new IllegalArgumentException("未知uri");
        }
        Objects.requireNonNull(getContext()).getContentResolver().notifyChange(uri, null);
        return num;
    }

    @Override
    public String getType(@NonNull Uri uri) {
        Log.e("wdl", "----------getType----------");
        switch (matcher.match(uri)) {
            case BOOKS:
                return "vnd.android.cursor.dir/wdl.books";
            case BOOK:
                return "vnd.android.cursor.item/wdl.book";
            default:
                throw new IllegalArgumentException("未知uri");
        }

    }

    @Override
    public Uri insert(@NonNull Uri uri, ContentValues values) {
        Log.e("wdl", "----------insert----------");
        //获取数据库实例
        SQLiteDatabase db = helper.getReadableDatabase();
        switch (matcher.match(uri)) {
            case BOOKS:
                //插入
                long rowId = db.insert("book", Books.Book.ID, values);
                if (rowId > 0) {
                    //uri末尾添加id
                    Uri bookUri = ContentUris.withAppendedId(uri, rowId);
                    //通知数据已经改变
                    Objects.requireNonNull(getContext()).getContentResolver().notifyChange(bookUri, null);
                    return bookUri;
                }
                break;
            default:
                throw new IllegalArgumentException("Not yet implemented");
        }
        return null;
    }

    @Override
    public Cursor query(@NonNull Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        Log.e("wdl", "----------query----------");
        SQLiteDatabase db = helper.getReadableDatabase();
        switch (matcher.match(uri)) {
            case BOOKS:
                return db.query("book",
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null, sortOrder);
            case BOOK:
                long id = ContentUris.parseId(uri);
                String whereClause = Books.Book.ID + "=" + id;
                if (!TextUtils.isEmpty(selection)) {
                    whereClause = whereClause + " and " + selection;
                }
                return db.query("book", projection, whereClause, selectionArgs, null, null, sortOrder);
            default:
                throw new IllegalArgumentException("未知uri");
        }
    }

    @Override
    public int update(@NonNull Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        Log.e("wdl", "----------update----------");
        SQLiteDatabase db = helper.getReadableDatabase();
        int num = 0;
        switch (matcher.match(uri)) {
            case BOOKS:
                num = db.update("book", values, selection, selectionArgs);
                break;
            case BOOK:
                long id = ContentUris.parseId(uri);
                String whereClause = Books.Book.ID + "=" + id;
                if (!TextUtils.isEmpty(selection)) {
                    whereClause = whereClause + " and " + selection;
                }
                num = db.update("book", values, whereClause, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("未知uri");
        }
        Objects.requireNonNull(getContext()).getContentResolver().notifyChange(uri, null);
        return num;
    }


}

Books.class,此类封装了可访问Uri以及可访问的数据列:

package com.wdl.crazyandroiddemo.contentProvider;

import android.net.Uri;
import android.provider.BaseColumns;

/**
 * author:   wdl
 * time: 2018/11/17 13:43
 * des:    TODO
 */
@SuppressWarnings("unused")
public class Books {
    //定义contentProvider 的Authorities值
    public static final String AUTHORITY = "org.wdl.book";

    //定义静态内部类,定义contentProvider所包含的数据列
    public static final class Book implements BaseColumns {
        //定义所允许操作的三个数据列
        public final static String ID = "id";
        public final static String NAME = "name";
        public final static String PRICE = "price";
        public final static String DATE = "publishdate";

        //定义提供服务的两个Uri
        public final static Uri BOOKS_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/books");
        public final static Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
    }
}

在Client中调用:

package com.wdl.contentproviderclient

import android.content.ContentUris
import android.content.ContentValues
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.*
import java.lang.StringBuilder

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val contentResolver = contentResolver
        btnQuery.setOnClickListener {
            val intent = Intent("com.wdl.mime")
            intent.data = Books.Book.BOOKS_CONTENT_URI
            startActivity(intent)
            val cursor = contentResolver.query(Books.Book.BOOKS_CONTENT_URI
                    ,null,"price=?", arrayOf("50.2"),null)
            //cursor.close()
            val stringBuilder = StringBuilder()
            while (cursor.moveToNext()){
                val id = cursor.getInt(cursor.getColumnIndexOrThrow(Books.Book.ID))
                val name = cursor.getString(cursor.getColumnIndexOrThrow(Books.Book.NAME))
                val price = cursor.getInt(cursor.getColumnIndexOrThrow(Books.Book.PRICE))
                stringBuilder.append("id=$id,name=$name,price=$price \n")
            }
            cursor.close()
            Log.e("wdl",stringBuilder.toString())
        }
        btnDelete.setOnClickListener {
            //val index = contentResolver.delete(uri,null,null)
            val index = contentResolver.delete(Books.Book.BOOKS_CONTENT_URI,"price=?", arrayOf("55.2"))

            Log.e("wdl",""+index)
        }
        btnInsert.setOnClickListener {
            val value = ContentValues()
            value.put(Books.Book.NAME,"smwdl")
            value.put(Books.Book.PRICE,50.2)
            val uri = contentResolver.insert(Books.Book.BOOKS_CONTENT_URI,value)
            Log.e("wdl",uri.toString())
        }
        btnUpdate.setOnClickListener {
            val value = ContentValues()
            value.put(Books.Book.PRICE,55.2)
            val uri = ContentUris.withAppendedId(Books.Book.BOOK_CONTENT_URI,1)
            val index = contentResolver.update(uri
                    ,value,"name like ?", arrayOf("smwdl"))
            Log.e("wdl",""+index)
        }
    }
}

注意事项:

  1. 在MyContentPrivoder中实现的CRUD方法,都需要通过uri判断根据match返回值来执行对应的方法。例如:
switch (matcher.match(uri)) {
            case BOOKS:
                return db.query("book",
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null, sortOrder);
            case BOOK:
                long id = ContentUris.parseId(uri);
                String whereClause = Books.Book.ID + "=" + id;
                if (!TextUtils.isEmpty(selection)) {
                    whereClause = whereClause + " and " + selection;
                }
                return db.query("book", projection, whereClause, selectionArgs, null, null, sortOrder);
            default:
                throw new IllegalArgumentException("未知uri");
        }

上段代码中,对匹配码做出了判断,并进行区分处理:Books代表对所有数据进行操作,Book代表对单项数据进行操作。而单项数据进行操作的化需要进行SQL语句的拼接。通常为添加ID。

  1. 在static静态代码块中需进行UriMatcher的注册
  2. 权限声明,在清单文件中配置相对应的读写权限。系统自带的ContentProvider需进行其他权限的声明。在6.0之后需动态的申请所需权限,否则无法进行数据的CRUD
  3. 在insert,update,delete之后都必须进行getContext().getContentResolver().notifyChange(uri, null)进行数据改变的响应
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值