首先,ContentProvider(内容提供者)是android中的四大组件之
一,但是在一般的开发中,可能使用的比较少。
ContentProvider为不同的软件之间数据共享,提供统一的接口。也就是说,如果我们想让其他的应用使用我们自己程序内的数据,就可以使用ContentProvider定义一个对外开放的接口,从而使得其他的应用可以使用咱们应用的文件、数据库内存储的信息。当然,自己开发的应用需要给其他应用共享信息的需求可能比较少见,但是在Android系统中,很多系统自带应用,比如联系人信息,图片库,音频库等应用,为了对其他应用暴露数据,所以就使用了ContentProvider机制。所以,我们还是要学习ContentProvider的基本使用,在遇到获取联系人信息,图片库,音频库等需求的时候,才能更好的实现功能
Android系统为了让我们更好的对外暴露数据,提供了统一的接口,所以定义了抽象类ContentProvider,因此,如果我们想对外提供数据,我们需要继承ContentProvider,并且实现下面的这几个方法:
onCreate() 当我们的provider初始化时被调用,我们应该在这个方法里面完成部分初始化操作 query() 查询方法,用于给调用者返回数据 insert() 插入操作,用于让外部应用插入数据到内容提供者中 update() 更新操作,用于更新内容提供者的数据 delete() 用于删除数据 getType 返回内容提供者的MIME Type
上面这些方法,当我们继承自ContentProvider的时候,eclipse会自动的给我们添加,但是这并不代表我们每个方法都需要自定义实现。如果我们只希望给其他应用提供数据,而不允许其他应用修改我们的数据,那么我们只需要实现onCreate(),getType()和query()这三个方法就可以了,其他的三个方法我们可以根据业务需求,实现或者是不实现。
上面的六个方法几乎每一个方法都会带有Uri这个参数,这个参数也正是调用ContentResolver的调用者传递过来的,所以要对传入的Uri参数进行解析,从而分析出调用者期望访问的表和数据。标准的URI写法如下:
content://com.example.app.provider/table1 这个表示的是调用者希望访问的是com.example.app这个应用的table1中的数据,
content://com.example.app.provider/table1/1 这个表示的是调用者希望访问的是com.example.app这个应用的table1中的id为1的数据。
我们还可以使用通配符的方式来匹配这两种格式的内容URI
*表示匹配任意长度的任意字符,能匹配任意表的内容的URI格式: content://com.example.app.provider/*
#表示匹配任意长度的数字,能匹配table1中任意一行数据格式: content://com.example.app.provider/table1/#
需要特别注意的是其中的gettype()方法,他是所有内容提供器都必须提供的一个方法,用于获取uri对象所对应的MIME类型,一个内容uri的MIME类型主要由三个部分组成:
必须以vnd开头
如果uri以路径结尾,后接android.cursor.dir/,如果以id结尾,后接android.cursor.item/
最后接上vnd.<authority>.<path>(authority是在AndroidManifest.xml中注册的时候自定义的)
根据上面的定义
对于content://com.example.app.provider/table1这个内容URI,对应的MIME类型为:vnd.android.cursor.dir/vnd.com.example.app.provider.table1
对于content://com.example.app.provider/table1/1这个内容URI,对应MIME类型为:vnd.android.cursor.item/vnd.com.example.app.provider.table1
新建一个 DatabaseProvider继承ContentProvider
package com.example.databasetest;
import DataBase.MyDataBaseHelper;
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;
import android.widget.Switch;
public class DataBaseProvider extends ContentProvider {
public static final int BOOK_DIR = 0 ;//访问BOOK表中所有数据
public static final int BOOK_ITEM = 1 ;//访问BOOK表中单条数据
public static final int CATEGORY_DIR = 2 ;//访问CATEGORY表中所有数据
public static final int CATEGORY_ITEM = 3 ;//访问CATEGORY表中单条数据
public static final String AUTHORITY = "com.example.databasetest.provider";//定义静态常量AUTHORITY,以便后边使用
private static UriMatcher uriMatcher;
private MyDataBaseHelper dbHelper;
//静态代码块:
//对UriMatcher进行初始化操作,将期望匹配的集中URI格式添加了进去
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);
}
//创建了一个MyDataBaseHelper的实例,然后返回true表示内容提供器初始化成功,这时数据库就完成了创建或者升级操作
@Override
public boolean onCreate() {
dbHelper = new MyDataBaseHelper(getContext(),"BookStore.db",null,2);
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// 查询数据
SQLiteDatabase db = dbHelper.getReadableDatabase(); //获取SQLiteDatabase实例
Cursor cursor = null; //定义光标
switch(uriMatcher.match(uri)) //根据传入的Uri判断用户要访问的是哪张表
{
case BOOK_DIR:
cursor = db.query("BOOK",projection,selection,selectionArgs,null,null,sortOrder);
break;
case BOOK_ITEM:
//uri的getPathSegments方法,它会将uri权限之后的部分以/符号进行分割,并把分割后的结果放入到一个字符串列表
//那个列表的0位置存放的是路径,1位置存放的是id
String bookid = uri.getPathSegments().get(1);
cursor = db.query("BOOK",projection,"id = ?",new String[]{bookid},null,null,sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("CATEGORY",projection,selection,selectionArgs,null,null,sortOrder);
break;
case CATEGORY_ITEM:
String categoryid = uri.getPathSegments().get(1);
cursor = db.query("CATEGORY",projection,"id = ?",new String[]{categoryid},null,null,sortOrder);
break;
}
return cursor;
}
//根据响应的规则,再根据uri判断返回特定的值以便获取MIME类型
@Override
public String getType(Uri uri) {
// 获取mime类型
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category";
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// 添加数据
SQLiteDatabase db = dbHelper.getWritableDatabase(); //获取SQLiteDatabase实例
Uri uriReturn = null ; //因为insert方法要求返回一个能够表达这条新数据的uri,所以定义uri便于接收后边的uri
switch(uriMatcher.match(uri))
{
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("Book", null, values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);//使用Uri的parse方法将内容URI解析成Uri对象赋值给uriReturn
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("Category", null, values);
uriReturn = Uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
break;
default:
break;
}
return uriReturn;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
//更新数据
SQLiteDatabase db = dbHelper.getWritableDatabase(); //获取SQLiteDatabase实例
int updatedRows = 0; //受影响更新的行数会作为返回值返回,用于接收返回值
switch (uriMatcher.match(uri)) { //判断用户想要访问哪张表中的数据
case BOOK_DIR:
updatedRows = db.update("Book", values, selection, selectionArgs);//执行更新操作
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("Book", values, "id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
updatedRows = db.update("Category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("Category", values, "id = ?", new String[]{categoryId});
break;
default:
break;
}
return updatedRows;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//删除数据
SQLiteDatabase db = dbHelper.getWritableDatabase(); //获取SQLiteDatabase数据库实例
int deletedRows = 0; //定义为了获取删除行数
switch (uriMatcher.match(uri)) { //根据传入的uri判断要访问那张表
case BOOK_DIR:
deletedRows = db.delete("Book",selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("Book", "id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
deletedRows = db.delete("Category", selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("Category", "id = ?", new String[]{categoryId});
break;
default:
break
}
return deletedRows;
}
}
把自定义的内容提供器注册内的内容表示对内容提供器进行注册,必须放在应用的下面,属性指定该类的全名;指定该内容提供器的权限,指定是否可以被其他程序访问;
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.databasetest"
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.databasetest.DataBaseProvider"
android:authorities="com.example.databasetest.provider"
android:exported="true">
</provider>
</application>
</manifest>
创建测试,新建项目ProviderTest,用于访问刚才的项目首先编写布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.providertest.MainActivity" >
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add To Book" />
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query From Book" />
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update Book" />
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete From Book" />
</LinearLayout>
修改MainActivity.java
package com.example.providertest;
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.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private String newId;//用于存放刚操作数据的id
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button addData = (Button) findViewById(R.id.add_data);
Button queryData = (Button) findViewById(R.id.query_data);
Button updateData = (Button) findViewById(R.id.update_data);
Button deleteData = (Button) findViewById(R.id.delete_data);
addData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//添加数据
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");//将内容URI解析成Uri对象,
//把要添加的数据都放到ContentValues的对象values中
ContentValues values = new ContentValues();
values.put("name", "A Clash of Kings");
values.put("author", "George Martin");
values.put("pages", 1040);
values.put("price", 55.55);
//当前Activity的getContentResolver()的insert方法插入数据
Uri newUri = getContentResolver().insert(uri, values);
//newUri的getPathSegments().get(1)取到新插入数据的id,
newId = newUri.getPathSegments().get(1);
}
});
queryData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//查询数据
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");//将内容URI解析成Uri对象
Cursor cursor = getContentResolver().query(uri, null, null, null, null);//调用getContentResolver的query方法把取到的值放在游标Cursor中
//取出cursor中的数据
if (cursor!=null) {
while (cursor.moveToNext()) {
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("MainActivity", "book name is "+ name);
Log.d("MainActivity", "book author is "+ author);
Log.d("MainActivity", "book pages is "+ pages);
Log.d("MainActivity", "book price is "+ price);
}
}
cursor.close();//关闭游标
}
});
updateData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 更新数据
Uri uri = Uri.parse("content://com.example.databasetest.provider/book/"+newId);//将内容URI解析成Uri对象
//将要更新的内容放到values中
ContentValues values = new ContentValues();
values.put("name", "A storm of Swords");
values.put("pages", 1216);
values.put("price", 24.05);
//执行更新操作
getContentResolver().update(uri, values, null, null);
}
});
deleteData.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 删除数据
Uri uri = Uri.parse("content://com.example.databasetest.provider/book/"+newId);//将内容URI解析成Uri对象
getContentResolver().delete(uri, null, null);//调用getContentResolver()的delete方法删除指定的数据
}
});
}
}