四大组件之ContentProvider(二)-轻轻松松自定义ContentProvider

来源:互联网 发布:好看的悬疑网络剧 编辑:程序博客网 时间:2024/04/28 22:31
更新时间 修改意见 2016-08-02 陈敏

第3节 自定义ContentProvider

自定义一个ContentProvider,需要

  1. 继承ContentProvider类重新创建一个类,并实现其中的一些方法;
  2. 在应用的AndroidManifest.xml文件中,声明这个新添加的组件;

但在这之前,我们要来设计一下外部访问它采用的地址。

3.1 地址设计

Urischeme字段是固定的,使用content:

authority定义成程序的包名com.anddle.mycontentprovider

path就像是网站内部的分类,依据网站的逻辑进行划分。
假设我们的ContentProvider提供书籍book和文件file两种内容的查询操作。而每种类型都可以进行单一数据的操作和多条数据的操作。

例如,

  • 操作所有书的信息:content://com.anddle.mycontentprovider/books;
  • 操作某本特定书的信息:content://com.anddle.mycontentprovider/books/8
  • 操作所有文件的信息:content://com.anddle.mycontentprovider/files
  • 操作某个特定文件的信息:content://com.anddle.mycontentprovider/files/3

所以,在对这些地址代表的对象进行增删改查的时候,就需要分析出它们针对的对象。

