ContentProvider——内容提供者。它是一个类,这个类主要是对Android系统中进行共享的数据进行包装,并提供了一组统一的访问接口供其他程序调用。这些被共享的数据,可以使系统自己的也可以使我们个人应用程序中的数据。
在Android中,数据的存储有很多种方式,最常用的就是SQLite和XML文件方式。在不同的应用程序间,其实数据是不能直接被相互访问和操作的,在这种情况下,ContentProvider很好的被用来解决了不同应用程序间数据共享的问题。
其实在Android系统中,已经为我们提供了许多ContentProvider,如:Contacts、Browser、CallLog、Settings等等。那么,Android系统中提供了这么多的ContentProvider,另外还有我们自己公开的共享数据,我们在写程序的时候,怎么才能让我们的应用程序知道去哪儿取、如何取这些数据呢?我们自然的会想到URI。
URI(Uniform Resource Identifier)——统一资源定位符,URI在ContentProvider中代表了要操做的数据。
在Android系统中通常的URI格式为:content://LiB.cprovider.myprovider.Users/User/21
在万维网访问时通常用的URI格式为:http://www.XXXX.com/AAA/123
综上所述,content://LiB.cprovider.myprovider.Users/User/21所代表的URI的意思为:标识LiB.cprovider.myprovider中Users表中_ID为21的User项。
4、 一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。
也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。
外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,
重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据
5.我们来看看Android系统中为我们提供了些有关操作URI的方法:
UriMatcher:用于匹配Uri,它的用法如下:
1.首先把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回匹配码为1
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配 content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回的匹配码为1。
ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
• withAppendedId(uri, id)用于为路径加上ID部分
• parseId(uri)方法用于从路径中获取ID部分
ContentResolver:当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver使用insert、delete、update、query方法,来操作数据。
6. ContentProvider中公开的几个方法
7.实现步骤:
想要自己的程序里面的数据能够被其他程序所访问到,有以下步骤:
第一:首先生成一个继承contentprovider的类.
第二:在androidMainfest.xml里面添加一个provider的标签就可以了.
是不是很简单?其他程序访问的时候只要按以下步骤就可以访问到了:
Uri uri=Uri.Uri.parse("content://"+AUTHORY+"/userinfo1");
AUTHORY其实就是 android:authorities的值.,注意.这里必须一样..否则系统是找不到的.也是就是
String AUTHORY="zuoyeSQLite.hall.MyContentProvider"
然后获取一个
ContentResolver mContentResolver=getContentResolver();
这样就其他程序就可以反问我们的数据了..
ContentResolver对应的几个方法:
query(Uri, String[], String, String[], String)
which returns data to the caller
insert(Uri, ContentValues)
which inserts new data into the content provider
update(Uri, ContentValues, String, String[])
which updates existing data in the content provider
delete(Uri, String, String[])
which deletes data from the content provider
其实和contentprovider里面的方法是一样的..他们所对应的数据,最终是会被传到我们在之前程序里面定义的那个contentprovider类的方法,至于你想要在这几个方法里面做什么事,那个就随你意了..
8.实例
在Manifest.xml的<application>和application>之间加入:
MyProvider代表contentprovider的实现类,com.snowdream.provider代表uri的authority部分,访问MyProvider的uri的authority部分必须是com.snowdream.provider
定义一个类存放各种常量
package com.snowdream.contentprovider;
import android.net.Uri;
public class Profile {
/**
* 表格名称
*/
public static final String TABLE_NAME = "profile";
/**
* 列表一,_ID,自动增加
*/
public static final String COLUMN_ID = "_id";
/**
* 列表二,名称
*/
public static final String COLUMN_NAME = "name";
public static final String AUTOHORITY = "com.snowdream.provider";
public static final int ITEM = 1;
public static final int ITEM_ID = 2;
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.snowdream.profile";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.snowdream.profile";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTOHORITY + "/profile");
}
package com.snowdream.contentprovider;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBHelper extends SQLiteOpenHelper {
/**
* 数据库名称
*/
private static final String DATABASE_NAME = "test.db";
/**
* 数据库版本
*/
private static final int DATABASE_VERSION = 1;
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) throws SQLException {
//创建表格
db.execSQL("CREATE TABLE IF NOT EXISTS "+ Profile.TABLE_NAME + "("+ Profile.COLUMN_ID +" INTEGER PRIMARY KEY AUTOINCREMENT," + Profile.COLUMN_NAME +" VARCHAR NOT NULL);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) throws SQLException {
//删除并创建表格
db.execSQL("DROP TABLE IF EXISTS "+ Profile.TABLE_NAME+";");
onCreate(db);
}
}
在query方法中,对查询结果的cursor调用setNotificationUri(ContentResolver cr, Uri notifyUri),注册contentprovider的观察者,这样contentprovider的数据发生变化时,调用
getContentResolver().notifyChange方法,系统会重新查询数据(调用contentprovider的query),并通知到cursor(调用cursor的相关回调),同时Cursor也可以注册观察者(下例中simplecursoradapter的构造函数中,
会把simplecursoradapter的一个成员注册成cursor的观察者),这样contentprovider的数据变化,调用getContentResolver().notifyChange方法,会通过Cursor通知
到simplecursoradapter,从而在listview中更新数据的显示
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class MyProvider extends ContentProvider {
DBHelper mDbHelper = null;
SQLiteDatabase db = null;
private static final UriMatcher mMatcher;
static{
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(Profile.AUTOHORITY,Profile.TABLE_NAME, Profile.ITEM);
mMatcher.addURI(Profile.AUTOHORITY, Profile.TABLE_NAME+"/#", Profile.ITEM_ID);
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
@Override
public String getType(Uri uri) {
switch (mMatcher.match(uri)) {
case Profile.ITEM:
return Profile.CONTENT_TYPE;
case Profile.ITEM_ID:
return Profile.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI"+uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
long rowId;
if(mMatcher.match(uri)!=Profile.ITEM){
throw new IllegalArgumentException("Unknown URI"+uri);
}
rowId = db.insert(Profile.TABLE_NAME,null,values);
if(rowId>0){
Uri noteUri=ContentUris.withAppendedId(Profile.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
mDbHelper = new DBHelper(getContext());
db = mDbHelper.getReadableDatabase();
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
Cursor c = null;
switch (mMatcher.match(uri)) {
case Profile.ITEM:
c = db.query(Profile.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
case Profile.ITEM_ID:
c = db.query(Profile.TABLE_NAME, projection,Profile.COLUMN_ID + "="+uri.getLastPathSegment(), selectionArgs, null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Unknown URI"+uri);
}
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
}
package com.snowdream.contentprovider;
import com.snowdream.contentprovider.R;
import android.app.ListActivity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.widget.SimpleCursorAdapter;
public class MainActivity extends ListActivity {
private SimpleCursorAdapter adapter= null;
private Cursor mCursor = null;
private ContentResolver mContentResolver = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initData();
initAdapter();
}
public void initData(){
mContentResolver = getContentResolver();
//填充数据
for (int i = 0; i < 100; i++) {
ContentValues values = new ContentValues();
values.put(Profile.COLUMN_NAME, "张三"+i);
mContentResolver.insert(Profile.CONTENT_URI, values);
}
}
public void initAdapter(){
//查询表格,并获得Cursor
//查询全部数据
mCursor = mContentResolver.query(Profile.CONTENT_URI, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, null, null, null);
//查询部分数据
//String selection = Profile.COLUMN_ID + " LIKE '%1'";
//mCursor = mContentResolver.query(Profile.CONTENT_URI, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, selection, null, null);
//查询一个数据
//Uri uri = ContentUris.withAppendedId(Profile.CONTENT_URI, 50);
//mCursor = mContentResolver.query(uri, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, null, null, null);
startManagingCursor(mCursor);
//设置adapter
adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, mCursor, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, new int[]{android.R.id.text1,android.R.id.text2});
setListAdapter(adapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
参考文章:
观察者模式和contentprovider数据更新