Android Contentprovider 经验总结
来源:互联网 发布:云播软件 编辑:程序博客网 时间:2024/06/01 07:51
Android提供了5种方式存储数据。
(1)SQLite数据库存储数据
(2)文件存储数据
(3)网络存储数据
(4)SharedPreferences存储数据
(5)ContentProvider存储数据
ContentProvider
1、适用场景
(1)ContentProvider为存储和读取数据提供了统一的接口
(2)使用ContentProvider,应用程序可以实现数据共享
(3)android内置的许多数据都是使用ContentProvider形式,供开发者调用的(如视频,音频,图片,通讯录等)
2、ContentProvider介绍
(1)ContentProvider简介 App中新建一个类继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。
(2)Uri 通用资源标志符(Universal Resource Identifier, 简称"URI")简介 Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact") 在Content Provider中使用的查询字符串有别于标准的SQL查询。很多诸如select, add, delete, modify等操作我们都使用一种特殊的URI来进行,这种URI由3个部分组成, “content://”, 代表数据的路径,和一个可选的标识数据的ID。
举个实际的例子:
content://com.example.project:200/folder/subfolder/etc
\---------/ \---------------------------/ \---/ \--------------------------/
scheme host port path
\--------------------------------/
authority
1.scheme:ContentProvider(内容提供者)的scheme已经由Android所规定为:content://。
2.主机名(或Authority):用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。
3.路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
content://media/internal/images 这个URI将返回设备上存储的所有图片
content://contacts/people/ 这个URI将返回设备上的所有联系人信息
content://contacts/people/45 这个URI返回单个结果(联系人信息中ID为45的联系人记录)
尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,Android提供一系列的帮助类(在android.provider包下),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,因此,如上面content://contacts/people/45这个URI就可以写成如下形式:
Uri person = ContentUris.withAppendedId(People.CONTENT_URI, 45);
然后执行数据查询:
Cursor cur = managedQuery(person, null, null, null);
这个查询返回一个包含所有数据字段的游标,可以通过迭代这个游标来获取所有的数据:
cursor = contentResolver.query(IPersonProvider.CONTENT_URI, new String[] { IPersonProvider.PERSON_ID,IPersonProvider.PERSON_NAME, IPersonProvider.PERSON_AGE }, null, null, "_id"); if (cursor != null && cursor.moveToFirst()) { Person p = new Person(); do { p.setName(cursor.getString(cursor.getColumnIndexOrThrow(IPersonProvider.PERSON_NAME))); p.setId(cursor.getString(cursor.getColumnIndexOrThrow(IPersonProvider.PERSON_ID))); p.setAge(cursor.getInt(cursor.getColumnIndexOrThrow(IPersonProvider.PERSON_AGE))); str.append(" person.id="+p.getId()+" person.name="+p.getName()+" person.age="+p.getAge()+ "\n"); textView.setText(str.toString()); }while (cursor.moveToNext()); } else { Log.i("TAG", "query failure!"); }
使用ContentResolver.update()方法来修改数据,写一个修改数据的方法,修改指定id的行的人的姓名。updateRecord(1,"ssss")修改第1条记录的人姓名为ssss:
private void updateRecord(int recNo, String name) { Uri uri = ContentUris.withAppendedId(IPersonProvider.CONTENT_URI, recNo); ContentValues values = new ContentValues(); values.put(IPersonProvider.PERSON_NAME, name); getContentResolver().update(uri, values, null, null); }
使用ContentResolver.insert()方法来插入数据,该方法接受一个要增加的记录的目标URI,以及一个包含了新记录值的Map对象,调用后的返回值是新记录的URI,包含记录号。
private int insertData(Person person) { ContentValues values = new ContentValues(); values.put(IPersonProvider.PERSON_NAME,person.getName()); values.put(IPersonProvider.PERSON_AGE,person.getAge()); Uri uri = getContentResolver().insert(IPersonProvider.CONTENT_URI,values); String lastPath = uri.getLastPathSegment(); if (TextUtils.isEmpty(lastPath)) { Log.i(TAG, "insert failure!"); } else { Log.i(TAG, "insert success! the id is " + lastPath); } return Integer.parseInt(lastPath); }使用ContentResolver.insert()方法来删除数据:
删除该表所有信息
private void deleteRecords() { Uri uri =IPersonProvider.CONTENT_URI; getContentResolver().delete(uri, null, null); }删除指定姓名的行
private void deleteRecords(String name) { Uri uri =IPersonProvider.CONTENT_URI; getContentResolver().delete(uri, IPersonProvider.PERSON_NAME + " = 'Michelle'",null ); }
3、创建ContentProvider
当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:
public class PersonContentProvider extends ContentProvider{ public boolean onCreate() public Uri insert(Uri uri, ContentValues values) public int delete(Uri uri, String selection, String[] selectionArgs) public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) public String getType(Uri uri)}
并定义需要用到常量数据
public interface IPersonProvider extends BaseColumns { public static final String AUTHORITY = "com.caidongdong.contentprovider.person"; public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person"; public static final String PERSON_NAME = "name"; public static final String PERSON_AGE = "age"; public static final Uri CONTENT_URI = Uri.parse("content://"+ AUTHORITY +"/persons"); public static final String TABLE_NAME = "person"; public static final String DEFAULT_SORT_ORDER = "age desc";}第二步需要在AndroidManifest.xml使用对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider ,ContentProvider采用了authorities(主机名/域名)对它进行唯一标识,你可以把ContentProvider看作是一个网 站(想想,网站也是提供数据者),authorities 就是他的域名:
<!-- 注意这个地方的位置,是在application标签里面;android:authorities对应Provider.AUTHORITY --> <provider android:name=".provider.PersonProvider" android:authorities="com.caidongdong.contentprovider.person" android:multiprocess="true" android:exported="true"> </provider>创建你的数据存储系统。大多数Content Provider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。如果你要存储字节型数据,比如位图文件等,数据列其实是一个表示实际保存文件的URI字符串,通过它来读取对应的文件数据。处理这种数据类型的Content Provider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源;如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。
4、监听ContentProvider数据变化
如果ContentProvider的访问者需要知道ContentProvider中的数据发生变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子如下:
public class PersonContentProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { db.insert("person", "personid", values); getContext().getContentResolver().notifyChange(uri, null); }}如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:
getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"), true, new PersonObserver(new Handler()));public class PersonObserver extends ContentObserver{ public PersonObserver(Handler handler) { super(handler); } public void onChange(boolean selfChange) { //此处可以进行相应的业务处理 }}
5、权限设置与线程同步
(1)可以在代码中通过setReadPermission()和setWritePermission()两个方法来设置ContentProvider的操作权限,也可以在配置文件中通过android:readPermission和android:writePermission属性来控制。
(2)因为ContentProvider可能被不同的进程和线程调用,所以里面的方法必须是线程安全的。
自定义ContentProvider读写权限
<permission android:name="com.caidongdong.contentprovider.person.read" android:label="provider pomission" android:protectionLevel="normal"/> <permission android:name="com.caidongdong.contentprovider.person.write" android:label="provider write pomission" android:protectionLevel="normal" />在provider标签中也需要设置改权限才有效
<provider android:name=".provider.PersonProvider" android:authorities="com.caidongdong.contentprovider.person" android:readPermission="com.caidongdong.contentprovider.person.read" android:writePermission="com.caidongdong.contentprovider.person.write" android:multiprocess="true" android:exported="true">这里对读写都加了权限,若第三方app想要获得数据,就必须要在Manifest.xml中获得读和写的权限
<uses-permission android:name="com.caidongdong.contentprovider.person.read" />如果这里不申请写权限,那么是没有办法向提供数据的app写入数据的。
提一下这个android:multiprocess="true",数据可能被不同的第三方app同时访问,要保持数据的同步线程安全就需要配置进程之间是否是单例,这句代码就是使用单例模式的意思。
如果你对下面几个问题感兴趣,那么就点击这个链接
1. 在应用程序A里面怎么跨进程拿到ContentProvider的对象呢?
2. ContentProvider实例对象是保存在哪里呢?
3. ContentProvider的方法实现要注意线程安全吗?
6、效果展示
附上自己做的demo图,一个提供数据源的app,一个第三方访问ContentProvider的app。
访问ContentProvider的app
需要源码的请留下邮箱
- Android Contentprovider 经验总结
- android contentprovider
- android contentprovider
- Android ContentProvider
- android ContentProvider
- Android ContentProvider
- android ContentProvider
- android ContentProvider
- Android ContentProvider
- Android ContentProvider
- android ContentProvider
- Android ContentProvider
- android contentprovider
- android contentProvider
- Android ContentProvider
- Android ContentProvider
- Android ContentProvider
- Android ContentProvider
- 使用Redis存储Nginx Tomcat负载均衡集群的Session
- SlidingMenu侧滑菜单
- Java并发编程:线程池的使用
- Akka简单的性能测试
- 二维码
- Android Contentprovider 经验总结
- IOS 开发过程中问题汇总
- 项目之JSP标签定义时URI报错的问题(产品追溯系统)2016-7-29
- java??List?LinkedList?ArrayList ??
- IOS 个人苹果开发者账号申请流程
- OSG测试gl.h编译出错
- 【poj 3714 / hdu 1007 /uva 10245】经典题 平面上最近的点对
- Oracle连接类型
- 用户界面框架jQuery EasyUI示例大全之Tree