android contentResolver的使用

释放双眼,带上耳机,听听看~!

今天,简单讲讲android 如何使用contentResolver。

这个contentResolver其实是属于ContentProvider 的内容,我之前一直没有用到android的ContentProvider ,所以昨天看到contentResolver时,居然不知道contentResolver怎么使用,后来,在网上查找了资料,最终解决了问题。这里记录一下。

 

在Android官方指出的Android的数据存储方式总共有五种,分别是:Shared
Preferences、网络存储、文件存储、外储存储、SQLite。

但是我们知道一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候我们需要操作其他应用程序的一些数据,

例如我们需要操作系统里的媒体库、通讯录等,这时我们就可能通过ContentProvider来满足我们的需求了。

采用文件方式对外共享数据,需要进行文件操作读写数据;

采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。

而使用ContentProvider共享数据的好处是统一了数据访问方式。

 

由于这边博客主要将contentResolver,而且ContentProvider的内容很多,我也没有完全弄懂,所以就不讲关于ContentProvider,大家有兴趣的可以自己查找资料。

 

Android是如何实现应用程序之间数据共享的?一个应用程序可以将自己的数据完全暴露出去,外界更本看不到,也不用看到这个应用程序暴露的数据是如何存储的,或者是使用数据库还是使用文件,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和这个程序里的数据打交道,例如:添加(insert)、删除(delete)、查询(query)、修改(update),当然需要一定的权限才可以。

如何将应用程序的数据暴露出去? Android提供了ContentProvider,一个程序可以通过实现一个Content provider的抽象接口将自己的数据完全暴露出去,而且Content providers是以类似数据库中表的方式将数据暴露。Content providers存储和检索数据,通过它可以让所有的应用程序访问到,这也是应用程序之间唯一共享数据的方法。要想使应用程序的数据公开化,可通过2种方法:创建一个属于你自己的Content provider或者将你的数据添加到一个已经存在的Content
provider中,前提是有相同数据类型并且有写入Content provider的权限。

如何通过一套标准及统一的接口获取其他应用程序暴露的数据?Android提供了ContentResolver,外界的程序可以通过ContentResolver接口访问ContentProvider提供的数据。

 

当前篇主要说明,如何获取其它应用程序共享的数据,比如获取Android 手机电话薄中的信息。

 

什么是URI?

在学习如何获取ContentResolver前,有个名词是必须了解的:URI。URI是网络资源的定义,在Android中赋予其更广阔的含义。

将其分为A,B,C,D 4个部分:
A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;
B:URI的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的  类名。这个标识在<provider> 元素的 authorities属性中说明:
<provider name=”.TransportationProvider”  authorities=”com.example.transportationprovider”  . . .  >
C:路径,Content Provider使用这些路径来确定当前需要生什么类型的数据,URI中可能不包括路径,也可能包括多个;
D:如果URI中包含,表示需要获取的记录的ID;如果没有ID,就表示返回全部;
由于URI通常比较长,而且有时候容易出错,切难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串,例如:People.CONTENT_URI

 

ContentResolver 介绍说明

看完这些介绍,大家一定就明白了,ContentResolver是通过URI来查询ContentProvider中提供的数据。除了URI以外,还必须知道需要获取的数据段的名称,以及此数据段的数据类型。如果你需要获取一个特定的记录,你就必须知道当前记录的ID,也就是URI中D部分。

前面也提到了Content providers是以类似数据库中表的方式将数据暴露出去,那么ContentResolver也将采用类似数据库的操作来从Content providers中获取数据。现在简要介绍ContentResolver的主要接口,如下:

返回值 函数声明
final Uri insert(Uri url, ContentValues values)Inserts a row into a table at the given URL.
final int delete(Uri url, String where, String[] selectionArgs)Deletes row(s) specified by a content URI.
final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)Query the given URI, returning a Cursor over the result set.
final int update(Uri uri, ContentValues values, String where, String[] selectionArgs)Update row(s) in a content URI.

看到这里,是否感觉与数据库的操作基本一样的?就是这样的,详细解析请参考了 Android SQLite解析 中的说明,不在此详细说明。

最后一个问题:如何获取ContentResolver?调用getContentResolver (),例如:ContentResolver cr = getContentResolver();

知道ContentResolver是通过ContentProvider来获取其他与应用程序共享的数据,那么ContentResolver与ContentProvider的接口应该差不多的。

 

其中ContentProvider负责

