Android开发之ContentProvider的简单使用

时间:2022-06-23 00:45:54

ContentProvider,内容提供者

官网结构图:

Android开发之ContentProvider的简单使用

作为四大组件之一的ContentProvider,主要是用于应用间数据共享使用的。

ContentProvider把应用的数据封装起来,然后提供一个对外访问的ContentResolver接口。

假如只是本应用使用的数据,直接通过SQLiteDatabase操作数据库。

主要原理:一个Uri对象通过ContentResolver请求该应用的数据,ContentProvider解析该Uri并返回给需要的数据。

实现自定义的ContentProvider,需要继承抽象类android.content.ContentProvider,并实现5个方法:

onCreate()    初始化provider,返回一个布尔值,创建成功返回一个true

query(Uri,String[],String,String[],String)   从ContentProvider中查询数据  返回一个Cursor

insert(Uri,ContentValues)  通过ContentProvider插入新的数据   返回一个Uri

update(Uri,ContentValues,String,String[])   通过ContentProvider更新数据   返回一个int

delete(Uri,String,String[])   通过ContentProvider删除数据       返回一个int

getType(Uri)   根据传入的Uri,返回相应的MIME类型。  返回一个String

Uri类

一个Uri有三部分组成:Scheme+Authority+Path

1.scheme, contentprovider为content://

2.Authority,一般使用应用的包名,如com.example.appone或者也可以这样com.example.appone.provider

3.Path,即提供对外访问的table,如访问表table,写成/table

上面的Uri可以写成这样:content://com.example.appone.provider/table

一个字符串通过Uri类中的parse()方法可以转换为Uri对象,如下:

 Uri uri=Uri.parse("content://com.example.appone.provider/table");

UriMatcher类,ContentUris类contentResolver类

UriMather类用于匹配uri,用法如下:

     private static final int PEOPLE = 1;
