ContentProvider向我们提供了我们在应用程序之前共享数据的一种机制,而我们知道每一个应用程序都是运行在不同的应用程序的,数据和文件在不同应用程序之间达到数据的共享不是没有可能,而是显得比较复杂,而正好Android中的ContentProvider则达到了这一需求,比如有时候我们需要操作手机里的联系人,手机里的多媒体等一些信息,我们都可以用到这个ContentProvider来达到我们所需。
1)、ContentProvider为存储和获取数据提供了统一的接口。ContentProvide对数据进行封装,不用关心数据存储的细节。使用表的形式来组织数据。
2)、使用ContentProvider可以在不同的应用程序之间共享数据。
3)、Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。
总的来说使用ContentProvider对外共享数据的好处是统一了数据的访问方式。
我们在此先介绍provider的写法,
(1)首先该类必须继承 ContentProvider
public class DictProvider extends ContentProvider
Uri
为系统的每一个资源起一个名字,比方说通话记录。
1)、每一个ContentProvider都拥有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。
2)、Android所提供的ContentProvider都存放在android.provider包中。 将其分为A,B,C,D 4个部分:
A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;”content://”
B:AUTHORITY (URI 的标识),用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称
C:路径(path),通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就可以了;”content://com.bing.provider.myprovider/tablename”
D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; “content://com.bing.provider.myprovider/tablename/#” #表示数据id。
PS:
路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
1、要操作person表中id为10的记录,可以构建这样的路径:/person/10
2、要操作person表中id为10的记录的name字段, person/10/name
3、要操作person表中的所有记录,可以构建这样的路径:/person
4、要操作xxx表中的记录,可以构建这样的路径:/xxx
5、当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:
要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name
6、如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:Uri uri = Uri.parse(“content://com.bing.provider.personprovider/person”)
(2)一般我们会在一个静态类里面定义AUTHORITY 即Uri
// 定义该ContentProvider的Authority
public static final String AUTHORITY
= "org.crazyit.providers.dictprovider";
// 定义该Content提供服务的两个Uri
public final static Uri DICT_CONTENT_URI = Uri
.parse("content://" + AUTHORITY + "/words");
public final static Uri WORD_CONTENT_URI = Uri
.parse("content://" + AUTHORITY + "/word");
UriMatcher类使用介绍
UriMatcher类用于匹配Uri,它的用法如下:
(3)为UriMatcher注册两个Uri
// 为UriMatcher注册两个Uri
matcher.addURI(Words.AUTHORITY, "words", WORDS);
matcher.addURI(Words.AUTHORITY, "word/#", WORD);
(4)注册完需要匹配的Uri后,就可以使用matcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,上面的匹配码分别是WORDS、WORD,这里值分别是1和2.
switch (matcher.match(uri))
{
// 如果操作的数据是多项记录
case WORDS:
...
case WORD:
...
default:
throw new IllegalArgumentException("未知Uri:" + uri);
}
ContentUris类使用介绍
ContentUris类用于操作Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:
Uri uri = Uri.parse("content://com.bing.provider.personprovider/person")
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成后的Uri为:content://com.bing.provider.personprovider/person/10
parseId(uri)方法用于从路径中获取ID部分:
Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person/10")
long personid = ContentUris.parseId(uri);//获取的结果为:10
(5)完成继承的方法增删改查及getType,onCreate方法的复写。值得注意的是,onCreate()是在系统调用该provider是自动调用的,而其他的方法则是在另一个程序里该同名方法被执行时自动调用的。
// 第一次调用该DictProvider时,系统先创建DictProvider对象,并回调该方法
@Override
public boolean onCreate()
{
dbOpenHelper = new MyDatabaseHelper(this.getContext(),
"myDict.db3", 1);
return true;
}
// 返回指定Uri参数对应的数据的MIME类型
@Override
public String getType(Uri uri)
{
switch (matcher.match(uri))
{
// 如果操作的数据是多项记录
case WORDS:
return "vnd.android.cursor.dir/org.crazyit.dict";
// 如果操作的数据是单项记录
case WORD:
return "vnd.android.cursor.item/org.crazyit.dict";
default:
throw new IllegalArgumentException("未知Uri:" + uri);
}
}
// 查询数据的方法
@Override
public Cursor query(Uri uri, String[] projection, String where,
String[] whereArgs, String sortOrder)
{
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
switch (matcher.match(uri))
{
// 如果Uri参数代表操作全部数据项
case WORDS:
// 执行查询
return db.query("dict", projection, where,
whereArgs, null, null, sortOrder);
// 如果Uri参数代表操作指定数据项
case WORD:
// 解析出想查询的记录ID
long id = ContentUris.parseId(uri);
String whereClause = Words.Word._ID + "=" + id;
// 如果原来的where子句存在,拼接where子句
if (where != null && !"".equals(where))
{
whereClause = whereClause + " and " + where;
}
return db.query("dict", projection, whereClause, whereArgs,
null, null, sortOrder);
default:
throw new IllegalArgumentException("未知Uri:" + uri);
}
}
// 插入数据方法
@Override
public Uri insert(Uri uri, ContentValues values)
{
// 获得数据库实例
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
switch (matcher.match(uri))
{
// 如果Uri参数代表操作全部数据项
case WORDS:
// 插入数据,返回插入记录的ID
long rowId = db.insert("dict", Words.Word._ID, values);
// 如果插入成功返回uri
if (rowId > 0)
{
// 在已有的 Uri的后面追加ID
Uri wordUri = ContentUris.withAppendedId(uri, rowId);
// 通知数据已经改变
getContext().getContentResolver()
.notifyChange(wordUri, null);
return wordUri;
}
break;
default :
throw new IllegalArgumentException("未知Uri:" + uri);
}
return null;
}
// 修改数据的方法
@Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs)
{
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
// 记录所修改的记录数
int num = 0;
switch (matcher.match(uri))
{
// 如果Uri参数代表操作全部数据项
case WORDS:
num = db.update("dict", values, where, whereArgs);
break;
// 如果Uri参数代表操作指定数据项
case WORD:
// 解析出想修改的记录ID
long id = ContentUris.parseId(uri);
String whereClause = Words.Word._ID + "=" + id;
// 如果原来的where子句存在,拼接where子句
if (where != null && !where.equals(""))
{
whereClause = whereClause + " and " + where;
}
num = db.update("dict", values, whereClause, whereArgs);
break;
default:
throw new IllegalArgumentException("未知Uri:" + uri);
}
// 通知数据已经改变
getContext().getContentResolver().notifyChange(uri, null);
return num;
}
// 删除数据的方法
@Override
public int delete(Uri uri, String where, String[] whereArgs)
{
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
// 记录所删除的记录数
int num = 0;
// 对uri进行匹配
switch (matcher.match(uri))
{
// 如果Uri参数代表操作全部数据项
case WORDS:
num = db.delete("dict", where, whereArgs);
break;
// 如果Uri参数代表操作指定数据项
case WORD:
// 解析出所需要删除的记录ID
long id = ContentUris.parseId(uri);
String whereClause = Words.Word._ID + "=" + id;
// 如果原来的where子句存在,拼接where子句
if (where != null && !where.equals(""))
{
whereClause = whereClause + " and " + where;
}
num = db.delete("dict", whereClause, whereArgs);
break;
default:
throw new IllegalArgumentException("未知Uri:" + uri);
}
// 通知数据已经改变
getContext().getContentResolver().notifyChange(uri, null);
return num;
}
至此我们已经完成了provider类。
不过在写好DictProvider类以后需要在manifest文件声明
<provider android:name=".DictProvider"
android:authorities="org.crazyit.providers.dictprovider"
android:exported="true"/>
而且要使该类能被调用,说明在调用以前也要写好该activity,可以数据库或者其他xml文件等。
接下来我们开始写应用该provider的activity
当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成。
(1)要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。
ContentResolver contentResolver;
// 获取系统的ContentResolver对象
contentResolver = getContentResolver();
ContentResolver 类提供了与ContentProvider类相同签名的四个方法:
public Uri insert(Uri uri, ContentValues values):该方法用于往ContentProvider添加数据。
public int delete(Uri uri, String selection, String[] selectionArgs):该方法用于从ContentProvider删除数据。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):该方法用于更新ContentProvider中的数据。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):该方法用于从ContentProvider中获取数据。
这些方法的第一个参数为Uri,代表要操作的ContentProvider和对其中的什么数据进行操作,
其实和contentprovider里面的方法是一样的.他们所对应的数据,最终是会被传到我们在之前程序里面定义的那个contentprovider类的方法。
(2)设置查询按钮,插入按钮
insert = (Button) findViewById(R.id.insert);
search = (Button) findViewById(R.id.search);
(3)为按钮添加事件,即进行查询,插入数据
insert.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View source)
{
// 获取用户输入
String word = ((EditText) findViewById(R.id.word))
.getText().toString();
String detail = ((EditText) findViewById(R.id.detail))
.getText().toString();
// 插入生词记录
ContentValues values = new ContentValues();
values.put(Words.Word.WORD, word);
values.put(Words.Word.DETAIL, detail);
contentResolver.insert(
Words.Word.DICT_CONTENT_URI, values);
// 显示提示信息
Toast.makeText(MainActivity.this, "添加生词成功!"
, Toast.LENGTH_SHORT).show();
}
});
// 为search按钮的单击事件绑定事件监听器
search.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View source)
{
// 获取用户输入
String key = ((EditText) findViewById(R.id.key))
.getText().toString();
// 执行查询
Cursor cursor = contentResolver.query(
Words.Word.DICT_CONTENT_URI, null,
"word like ? or detail like ?", new String[] {
"%" + key + "%", "%" + key + "%" }, null);
// 创建一个Bundle对象
Bundle data = new Bundle();
data.putSerializable("data", converCursorToList(cursor));
// 创建一个Intent
Intent intent = new Intent(MainActivity.this,
ResultActivity.class);
intent.putExtras(data);
// 启动Activity
startActivity(intent);
}
});
这里调用的contentResolver.insert(), contentResolver.query()实际上是为dictPrivider里对应的方法提供参数。
这样我们就完成不同程序间数据共享。