* 组织应用程序的数据;
* 向其他应用程序提供数据;

ContentResolver则负责

* 获取ContentProvider提供的数据;
* 修改/添加/删除更新数据等;

ContentProvider 是如何向外界提供数据的?

Android提供了ContentProvider,一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProviders是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。至于如何从URI中识别出外界需要的是哪个“数据库”,这就是Android底层需要做的事情了,不在此详细说。简要分析下ContentProvider向外界提供数据操作的接口:

query(Uri,String[], String, String[], String)
insert(Uri, ContentValues)
update(Uri,
ContentValues, String, String[])

delete(Uri, String, String[])

这些操作与数据库的操作基本上完全一样,在此不详细说,具体的解析可以参考 Android Sqlite解析 中的详细说明。需要特殊说明的地方是URI:

在URI的D部分可能包含一个_ID ,这个应该出现在SQL语句中的,可以以种特殊的方式出现,这就要求我们在提供数据的时候,需要来额外关注这个特殊的信息。Android  SDK推荐的方法是:在提供数据表字段中包含一个ID,在创建表时INTEGER PRIMARY KEY AUTOINCREMENT标识此ID字段。

ContentProvider 是如何组织数据的?

组织数据主要包括:存储数据,读取数据,以数据库的方式暴露数据。数据的存储需要根据设计的需求,选择合适的存储结构,首选数据库,当然也可以选择本地其他文件,甚至可以是网络上的数据。数据的读取,以数据库的方式暴露数据这就要求,无论数据是如何存储的,数据最后必须以数据的方式访问。

可能还有2个问题,是需要关注的。

1. ContentProvider是什么时候创建的,是谁创建的?访问某个应用程序共享的数据,是否需要启动这个应用程序?这个问题在 Android SDK中没有明确说明,但是从数据共享的角度出发,ContentProvider应该是Android在系统启动时就创建了,否则就谈不上数据共享了。这就要求在AndroidManifest.XML中使用<provider>元素明确定义。
2. 可能会有多个程序同时通过ContentResolver访问一个ContentProvider,会不会导致像数据库那样的“脏数据”?这个问题一方面需要数据库访问的同步,尤其是数据写入的同步,在AndroidManifest.XML中定义ContentProvider的时候,需要考虑是<provider>元素multiprocess属性的值;另外一方面Android在ContentResolver中提供了 notifyChange()接口,在数据改变时会通知其他ContentObserver,这个地方应该使用了观察者模式,在
ContentResolver中应该有一些类似register,unregister的接口。

至此,已经对ContentProvider提供了比较全面的分析,至于如何创建ContentProvider,可通过2种方法:创建一个属于你自己的ContentProvider或者将你的数据添加到一个已经存在的ContentProvider中,当然前提是有相同数据类型并且有写入 Content provider的权限。在Android SDK的sample中提供的 Notepad具体实例 中去看源代码!

制作ContentResolver实例

以上就完全介绍了如何获取、使用ContentResolver,启动Eclipes,制作一个完整的实例如下:
打开showcontent.java,修改如下:

 

制作ContentResolver实例

打开showcontent.java,修改如下:

package moandroid.showcontact;
import android.app.ListActivity;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.Contacts.Phones;
import android.widget.ListAdapter;
import android.widget.SimpleCursorAdapter;
public class showcontact extends ListActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Cursor c = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);
startManagingCursor(c);
ListAdapter adapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_2, c,
new String[] { Phones.NAME, Phones.NUMBER },
new int[] { android.R.id.text1, android.R.id.text2 });
setListAdapter(adapter);
}

 

然后在AndroidManifest.XML中<application>元素前增加如下许可:

<uses-permission android:name=”android.permission.READ_CONTACTS” />

最后运行程序,在模拟器启动后,单击Menu返回到Home界面,打开Contacts选择Contacts标签页,添加2个联系人信息。返回到Home,选择moandroid.showcontact运行,刚添加的2个联系人信息将显示在界面上,如下:

总结说明

ContentResolver的使用极大的方便了应用程序之间共享数据,如何将应用程序的数据完全暴露给给他应用程序使用了

 

android中ContentResolver的使用

使用ContentResolver增删改查电话本信息,详细代码如下:

