ContentProvider是安卓平台中,在不同应用程序之间实现数据共享的一种机制。一个应用程序如果需要让别的程序可以操作自己的数据,即可采用这种机制。并且此种方式忽略了底层的数据存储实现,ContentProvider提供了一种统一的通过Uri实现数据操作的方式。Android已经为常见的一些数据提供了默认的ContentProvider,比如联系人、短信、图片、音视频等。
当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数 据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数 据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。
使用ContentProvider我们经常会用到三个类:
ContentProvider、Uri、ContentResolver
1、 定义自己的ContentProvider类,重写以下方法
public boolean onCreate():该方法在ContentProvider创建后就会被调用,Android开机后,ContentProvider在其它应用第一次访问它时才会被创建。
安装应用并第一次运行的时候才会回调onCreate, 在此创建helper
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中获取数据。
public String getType(Uri uri):该方法用于返回当前Url所代表数据的MIME类型。用不到直接返回null
2. ContentProvider是Android中的四大组件之一 ,所以需要在AndroidManifest.xml文件中进行注册。注册成功之后,其他应用程序就可以通过该Uri来访问。注册的时候,与注册Activity类似
android:name="com.qianfeng.MyProvider" //自定义的ContentProvider的类名 android:authorities="com.qianfeng.provider" //Uri标示,相当于当前网站的主机 android:exported="true" > //true代表暴露给另外的程序 在需要获得数据的应用程序中使用ContentResolver 1、 获得ContentResolver对象: 调用Context对象的getContentResolver()方法,获取当前应用默认的ContentResolver对象。 2、 获得ContentProvider对象之后,就可以调用他的下面的方法来操作数据: a: insert(Uri,values) 插入数据 b: delete(uri, where, selectionArgs) 删除数据 c: update(…) 更新数据 d: query(…) 查询数据 e:getType暂时不管 注意:ContentProvider负责暴露数据、ContentResolver操作暴露出来的数据 ContentResolver和ContentProvider的深层理解 (一)Uri的简介 在android中可以为系统的每一个资源给起一个名字,这个名字就是这个资源的Uri。 在ContentProvider中使用的Uri非常类似于我们以前访问网站使用Url (二)Uri的结构 一个Uri: content://qianfeng.com.providers/abc/2 主要可以分为三部分: 1、content:// 是ContentProvider规定好的。协议部分,表示ContentProvider使用的协议必须是content:// 2、qianfeng.com.providers : URI 的标识,用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称 前两部分必须有----匹配清单中的authorities属性 File:// Tel:// http:// 3、abc : 路径。通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就可以了;ContentProvider怎么定义,访问的时候就要怎么写。 4、2 :如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部 为了将字符串转换成Uri,可以使用Uri中的静态方法: Uri uri = Uri.parse(“”); 路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下: 7.#代表通配数字 (三)Uri的匹配----为了验证uri传入得是否正确,也为了针对不同的请求做出不同的响应 因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。 Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris 。 ContentURI的全局匹配流程图 到匹配的第二步,我们已经匹配到了content://com.example.transportionprovider,那后面的/trains/122的匹配工作就只有交由ContentProvider来处理了。 在ContentProvder中的匹配是利用UriMatcher来完成的! A:UriMatcher: UriMatcher的匹配工作的第一步就是先将所需要的匹配的URI使用addURI()添加到UriMatcher中,这个步骤是外界还没有访问数据的时候就要作的 public void addURI(String authority, String path, int code) 其中第一个参数authority:就是URI对应的authority path:就是我们在URI中 authority后的那一串 code:表示匹配成功以后的返回值; 第二步是匹配Uri,使用方法int match(Uri uri) 当有人调用当前ContentProvider的CRUD的时候,我们可以使用这个方法来进行传来的Uri和我们注册的Uri匹配,如果成功就执行,否则不执行。 注意:至于到底需要为UriMatcher对象注册多少个Uri,则取决于系统的业务需求。 具体使用方式是: a.在ContentProvider中添加以下代码注册Uri: private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); static{ //当有Uri匹配的时候返回1 *可以匹配任意的字符串 #可以匹配任意的整型数字 matcher.addURI(AUTHORITY, "person", 1); // content://com.qianfeng.person.provider/person matcher.addURI(AUTHORITY, "user", 2); // content://com.qianfeng.person.provider/user matcher.addURI(AUTHORITY, "human", 3); //content://com.qianfeng.person.provider/human } b.在query()、insert()、update()、delete()等方法中添加以下代码来检测Uri if(matcher.match(uri) == 1){ db.insert("person", null, values); .... } 如果不匹配,抛出异常: throw new IllegalArgumentException("Unknown URI " + uri); B: ContentUris: 这个类是一个操作Uri字符串的工具类,主要是拼接Uri字符串用。 withAppendedId(uri, id) 用于为路径加上id部分 parseId(uri) 用于从指定的Uri中解析出所包含的id 工作中,如果开启线程不断的查询某个数据库的内容是否发生变化,会导致系统的开销很大,利用ContentObserver可以完美的解决该问题。 ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,当ContentObserver所观察的Uri发生变化时,便会触发它。熟悉Content Provider(内容提供者)的应该知道,我们可以通过UriMatcher类注册不同类型的Uri,我们可以通过这些不同的Uri来查询不同的结果。那么我们也可以监视Uri所指定的数据的变化情况 注册观察者 ContentResolver类中注册内容观察者的方法原型如下: public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer) 功能:为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。 参数1:uri 要观察的Uri(需要在UriMatcher里注册,否则该Uri也没有意义了) 参数2:notifyForDescendents 为false 表示精确匹配,即只匹配该Uri 为true 表示可以同时匹配其派生的Uri, 举例如下: 假设UriMatcher 里注册的Uri共有一下类型: 1 、content://com.qin.cb/student (学生) 2 、content://com.qin.cb/student/# 3、 content://com.qin.cb/student/schoolchild(小学生,派生的Uri) 假设我们当前需要观察的Uri为content://com.qin.cb/student,如果发生数据变化的 Uri 为content://com.qin.cb/student/schoolchild ,当notifyForDescendents为 false,那么该ContentObserver会监听不到,但是当notifyForDescendents 为ture,能捕捉该Uri的数据库变化。 参数3:observer 定义类继承ContentObserver, 重写onChange()方法,构建该类实例传入进行注册,在收到数据改变通知之后, 会自动执行onChange()方法 例如在访问数据库的相关应用程序中添加: getContentResolver().registerContentObserver(uri,true,new MyObserver()); public class MyObserver extends ContentObserver { public MyObserver(Handler handler) { super(handler); // TODO Auto-generated constructor stub } public MyObserver() { // TODO Auto-generated constructor stub super(new Handler()); } @Override public void onChange(boolean selfChange) { // 此处写当数据变化的时候的处理代码 } } 通知观察者 注册观察者之后, 需要在ContentProvider中进行通知, 观察者才能收到, 使用ContentResolver.notifyChange()方法可以通知数据的改变 例如在contentprovider的数据改变的方法中添加: getContext().getContentResolver().notifyChange(uri, null); xml中主要配置有四个属性,如下: android:iconifiedByDefault表示搜索图标是否在输入框内。true效果更佳 android:imeOptions设置IME options,即输入法的回车键的功能,可以是搜索、下一个、发送、完成等等。这里actionSearch表示搜索 android:inputType输入框文本类型,可控制输入法键盘样式,如numberPassword即为数字密码样式 android:queryHint输入框默认文本 主要方法: setOnCloseListener(SearchView.OnCloseListener listener)表示注册点击取消按钮时的listener,默认点击搜索输入框 setOnQueryTextListener(SearchView.OnQueryTextListener listener)表示输入框文字变化的listener,包括public boolean onQueryTextSubmit(String query)和public boolean onQueryTextChange(String newText)两个方法
3.Uri类的介绍和Uri字符串的定义
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.* 代表通配所有字符
三、内容观察者----了解------Loader
四、SearchView的使用