ContentProvider实现数据共享-->【好处:统一了数据的访问方式】
一、ContentProvider的作用与常用方法
Uri简介
ContentResolver
Context提供了getContentResolver()来获得ContentResolver对象。
调用ContentResolver的如下方法来操作数据:
<1>、insert(Uri url, ContentValues values):向Uri对应的ContentProvider中插入values对应的数据;
<2>、delete(Uri url, String where, String[] selectionArgs):删除Uri对应的ContentProvider中where提交匹配的数据;
<3>、update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri对应的ContentProvider中where提交匹配的数据;
<4>、query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):查询Uri对应的ContentResolver中where提交匹配的数据;
ContentResolver与ContentProvider关系
以指定Uri为标识,ContentResolver可以实现“间接调用”ContentProvider的CRUD方法。
开发ContentProvider
(1)、开发一个ContentProvider的子类,该子类需要实现query()、insert()、update()、delete()等方法;
(2)、在清单文件中注册该ContentProvider,指定android:authorities属性;
配置ContentProvider
android:name=".DictProvider" //类名
android:authorities="com.android.providers.dictprovider" //指定Uri
android:exported="true" //是否允许其他程序调用
/>
创建ContentProvider
ContentProvider只有一个生命周期方法:onCreate()方法,第一次访问ContentProvider时,onCreate()方法会被回调。
UriMatcher工具类提供了2个方法用来确定内容提供者实际能处理的Uri:
1>.void addRUI(String authority,String path,int code):用于向UriMathcher对象注册Uri.authority和path组合成一个Uri,而code则代表该Uri对应的标识符。
2>.int match(Uri uri):根据前面注册的Uri来判断指定uri对应的标识符,如果找不到匹配的标识码就返回-1.
二、使用ContentProvider操作数据
public class zouyongprovider extends ContentProvider
{
private DBOpenHelper dbhelper;
private static final UriMatcher MARCHER=new UriMatcher(UriMatcher.NO_MATCH);
private static final int USERS=1;
private static final int USER=2;
static
{
MARCHER.addURI("com.zouyong.provider.zouyongprovider", "user", USERS);
MARCHER.addURI("com.zouyong.provider.zouyongprovider", "user/#", USER);//#代表数字
}
public boolean onCreate() //对象被创建时调用,调用1次
{
this.dbhelper=new DBOpenHelper(getContext());
return true;
}
public Uri insert(Uri uri, ContentValues values) //插入数据
{
SQLiteDatabase db=dbhelper.getWritableDatabase();
switch (MARCHER.match(uri)) {
case USERS:
long rawid=db.insert("userinfo", "user", values);
Uri insertUri=ContentUris.withAppendedId(uri, rawid);
this.getContext().getContentResolver().notifyChange(insertUri, null);//发出数据变化通知
return insertUri;
default:
throw new IllegalArgumentException("Wrong Uri!");
}
}
public int delete(Uri uri, String selection, String[] selectionArgs) //删除数据
{
SQLiteDatabase db=dbhelper.getWritableDatabase();
int num=0;
switch (MARCHER.match(uri)) {
case USERS:
num=db.delete("userinfo", selection, selectionArgs);
break;
case USER:
long userid=ContentUris.parseId(uri);
String whereClause="_id="+userid;
if(selection!=null && !"".equals(selection.trim()))
whereClause=whereClause+" and "+selection;
num=db.delete("userinfo", whereClause, selectionArgs);
break;
default:
throw new IllegalArgumentException("Wrong Uri!");
}
return num;
}
public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) //更新数据
{
SQLiteDatabase db=dbhelper.getWritableDatabase();
int num=0;
switch (MARCHER.match(uri)) {
case USERS:
num=db.update("userinfo", values, selection, selectionArgs);
break;
case USER:
long userid=ContentUris.parseId(uri);
String whereClause="_id="+userid;
if(selection!=null && !"".equals(selection.trim()))
whereClause=whereClause+" and "+selection;
num=db.update("userinfo", values, whereClause, selectionArgs);
break;
default:
throw new IllegalArgumentException("Wrong Uri!");
}
return num;
}
public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)//外部应用查询
{
SQLiteDatabase db=dbhelper.getReadableDatabase();
switch (MARCHER.match(uri)) {
case USERS:
return db.query("userinfo", projection, selection, selectionArgs, null, null, sortOrder);//返回Cursor对象
case USER:
long userid=ContentUris.parseId(uri);
String whereClause="_id="+userid;
if(selection!=null && !"".equals(selection.trim()))
whereClause=whereClause+" and "+selection;
return db.query("userinfo", projection, whereClause, selectionArgs, null, null, sortOrder);//返回Cursor对象
default:
throw new IllegalArgumentException("Wrong Uri!");
}
}
public String getType(Uri uri)
//该方法用于返回当前Uri所代表数据的MIME类型,如果操作的数据数据集合类型,MIME类型字符串应该以vnd.android.cursor.dir/开头;如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头
{
switch (MARCHER.match(uri)) {
case USERS:
return "vnd.android.cursor.dir/user";
case USER:
return "vnd.android.cursor.item/user";
default:
throw new IllegalArgumentException("Wrong Uri!");
}
}
}
其他应用使用内容提供者的数据
public class ContentProviderTest extends AndroidTestCase
{
private static final String TAG="ContentProviderTest";
public void testInsert() throws Exception
{
Uri uri=Uri.parse("content://com.zouyong.provider.zouyongprovider/user");
ContentResolver resolver=this.getContext().getContentResolver();
ContentValues values=new ContentValues();
values.put("name", "zhangsan");
values.put("phone", "123456");
values.put("amount", "1222222");
resolver.insert(uri, values);
}
public void testDelete() throws Exception
{
Uri uri=Uri.parse("content://com.zouyong.provider.zouyongprovider/user/89");
ContentResolver resolver=this.getContext().getContentResolver();
resolver.delete(uri, null, null);
}
public void testUpdate() throws Exception
{
Uri uri=Uri.parse("content://com.zouyong.provider.zouyongprovider/user/89");
ContentResolver resolver=this.getContext().getContentResolver();
ContentValues values=new ContentValues();
values.put("name", "wangwu");
resolver.update(uri, values, null, null);
}
public void testQuery() throws Exception
{
Uri uri=Uri.parse("content://com.zouyong.provider.zouyongprovider/user/89");
ContentResolver resolver=this.getContext().getContentResolver();
Cursor cursor=resolver.query(uri, null, null, null, "_id asc");
while(cursor.moveToNext())
{
String name=cursor.getString(cursor.getColumnIndex("name"));
Log.i(TAG, name);
}
cursor.close();
}
}
三、监听ContentProvider中的数据变化
ContentProvider中:
public Uri insert(Uri uri, ContentValues values) //插入数据
{
......
this.getContext().getContentResolver().notifyChange(insertUri, null);
......
}
A应用插入数据:
public void insert(View view)
{
Uri uri=Uri.parse("content://com.zouyong.provider.zouyongprovider/user");
ContentResolver resolver=this.getContentResolver();
ContentValues values=new ContentValues();
values.put("name", "zhangsan");
values.put("phone", "8888888");
values.put("amount", "9999999");
resolver.insert(uri, values);
}
B应用中监听数据变化:
Uri uri=Uri.parse("content://com.zouyong.provider.zouyongprovider/user");
this.getContentResolver().registerContentObserver(uri, true, new UserContentObserver(new Handler()));
private class UserContentObserver extends ContentObserver
{
public UserContentObserver(Handler handler)
{
super(handler);
}
public void onChange(boolean selfChange)
{
Uri uri=Uri.parse("content://com.zouyong.provider.zouyongprovider/user");
ContentResolver resolver=getContentResolver();
Cursor cursor=resolver.query(uri, null, null, null, "_id desc limit 1");
if(cursor.moveToFirst())
{
String name=cursor.getString(cursor.getColumnIndex("name"));
Log.i(TAG, name);
}
cursor.close();
}
}
/***********监听用户发出的短信**************/
/****Uri为content://sms的数据改变即可监听到用户短信的数据变化,并在监听器的onChange(boolean selfChange)方法里查询Uri为content://sms/outbox的数据,这样即可获取用户正在发送的短信****************/
public class MonitorSms extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 为content://sms的数据改变注册监听器
getContentResolver().registerContentObserver(
Uri.parse("content://sms"), true,
new SmsObserver(new Handler()));
}
// 提供自定义的ContentObserver监听器类
private final class SmsObserver extends ContentObserver
{
public SmsObserver(Handler handler)
{
super(handler);
}
public void onChange(boolean selfChange)
{
// 查询发送箱中的短信(处于正在发送状态的短信放在发送箱)
Cursor cursor = getContentResolver().query(
Uri.parse("content://sms/outbox")
, null, null, null, null);
// 遍历查询得到的结果集,即可获取用户正在发送的短信
while (cursor.moveToNext())
{
StringBuilder sb = new StringBuilder();
// 获取短信的发送地址
sb.append("address=").append(cursor
.getString(cursor.getColumnIndex("address")));
// 获取短信的标题
sb.append(";subject=").append(cursor
.getString(cursor.getColumnIndex("subject")));
// 获取短信的内容
sb.append(";body=").append(cursor
.getString(cursor.getColumnIndex("body")));
// 获取短信的发送时间
sb.append(";time=").append(cursor
.getLong(cursor.getColumnIndex("date")));
Toast.makeText(getApplicationContext(), sb.toString(), 1).show();
}
}
}
}
四、调用系统的ContentProvider
访问通讯录中的联系人和添加联系人
Android系统对联系人管理ContentProvider的几个Uri如下:
ContactsContract.Contacts.CONTENT_URI:管理联系人的Uri
ContactsContract.CommonDataKinds.Phone.CONTENT_URI:管理联系人电话的Uri
ContactsContract.CommonDataKinds.Email.CONTENT_URI:管理联系人Email的Uri
/**********书籍中的例子******************/
public class ContactProviderTest extends Activity
{
Button search;
Button add;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取系统界面中查找、添加两个按钮
search = (Button) findViewById(R.id.search);
add = (Button) findViewById(R.id.add);
search.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View source)
{
// 定义两个List来封装系统的联系人信息、指定联系人的电话号码、Email等详情
final ArrayList names = new ArrayList();
final ArrayList> details
= new ArrayList>();
// 使用ContentResolver查找联系人数据
Cursor cursor = getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI, null, null,
null, null);
// 遍历查询结果,获取系统中所有联系人
while (cursor.moveToNext())
{
// 获取联系人ID
String contactId = cursor.getString(cursor
.getColumnIndex(ContactsContract.Contacts._ID));
// 获取联系人的名字
String name = cursor.getString(cursor.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME));
names.add(name);
// 使用ContentResolver查找联系人的电话号码
Cursor phones = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = " + contactId, null, null);
ArrayList detail = new ArrayList();
// 遍历查询结果,获取该联系人的多个电话号码
while (phones.moveToNext())
{
// 获取查询结果中电话号码列中数据。
String phoneNumber = phones.getString(phones
.getColumnIndex(ContactsContract
.CommonDataKinds.Phone.NUMBER));
detail.add("电话号码:" + phoneNumber);
}
phones.close();
// 使用ContentResolver查找联系人的Email地址
Cursor emails = getContentResolver().query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID
+ " = " + contactId, null, null);
// 遍历查询结果,获取该联系人的多个Email地址
while (emails.moveToNext())
{
// 获取查询结果中Email地址列中数据。
String emailAddress = emails.getString(emails
.getColumnIndex(ContactsContract
.CommonDataKinds.Email.DATA));
detail.add("邮件地址:" + emailAddress);
}
emails.close();
details.add(detail);
}
cursor.close();
// 加载result.xml界面布局代表的视图
View resultDialog = getLayoutInflater().inflate(
R.layout.result, null);
// 获取resultDialog中ID为list的ExpandableListView
ExpandableListView list = (ExpandableListView) resultDialog
.findViewById(R.id.list);
// 创建一个ExpandableListAdapter对象
ExpandableListAdapter adapter =
new BaseExpandableListAdapter()
{
// 获取指定组位置、指定子列表项处的子列表项数据
@Override
public Object getChild(int groupPosition,
int childPosition)
{
return details.get(groupPosition).get(
childPosition);
}
@Override
public long getChildId(int groupPosition,
int childPosition)
{
return childPosition;
}
@Override
public int getChildrenCount(int groupPosition)
{
return details.get(groupPosition).size();
}
private TextView getTextView()
{
AbsListView.LayoutParams lp = new AbsListView
.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, 64);
TextView textView = new TextView(
ContactProviderTest.this);
textView.setLayoutParams(lp);
textView.setGravity(Gravity.CENTER_VERTICAL
| Gravity.LEFT);
textView.setPadding(36, 0, 0, 0);
textView.setTextSize(20);
return textView;
}
// 该方法决定每个子选项的外观
@Override
public View getChildView(int groupPosition,
int childPosition, boolean isLastChild,
View convertView, ViewGroup parent)
{
TextView textView = getTextView();
textView.setText(getChild(groupPosition,
childPosition).toString());
return textView;
}
// 获取指定组位置处的组数据
@Override
public Object getGroup(int groupPosition)
{
return names.get(groupPosition);
}
@Override
public int getGroupCount()
{
return names.size();
}
@Override
public long getGroupId(int groupPosition)
{
return groupPosition;
}
// 该方法决定每个组选项的外观
@Override
public View getGroupView(int groupPosition,
boolean isExpanded, View convertView,
ViewGroup parent)
{
TextView textView = getTextView();
textView.setText(getGroup(groupPosition)
.toString());
return textView;
}
@Override
public boolean isChildSelectable(int groupPosition,
int childPosition)
{
return true;
}
@Override
public boolean hasStableIds()
{
return true;
}
};
// 为ExpandableListView设置Adapter对象
list.setAdapter(adapter);
// 使用对话框来显示查询结果。
new AlertDialog.Builder(ContactProviderTest.this)
.setView(resultDialog).setPositiveButton("确定", null)
.show();
}
});
// 为add按钮的单击事件绑定监听器
add.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
// 获取程序界面中的3个文本框
String name = ((EditText) findViewById(R.id.name))
.getText().toString();
String phone = ((EditText) findViewById(R.id.phone))
.getText().toString();
String email = ((EditText) findViewById(R.id.email))
.getText().toString();
// 创建一个空的ContentValues
ContentValues values = new ContentValues();
// 向RawContacts.CONTENT_URI执行一个空值插入,
// 目的是获取系统返回的rawContactId
Uri rawContactUri = getContentResolver().insert(
RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
// 设置内容类型
values
.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
// 设置联系人名字
values.put(StructuredName.GIVEN_NAME, name);
// 向联系人URI添加联系人名字
getContentResolver().insert(
android.provider.ContactsContract.Data.CONTENT_URI,
values);
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
// 设置联系人的电话号码
values.put(Phone.NUMBER, phone);
// 设置电话类型
values.put(Phone.TYPE, Phone.TYPE_MOBILE);
// 向联系人电话号码URI添加电话号码
getContentResolver().insert(
android.provider.ContactsContract.Data.CONTENT_URI,
values);
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
// 设置联系人的Email地址
values.put(Email.DATA, email);
// 设置该电子邮件的类型
values.put(Email.TYPE, Email.TYPE_WORK);
// 向联系人Email URI添加Email数据
getContentResolver().insert(
android.provider.ContactsContract.Data.CONTENT_URI,
values);
Toast.makeText(ContactProviderTest.this, "联系人数据添加成功",
Toast.LENGTH_LONG).show();
}
});
}
}
/**********视频教程中例子******************/
//往通信录中添加联系人
public void testAddContact()
{
Uri uri=Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver=this.getContext().getContentResolver();
ContentValues values=new ContentValues();
long contactid=ContentUris.parseId(resolver.insert(uri, values));
uri=Uri.parse("content://com.android.contacts/data");
//添加姓名
values.put("raw_contact_id", contactid);
values.put("mimetype", "vnd.android.cursor.item/name");
values.put("data2", "zhangsan");
resolver.insert(uri, values);
//添加电话
values.clear();
values.put("raw_contact_id", contactid);
values.put("mimetype", "vnd.android.cursor.item/phone_v2");
values.put("data2", "2");
values.put("data1", "999");
resolver.insert(uri, values);
//添加E-Mail
values.clear();
values.put("raw_contact_id", contactid);
values.put("mimetype", "vnd.android.cursor.item/email_v2");
values.put("data2", "2");
values.put("data1", "[email protected]");
resolver.insert(uri, values);
}
//在同一个事务中完成联系人各项数据的添加
public void testSaveContact() throws RemoteException, OperationApplicationException
{
Uri uri=Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver=this.getContext().getContentResolver();
ArrayList operations=new ArrayList();
ContentProviderOperation op1=ContentProviderOperation.newInsert(uri)
.withValue("account_name", null)//Gmail帐号
.build();
operations.add(op1);
uri=Uri.parse("content://com.android.contacts/data");
ContentProviderOperation op2=ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/name")
.withValue("data2", "lisi")
.build();
operations.add(op2);
ContentProviderOperation op3=ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/phone_v2")
.withValue("data2", "2")
.withValue("data1", "911")
.build();
operations.add(op3);
ContentProviderOperation op4=ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/email_v2")
.withValue("data2", "2")
.withValue("data1", "[email protected]")
.build();
operations.add(op4);
resolver.applyBatch("com.android.contacts", operations);
}
//查询所有联系人
public void testContacts()
{
Uri uri=Uri.parse("content://com.android.contacts/contacts");
ContentResolver resolver=this.getContext().getContentResolver();
Cursor cursor=resolver.query(uri, new String[]{"_id"}, null, null, null);
while(cursor.moveToNext())
{
StringBuilder sb=new StringBuilder();
long contactid=cursor.getInt(0);
sb.append("contactid:"+contactid+";");
uri=Uri.parse("content://com.android.contacts/contacts/"+contactid+"/data");
Cursor datacursor=resolver.query(uri, new String[]{"mimetype","data1","data2"}, null, null, null);
while(datacursor.moveToNext())
{
String data=datacursor.getString(datacursor.getColumnIndex("data1"));
String type=datacursor.getString(datacursor.getColumnIndex("mimetype"));
if(type.equals("vnd.android.cursor.item/name"))
{
sb.append("name:"+data+";");
}
if(type.equals("vnd.android.cursor.item/phone_v2"))
{
sb.append("phonenumer:"+data+";");
}
if(type.equals("vnd.android.cursor.item/email_v2"))
{
sb.append("email:"+data+".");
}
}
Log.i(TAG, sb.toString());
}
}
//根据号码查询联系人姓名
public void testContactByNumber()
{
String mobilenumber="999";
Uri uri=Uri.parse("content://com.android.contacts/data/phones/filter/"+mobilenumber);
ContentResolver resolver=this.getContext().getContentResolver();
Cursor cursor=resolver.query(uri, new String[]{"display_name"}, null, null, null);
if(cursor.moveToFirst())
{
String contactname="Name:"+cursor.getString(0);
Log.i(TAG, contactname);
}
cursor.close();
}
Android为多媒体提供的ContentProvider的Uri如下:
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI:存储在外置SD卡上音频内容的URI
MediaStore.Audio.Media.INTERNAL_CONTENT_URI:存储在内置SD卡上音频内容的URI
MediaStore.Images.Media.EXTERNAL_CONTENT_URI:存储在外置SD卡上图片文件的URI
MediaStore.Images.Media.INTERNAL_CONTENT_URI:存储在内置SD卡上图片文件的URI
MediaStore.Video.Media.EXTERNAL_CONTENT_URI:存储在外置SD卡上视频内容的URI
MediaStore.Video.Media.INTERNAL_CONTENT_URI:存储在内置SD卡上视频内容的URI
public class MediaProviderTest extends Activity
{
Button add;
Button view;
ListView show;
ArrayList names = new ArrayList();
ArrayList descs = new ArrayList();
ArrayList fileNames = new ArrayList();
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
add = (Button) findViewById(R.id.add);
view = (Button) findViewById(R.id.view);
show = (ListView) findViewById(R.id.show);
// 为add按钮的单击事件绑定监听器
add.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
// 创建ContentValues对象,准备插入数据
ContentValues values = new ContentValues();
values.put(Media.DISPLAY_NAME, "jinta");
values.put(Media.DESCRIPTION, "金塔");
values.put(Media.MIME_TYPE, "image/jpeg");
// 插入数据,返回所插入数据对应的Uri
Uri uri = getContentResolver().insert(
Media.EXTERNAL_CONTENT_URI, values);
// 加载应用程序下的jinta图片
Bitmap bitmap = BitmapFactory.decodeResource(
MediaProviderTest.this.getResources(),
R.drawable.jinta);
OutputStream os = null;
try
{
// 获取刚插入的数据的Uri对应的输出流
os = getContentResolver().openOutputStream(uri); //①
// 将bitmap图片保存到Uri对应的数据节点中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
os.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
});
// 为view按钮的单击事件绑定监听器
view.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
// 清空names、descs、fileNames集合里原有的数据。
names.clear();
descs.clear();
fileNames.clear();
// 通过ContentResolver查询所有图片信息
Cursor cursor = getContentResolver().query(
Media.EXTERNAL_CONTENT_URI, null, null, null, null);
while (cursor.moveToNext())
{
// 获取图片的显示名
String name = cursor.getString(cursor
.getColumnIndex(Media.DISPLAY_NAME));
// 获取图片的详细描述
String desc = cursor.getString(cursor
.getColumnIndex(Media.DESCRIPTION));
// 获取图片的保存位置的数据
byte[] data = cursor.getBlob(cursor
.getColumnIndex(Media.DATA));
// 将图片名添加到names集合中
names.add(name);
// 将图片描述添加到descs集合中
descs.add(desc);
// 将图片保存路径添加到fileNames集合中
fileNames.add(new String(data, 0, data.length - 1));
}
// 创建一个List集合,List集合的元素是Map
List