3.2 创建ContentProvider子类

  1. 继承ContentProvider类,会要求我们实现getType() insert() delete() update() query() onCreate()等接口,

    public class MyContentProvider extends ContentProvider {    @Override    public boolean onCreate() {        return true;    }    @Override    public String getType(Uri uri) {        throw new UnsupportedOperationException("Not yet implemented");    }    @Override    public Uri insert(Uri uri, ContentValues values) {        throw new UnsupportedOperationException("Not yet implemented");    }    @Override    public int delete(Uri uri, String selection, String[] selectionArgs) {        throw new UnsupportedOperationException("Not yet implemented");    }    @Override    public int update(Uri uri, ContentValues values, String selection,                      String[] selectionArgs) {        throw new UnsupportedOperationException("Not yet implemented");    }    @Override    public Cursor query(Uri uri, String[] projection, String selection,                        String[] selectionArgs, String sortOrder) {        throw new UnsupportedOperationException("Not yet implemented");    }}
  2. 定义提供给其他组件使用的“网络地址”URI,这里我们把它们定义成content://com.anddle.mycontentprovider/books content://com.anddle.mycontentprovider/files

    private static final String SCHEME = "content://";private static final String PATH_BOOKS = "/books";private static final String PATH_FILES = "/files";public static final String AUTHORITY = "com.anddle.mycontentprovider";//"content://com.anddle.mycontentprovider/books"public static final Uri CONTENT_BOOKS_URI = Uri.parse(SCHEME + AUTHORITY + PATH_BOOKS);//"content://com.anddle.mycontentprovider/files"public static final Uri CONTENT_FILES_URI = Uri.parse(SCHEME + AUTHORITY + PATH_FILES);
  3. 定义解析地址的匹配器,

    private static final int BOOKS = 0;private static final int BOOK = 1;private static final int FILES = 2;private static final int FILE = 3;private static final UriMatcher sUriMatcher;static {    sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);    sUriMatcher.addURI(AUTHORITY, PATH_BOOKS, BOOKS);    sUriMatcher.addURI(AUTHORITY, PATH_BOOKS+"/#", BOOK);    sUriMatcher.addURI(AUTHORITY, PATH_FILES, FILES);    sUriMatcher.addURI(AUTHORITY, PATH_FILES+"/#", FILE);}

    UriMatcher可以对传入的字符串进行匹配检测,如果匹配成功,会返回一个对应的值,例如,

    int type = sUriMatcher.match(Uri.parse("content://com.anddle.mycontentprovider/files"));//type就等于FILES的值2

    type就等于FILES的值2。利用这个方法,我们就可以区分出Uri访问ContentProvider时,到底希望操作什么样的数据。

    创建匹配器的时候,加入sUriMatcher.addURI("content://com.anddle.mycontentprovider","files/#", FILE);这种带有”#”的关键字段,表示如下这种匹配方式,即匹配任何数字

    content://com.anddle.mycontentprovider/files/0content://com.anddle.mycontentprovider/files/1content://com.anddle.mycontentprovider/files/3

    创建匹配器的时候,加入sUriMatcher.addURI("content://com.anddle.mycontentprovider","files/*", FILE);这种带有”#”的关键字段,表示如下这种匹配方式,即匹配任何字符

    content://com.anddle.mycontentprovider/files/how-to-programcontent://com.anddle.mycontentprovider/files/biblecontent://com.anddle.mycontentprovider/files/101story
  4. 在回调函数中,根据Uri,做对应的操作,

    @Overridepublic Uri insert(Uri uri, ContentValues values) {   Uri result = null;
   switch (sUriMatcher.match(uri)) {       case BOOKS: {            //从ContentValues中取出数据,保存起来;返回保存数据的Uri地址,例如            //content://com.anddle.mycontentprovider/books       }       break;       case BOOK: {            //从ContentValues中取出数据,保存起来;返回保存数据的Uri地址,例如            //content://com.anddle.mycontentprovider/books/8       }       break;       case FILES: {            //从ContentValues中取出数据,保存起来;返回保存数据的Uri地址,例如            //content://com.anddle.mycontentprovider/files       }       break;       case FILE: {            //从ContentValues中取出数据,保存起来;返回保存数据的Uri地址,例如            //content://com.anddle.mycontentprovider/files/8       }       break;       default:           throw new IllegalArgumentException("Unknown URI " + uri);   }   return result;}

    这里就要实现对数据增删改查的真正操作。

    ContentProvider中,可以使用很多方式对数据进行保存、修改,例如SQL数据库。不过我们暂时不去实现,把它放到下一个章节专门介绍。

    其他delete() update() query()实现的函数做类似处理。

  5. 对于getType(), 需要为每一种类型的Uri返回一种数据类型-MIME type,告诉调用者,当前这种Uri可以处理什么类型的数据。

    它的格式型如type\subtype,有很多知名的MIME type类型,例如application/pdf image/jpeg等等,可以在网上查找到公开的MIME type类型有哪些。也可以自定义自己应用支持的特殊MIME type类型。

    这里我们就返回一个空值,

    @Overridepublic String getType(Uri uri) {   return null;}

至此,一个ContentProvider的就完成了。不过它现在还没有添加上真正可以存储数据的功能。

3.3 声明ContentProvider

千万不要忘记,在应用的AndroidManifest.xml文件中,声明新添加的ContentProvider

<provider    android:name=".MyContentProvider"    android:authorities="com.anddle.mycontentprovider"    android:enabled="true"    android:exported="true" />

这里的android:authorities属性值,就要填写定义MyContentProvider时,代码中的那个,

public static final String AUTHORITY = "com.anddle.mycontentprovider";

android:exported属性如何设置成true,说明这个ContentProvider可以被其他应用使用(就像一个公共网站,可以被任何人访问),如果设置成false,说明它只能被自己所在的应用使用(就像一个内部网站,只能在公司内部访问)。

3.4 使用自定义ContentProvider

无论是使用应用自己的ContentProvider还是使用其他应用提供的,它们的使用方式都和使用系统提供的ContentProvider一样,

  1. 添加一条数据数据:通过ContentResolver获取访问ContentProvider的入口,使用ContentValues添加要插入的数据;

    ContentResolver cr = getContentResolver();ContentValues cv = new ContentValues();cv.put("数据字段名称", "数据内容");Uri uri = cr.insert(Uri.parse("content://com.anddle.mycontentprovider/books"), cv)

    通常会返回指向刚成功插入的这条数据的Uri(内容就如content://com.anddle.mycontentprovider/books/8)。

  2. 删除一条数据:通过ContentResolver获取访问ContentProvider的入口,使用Uri删除指定的数据;

    String where = null;String [] keywords = null;ContentResolver cr = getContentResolver();cr.delete(Uri.parse("content://com.anddle.mycontentprovider/books/8"), where, keywords);
  3. 修改一条数据:通过ContentResolver获取访问ContentProvider的入口,使用Uri更新指定的数据,要修改的数据放在ContentValues当中;

    String where = null;String [] keywords = null;ContentResolver cr = getContentResolver();ContentValues cv = new ContentValues();cv.put("数据字段名称", "新数据内容",where,keywords);cr.update(Uri.parse("content://com.anddle.mycontentprovider/books"), cv, where, keywords)
  4. 查询某一类的数据(或者特定某条数据),

    Uri uri = MyContentProvider.CONTENT_BOOKS_URI;String[] searchKey = null;String where = null;String [] keywords = null;String sortOrder = null;ContentResolver resolver = getContentResolver();Cursor cursor = resolver.query(                    uri,                    searchKey,                     where,                     keywords,                     sortOrder);if(cursor != null) {    while(cursor.moveToNext()) {           ......      }    cursor.close();}

注意,在删改查的操作中,还会使用诸如where sortOrder keywords searcgKey这样的参数,它们是辅助ContentProvider查询特定数据时用的。

虽然Uri已经能定位到某条具体的数据了,但是大部分的ContentProvider都是通过SQL数据库来实现的真正存储,因此,在设计这些接口的时候就保留了SQL语言的一些用法,让使用者也能像直接操作SQL数据库那样灵活方便的使用ContentProvider

假如ContentProvider不用SQL实现数据存储功能,而采用别的存储机制,那么这些额外的参数就可以派上别的用场或者完全用不着。


/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。

*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店跟我学Arduino编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。

*最后再次感谢各位读者对安豆的支持,谢谢:)
/*******************************************************************/

0 0
原创粉丝点击