import android.app.ListActivity;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.Contacts.People;
import android.support.v4.widget.SimpleCursorAdapter;
import android.widget.ListAdapter;
public class MainActivity extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
insert("tester1");
update("tester2", new String(People.NAME + "='tester1'"));
delete(new String(People.NAME + "='tester2'"));
select();
}
/*
* 向联系人列表中插入新的联系人
* @param name The value of People.NAME
*/
public void insert(String name) {
ContentValues ct = new ContentValues();
ct.put(People.NAME, name);
getContentResolver().insert(People.CONTENT_URI, ct);
}
/*
* 更新手机中指定的联系人
* @param name A new name for People.NAME
* @param where The update requirement.
*/
public void update(String name, String where) {
ContentValues ct = new ContentValues();
ct.put(People.NAME, name);
getContentResolver().update(People.CONTENT_URI, ct, where, null);
}
/*
* 删除手机中指定的联系人
* @param where The delete requirement.
*/
public void delete(String where) {
getContentResolver().delete(People.CONTENT_URI, where, null);
}
// 查找所有联系人
public void select() {
Cursor cursor = getContentResolver().query(People.CONTENT_URI,
new String[] { People._ID, People.NAME }, null, null, null);
ListAdapter adapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1, cursor,
new String[] { People.NAME }, new int[] { android.R.id.text1 });
setListAdapter(adapter);
}
}

AndroidManifest.xml中添加如下权限:

<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>

简单讲讲,其实contentResolver就是可以使用ContentProvider提供的数据,并且进行增,删,查,改。如果自己想对外提供数据,则必须新建一个类,继承ContentProvider,提供contentResolver的增,删,查,改函数。

 

下面介绍一下ContentResolver查询的三种方式:

查询手机中短信的信息,当然得去系统暴露出来的数据库中去查询了,后来发现有三种方式可以选择,下面一一写出来。

1、方式一

第一种方式,采用 getContentResolver().query()方法在主线程中查询数据。这种查询方式是不是异步查询的,直接在UI线程中查询数据,代码如下:

Cursor cursor1 = getContentResolver().query(Sms.CONVESATION_URI, CONVERSATION_PROJECTION,null, null," sms.date desc");
while (cursor1.moveToNext()) {
Log.i("cursor1", String.valueOf(cursor1.getInt(0)));
Log.i("cursor1", cursor1.getString(1));
Log.i("cursor1", cursor1.getString(2));
}
cursor1.close();

2、方式二

第二种查询方式,这种查询同样是在UI线程中查询数据,不过这种方式得到的Cursor不用手动去关闭,是Activity自动会去关闭的,cursor由activity去管理,代码如下:

Cursor cursor2 = managedQuery(Sms.CONVESATION_URI, CONVERSATION_PROJECTION, null, null, "sms.date desc");
while (cursor2.moveToNext()) {
Log.i("cursor2", String.valueOf(cursor2.getInt(0)));
Log.i("cursor2", cursor2.getString(1));
Log.i("cursor2", cursor2.getString(2));
}

 

3、方式三

第三种查询方式,这种方式是利用Android提供的异步查询框架AsyncQueryHandler,/是一种异步查询方式,当单查询完毕后,会调用onQueryComplete(token, cookie, cursor)通知查询完毕,并且传回cursor。代码如下:

private void startQuery() {
Uri uri = Sms.CONVESATION_URI;
mQueryHandler.startQuery(0, null, uri, CONVERSATION_PROJECTION, null, null, "sms.date desc");
}
// 写一个异步查询类
private final class QueryHandler extends AsyncQueryHandler {
public QueryHandler(ContentResolver cr) {
super(cr);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
super.onQueryComplete(token, cookie, cursor);
// 更新mAdapter的Cursor
mAdapter.changeCursor(cursor);
}
}

Android开发中性能优化始终要谨记在心,所以如果数据量稍微大点都要使用异步查询,尽量避免在UI线程中做耗时操作,而查询而言,利用Android提供好的异步查询框架是最适合的了,其实AsyncQueryHandler也是封装了Handler来实现的,还有一点,一般在查询本地的应用的数据的时候要去采用CursorAdapter。

 

最后,在举一个例子,如何将自己保存的图片写入手机的图库,使图库也可以显示:

File file = new File(fileName);
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); // setar isso
context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

其实就是图片的文件路径通过contentResolver写入手机图库的ContentProvider里,这样手机图库就可以显示了

 

android contentResolver的使用就讲完了。

 

就这么简单。

人已赞赏
Android文章

android StringBuilder的Capacity的使用

2020-4-7 18:46:48

Android文章

android 如何使用SAX解析XML

2020-4-7 20:01:48

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索