没有人会因学问而成为智者。学问或许能由勤奋得来,而机智与智慧却有懒于天赋。
本讲内容:ContentProvider 内容提供器的使用
一、内容提供器的简介
内容提供器主要用于在不同的的应用程序之间实现数据共享的功能。内容提供器的用法有两种,一种是使用现有的内容提供器来读取和操作相应程序中的数据,譬如Android系统中自带的电话簿、短信、媒体库等程序都提供了类似的访问接口,另一种是创建自己的内容提供器给我们程序的数据提供外部访问接口。
二、内容提供器的用法
使用ContentResolver操作ContentProvider中的数据
如果想要访问内容提供器中共享的数据,必须借助ContentProvider 类,通过Context中的getContentResolver()方法可以获取到该类的实例。ContentProvider中的增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI,内容URI给内容提供器中的数据建立了唯一标识符,它主要由权限和路径组成。权限是用于对不同的应用程序做区分,一般用包名进行命名,譬如某个程序包名是com.example.app,那么该程序对应的权限就可以命名为com.example.app.provider。路径则是用于对同一应用程序中不同的表做区分的,通常都会添加到权限的后面。譬如某个程序的数据库存在两张表,table1和table2,这时路径就可以分别命名为/table1和/table2,然后把权限和路径进行组合,内容URI就变成com.example.app.provider/table1和com.example.app.provider/table2,不过,目前还是很难认出这两个字符串就是两个内容URI,我们还需要在字符串的头部加上协议声明,因此内容URI最标准的格式写法如下:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
我们还可以在这个内容URI的后面加上一个id,譬如content://com.example.app.provider/table1/1表示访问table1表中id为1的数据。在得到内容URI字符串之后,我们还需要将它解析成Uri对象才可以作为参数传入,调用Uri.parse()方法就可以将内容URI字符串解析成Uri对象了
三、UriMatcher类
这个类实现匹配内容URI的功能。UriMatcher中提供了一个addURI()方法添加一个拼凑的Uri,这个方法接收三个参数,可以分别权限、路径和一个自定义code值。UriMatcher为一个Uri的容器,容器里面包含着我们即将可能要操作的Uri,如果通过match()方法匹配成功就返回设置的code值,反之,返回一个UriMatcher.NO_MATCH常量(-1)
UriMatcher.NO_MATCH表示不匹配任何路径的返回码。
示例一:读取系统联系人(说明跨程序访问数据成功)
使用现有的内容提供器来读取和操作相应程序中的数据,首先获取到该应用程序的内容URI,然后借助ContentProvider进行CRUD操作。
下面是res/layout/activity_main.xml 布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/id_listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
下面是MainActivity.java主界面文件:
public class MainActivity extends Activity {
private ListView listView;
private ArrayAdapter<String> adapter;
private List<String> listData;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView=(ListView) findViewById(R.id.id_list);
adapter=new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, getContacts());
listView.setAdapter(adapter);
}
/**
* 获取联系人
* @return
*/
private List<String> getContacts(){
listData=new ArrayList<String>();
Cursor cursor=null;
try {// 查询联系人数据
/**
* 这里为什么没有调用Uri.parse()方法去解析一个内容URI字符串呢?,这是因为ContactsContract.CommonDataKinds.Phone类
* 已经帮我们做好了封装,提供了一个CONTENT_URI常量,而这个常量就是使用Uri.parse()方法解析出来的结果。
*/
cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
while(cursor.moveToNext()){
// 获取联系人姓名、号码
String name=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
listData.add(name+"\n"+number);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(cursor!=null){
cursor.close();
}
}
return listData;
}
}
在AndroidManifest.xml文件中添加读取联系人权限
<uses-permission android:name="android.permission.READ_CONTACTS"/>
示例二:创建自己的内容提供器实现跨程序数据共享
新建一个DatabaseText01项目(项目有二张表并使用内容提供器向外部提供接口)
下面是MainActivity.java主界面文件:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
下面是MyDatabaseHelper.java主界面文件:
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)";
//四个参数的构造函数
public MyDatabaseHelper(Context context, String name,CursorFactory factory, int version) {
super(context, name, factory, version);
}
//回调函数,第一次创建数据库时才会调用此函数
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
}
//回调函数,当你构造DBHelper的传递的Version与之前的Version不同时调用此函数
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
下面是DatabaseProvider.java主界面文件:
//通过内容提供器来给程序加入外部访问接口
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;
public static final int CATEGORY_ITEM=3;
public static final String AUTHORITY="com.example.databasetext01.provider";
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);
}
//初始化内容提供器时调用,返回true表示内容提供器初始化成功,这里通常完成对数据库的创建和升级
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 1);
return true;
}
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:
String bookId=uri.getPathSegments().get(1);//获取id
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);
default:
break;
}
return cursor;
}
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);
//将一个内容URI解析成Uri对象,这个内容URI是以新增数据的id结尾的
uriReturn=Uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
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;
}
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
//更新数据
SQLiteDatabase db=dbHelper.getWritableDatabase();
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;
}
public int delete(Uri uri, String selection, String[] selectionArgs) {
//删除数据
SQLiteDatabase db=dbHelper.getWritableDatabase();
int deletedRows=0;
switch (uriMatcher.match(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;
}
public String getType(Uri uri) {//获取当前uri的MIME类型
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetext01.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetext01.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetext01.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetext01.provider.category";
}
return null;
}
}
在AndroidManifest.xml文件中注册
<provider android:name=".DatabaseProvider"
android:authorities="com.example.databasetext01.provider"></provider>
android:authorities属性中指定了该内容提供器的权限
然后运行程序,接着关闭掉 DatabaseText01这个项目,并创建一个新的项目ProviderText,我们通过这个程序去访问DatabaseText01中的数据。
运行ProviderText,点击Add To Book 按钮添加数据,然后通过点击Query From Book按钮来检查
然后点击Update Book按钮来更新数据,再点击Query From Book按钮来检查
最后点击Delete From Book 按钮删除数据,再点击Query From Book按钮来检查就查不到数据了
下面是res/layout/activity_main.xml 布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<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主界面文件:
public class MainActivity extends Activity implements OnClickListener{
private static final String TAG="MainActivity";
private String newId;
private Button addData;
private Button queryData;
private Button updateData;
private Button deleteData;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addData=(Button) findViewById(R.id.add_data);
queryData=(Button) findViewById(R.id.query_data);
updateData=(Button) findViewById(R.id.update_data);
deleteData=(Button) findViewById(R.id.delete_data);
addData.setOnClickListener(this);
queryData.setOnClickListener(this);
updateData.setOnClickListener(this);
deleteData.setOnClickListener(this);
}
public void onClick(View v) {
Uri uri;
ContentValues values;
switch (v.getId()) {
case R.id.add_data://添加数据
uri=Uri.parse("content://com.example.databasetext01.provider/book");
values=new ContentValues();
values.put("name", "A Clash of Kings");
values.put("author", "George Martin");
values.put("pages", "1024");
values.put("price", "23.45");
Uri newUri=getContentResolver().insert(uri, values);
newId=newUri.getPathSegments().get(1);
break;
case R.id.query_data://查询数据
uri=Uri.parse("content://com.example.databasetext01.provider/book");
Cursor cursor=getContentResolver().query(uri, null, null, null, null);
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(TAG, "book name is "+name);
Log.d(TAG, "book author is "+author);
Log.d(TAG, "book pages is "+pages);
Log.d(TAG, "book price is "+price);
}
cursor.close();
}
break;
case R.id.update_data://更新数据
uri=Uri.parse("content://com.example.databasetext01.provider/book/"+newId);
values=new ContentValues();
values.put("name", "A Storm of Swords");
values.put("pages", 1280);
values.put("price", 24.68);
getContentResolver().update(uri, values, null, null);
break;
case R.id.delete_data://删除数据
uri=Uri.parse("content://com.example.databasetext01.provider/book/"+newId);
getContentResolver().delete(uri, null, null);
break;
default:
break;
}
}
}
Take your time and enjoy it 要原码的、路过的、学习过的请留个言,顶个呗~~