private static final int PEOPLE_ID = 2;
private static final int PEOPLE_PHONES = 3;
private static final int PEOPLE_PHONES_ID = 4;
private static final int PEOPLE_CONTACTMETHODS = 7;
private static final int PEOPLE_CONTACTMETHODS_ID = 8; private static final int DELETED_PEOPLE = 20; private static final int PHONES = 9;
private static final int PHONES_ID = 10;
private static final int PHONES_FILTER = 14; private static final int CONTACTMETHODS = 18;
private static final int CONTACTMETHODS_ID = 19; private static final int CALLS = 11;
private static final int CALLS_ID = 12;
private static final int CALLS_FILTER = 15; private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1) static
{
sURIMatcher.addURI("contacts", "people", PEOPLE); //如果match()方法匹配,返回匹配码为1
sURIMatcher.addURI("contacts", "people/#", PEOPLE_ID); //如果match()方法匹配,返回匹配码为2
sURIMatcher.addURI("contacts", "people/#/phones", PEOPLE_PHONES);
sURIMatcher.addURI("contacts", "people/#/phones/#", PEOPLE_PHONES_ID);
sURIMatcher.addURI("contacts", "people/#/contact_methods", PEOPLE_CONTACTMETHODS);
sURIMatcher.addURI("contacts", "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
sURIMatcher.addURI("contacts", "deleted_people", DELETED_PEOPLE);
sURIMatcher.addURI("contacts", "phones", PHONES);
sURIMatcher.addURI("contacts", "phones/filter/*", PHONES_FILTER);
sURIMatcher.addURI("contacts", "phones/#", PHONES_ID);
sURIMatcher.addURI("contacts", "contact_methods", CONTACTMETHODS);
sURIMatcher.addURI("contacts", "contact_methods/#", CONTACTMETHODS_ID);
sURIMatcher.addURI("call_log", "calls", CALLS);
sURIMatcher.addURI("call_log", "calls/filter/*", CALLS_FILTER);
sURIMatcher.addURI("call_log", "calls/#", CALLS_ID);
}

从API:18开始,路径可以用一个斜线开始。 例如:

 sURIMatcher.addURI("contacts", "/people", PEOPLE);

注册完需要匹配的Uri后,在getType()方法中可以使用sURIMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配成功就返回匹配码,匹配码是调用addURI()方法传入的第三个参数。

     public String getType(Uri url)
{
int match = sURIMatcher.match(url);
switch (match)
{
case PEOPLE:
return "vnd.android.cursor.dir/person";
case PEOPLE_ID:
return "vnd.android.cursor.item/person";
... snip ...
return "vnd.android.cursor.dir/snail-mail";
case PEOPLE_ADDRESS_ID:
return "vnd.android.cursor.item/snail-mail";
default:
return null;
}
}

ContentUris类,用于获取Uri路径后面的ID部分,它有两个比较实用的方法:

withAppendedId(uri, id)用于为路径加上ID部分

 Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person")
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成后的Uri为:content://com.ljq.provider.personprovider/person/10

parseId(uri)方法用于从路径中获取ID部分

 Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person/10")
long personid = ContentUris.parseId(uri);//获取的结果为:10

ContentResolver类,当外部应用需要对ContentProvider的数据库进行CRUD时,可以使用ContentResolver类来完成。获取ContentResolver对象,可以通过Content的getContentResolver()方法获取到。ContentResolver使用insert()、delete()、update()、query()方法,来操作数据。

实现了一个Demo,代码如下:

1.工具类:Xin类

 package com.example.appone;

 public class Xin {

     public static final String DBNAME = "xinonlinedb";
public static final String TNAME = "xinonline";
public static final int VERSION = 3; public static String TID = "tid";
public static final String EMAIL = "email";
public static final String USERNAME = "username";
public static final String DATE = "date";
public static final String SEX = "sex"; public static final String AUTOHORITY = "com.example.appone.provider";
public static final int ITEM = 1;
public static final int ITEM_ID = 2; public static final String CONTENT_TYPE = "vnd.android.cursor.dir";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item"; }

2.创建数据库:DBlite类

 package com.example.appone;

 import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper; public class DBlite extends SQLiteOpenHelper { public DBlite(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version); } @Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Xin.TNAME + "(" + Xin.TID
+ " integer primary key autoincrement not null," + Xin.EMAIL
+ " text not null, " + Xin.USERNAME + " text not null,"
+ Xin.DATE + " text not null, " + Xin.SEX + " text not null);");
} @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }

3.创建自定义的ContentProvider,对数据库进行包装:MyProvider类

 package com.example.appone;

 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 MyProvider extends ContentProvider { private DBlite dBlite;
private SQLiteDatabase db;
private Cursor cursor; private static final UriMatcher sMatcher;
static {
sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sMatcher.addURI(Xin.AUTOHORITY, Xin.TNAME, Xin.ITEM);
sMatcher.addURI(Xin.AUTOHORITY, Xin.TNAME + "/#", Xin.ITEM_ID); } @Override
public boolean onCreate() {
dBlite = new DBlite(getContext(), Xin.DBNAME, null, Xin.VERSION);
return true; } @Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
db = dBlite.getWritableDatabase();
switch (sMatcher.match(uri)) {
case Xin.ITEM:
cursor = db.query(Xin.TNAME, projection, selection, selectionArgs,
null, null, sortOrder);
break;
case Xin.ITEM_ID:
String id = uri.getPathSegments().get(1);
cursor = db.query(Xin.TNAME, projection, "tid=?",
new String[] { id }, null, null, sortOrder);
break; default:
break;
}
return cursor;
} @Override
public Uri insert(Uri uri, ContentValues values) {
db = dBlite.getWritableDatabase();
long returnId = db.insert(Xin.TNAME, null, values);
Uri uriReturn = Uri.parse("content://" + Xin.AUTOHORITY + "/"
+ Xin.TNAME + "/" + returnId); return uriReturn;
} @Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
db = dBlite.getWritableDatabase();
int deleteId = 0;
switch (sMatcher.match(uri)) {
case Xin.ITEM:
deleteId = db.delete(Xin.TNAME, selection, selectionArgs); break;
case Xin.ITEM_ID:
String id = uri.getPathSegments().get(1);
deleteId = db.delete(Xin.TNAME, "tid=?", new String[] { id }); default:
break;
}
return deleteId;
} @Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
db = dBlite.getWritableDatabase();
int updateId = 0;
switch (sMatcher.match(uri)) {
case Xin.ITEM:
updateId = db.update(Xin.TNAME, values, selection, selectionArgs);
break;
case Xin.ITEM_ID:
String id = uri.getPathSegments().get(1);
updateId = db.update(Xin.TNAME, values, "tid= ?",
new String[] { id });
break; default:
break;
}
return updateId;
} @Override
public String getType(Uri uri) {
switch (sMatcher.match(uri)) {
case Xin.ITEM:
return Xin.CONTENT_TYPE + "/vnd." + Xin.AUTOHORITY + Xin.TNAME;
case Xin.ITEM_ID:
return Xin.CONTENT_ITEM_TYPE + "/vnd." + Xin.AUTOHORITY + Xin.TNAME; }
return null;
} }

4.在manifest文件中对MyProvider进行注册

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.appone"
android:versionCode="1"
android:versionName="1.0" > <uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="21" /> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity> <provider
android:name="com.example.appone.MyProvider"
android:authorities="com.example.appone.provider"
android:exported="true" >
</provider>
</application> </manifest>

5.在MainActivity中使用ContentProvider进行CRUD

 package com.example.appone;

 import android.app.Activity;
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.view.View.OnClickListener;
import android.view.Window; public class MainActivity extends Activity implements OnClickListener { private String newid; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_contentprovider); findViewById(R.id.contentProvider_query).setOnClickListener(this);
findViewById(R.id.contentProvider_insert).setOnClickListener(this);
findViewById(R.id.contentProvider_update).setOnClickListener(this);
findViewById(R.id.contentProvider_delete).setOnClickListener(this);
} @Override
public void onClick(View v) {
Uri uriItem = Uri.parse("content://"+Xin.AUTOHORITY+"/"+Xin.TNAME);
Uri uriId = Uri.parse("content://"+Xin.AUTOHORITY+"/"+Xin.TNAME+"/"+newid);
switch (v.getId()) {
case R.id.contentProvider_query:
Cursor cursor=this.getContentResolver().query(uriItem, null, null, null, null);
if (cursor!=null) {
while (cursor.moveToNext()) {
String userName=cursor.getString(cursor.getColumnIndex(Xin.USERNAME));
String sex = cursor.getString(cursor.getColumnIndex(Xin.SEX));
String email = cursor.getString(cursor.getColumnIndex(Xin.EMAIL));
String date = cursor.getString(cursor.getColumnIndex(Xin.DATE));
Log.e("appone", "获取到的信息为:"+userName+email+date+sex);
}
cursor.close();
}
break; case R.id.contentProvider_insert:
ContentValues values = new ContentValues();
values.put(Xin.EMAIL, "1234@12.com");
values.put(Xin.USERNAME, "张三");
values.put(Xin.DATE, "2015");
values.put(Xin.SEX, "男");
Uri newUri = this.getContentResolver().insert(uriItem,values);
newid = newUri.getPathSegments().get(1);
break; case R.id.contentProvider_update:
ContentValues values2= new ContentValues();
values2.put(Xin.USERNAME, "王五");
values2.put(Xin.EMAIL, "xxx@ddd.com");
values2.put(Xin.SEX, "女");
this.getContentResolver().update(uriId, values2, null, null);
break; case R.id.contentProvider_delete:
this.getContentResolver().delete(uriId, null, null);
break; default:
break;
}
} }

注意:

首先需要插入消息,然后再查询或者更新或者删除,否则newId的值为空。