博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
content-providers
阅读量:5135 次
发布时间:2019-06-13

本文共 16063 字,大约阅读时间需要 53 分钟。

阅读:http://developer.android.com/guide/topics/providers/content-providers.html

When you want to access data in a content provider, you use the object in your application's to communicate with the provider as a client. The object communicates with the provider object, an instance of a class that implements . The provider object receives data requests from clients, performs the requested action, and returns the results.

  当我们想获得content provider,可以使用 ContentResolver对象来获得provider object ,继承ContentProvider的一个对象。provider object能够从用户获得请求并返回结果。


 

 

// Queries the user dictionary and returns resultsmCursor = getContentResolver().query(    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table    mProjection,                        // The columns to return for each row    mSelectionClause                    // Selection criteria    mSelectionArgs,                     // Selection criteria    mSortOrder);                        // The sort order for the returned rows

一般通过ContentResolver的方法来进行请求,相应的参数如下:

Table 2: Query() compared to SQL query.

query() argument SELECT keyword/parameter Notes
Uri FROM table_name Uri maps to the table in the provider named table_name.
projection col,col,col,... projection is an array of columns that should be included for each row retrieved.
selection WHERE col = value selection specifies the criteria for selecting rows.
selectionArgs (No exact equivalent. Selection arguments replace ? placeholders in the selection clause.)
sortOrder ORDER BY col,col,... sortOrder specifies the order in which rows appear in the returned .

 

   content URI是一个在provide里已经定义好的URI,它是用户请求的一个URI。形式像是这样:

content://user_dictionary/words

  


 

  你还可以通过URI直接连接到_ID为某个值的那行,使用这个方法:

  Uri singleUri =ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

  最后的参数为_ID的值。


 

官方文档中,建议使用异步操作进行数据的请求:

 


 

 

一般数据的请求还要有相关的权限。

 


 

 

/* * This defines a one-element String array to contain the selection argument. */String[] mSelectionArgs = {""};// Gets a word from the UImSearchString = mSearchWord.getText().toString();// Remember to insert code here to check for invalid or malicious input.// If the word is the empty string, gets everythingif (TextUtils.isEmpty(mSearchString)) {    // Setting the selection clause to null will return all words    mSelectionClause = null;    mSelectionArgs[0] = "";} else {    // Constructs a selection clause that matches the word that the user entered.    mSelectionClause = UserDictionary.Words.WORD + " = ?";    // Moves the user's input string to the selection arguments.    mSelectionArgs[0] = mSearchString;}// Does a query against the table and returns a Cursor objectmCursor = getContentResolver().query(    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table    mProjection,                       // The columns to return for each row    mSelectionClause                   // Either null, or the word the user entered    mSelectionArgs,                    // Either empty, or the string the user entered    mSortOrder);                       // The sort order for the returned rows// Some providers return null if an error occurs, others throw an exceptionif (null == mCursor) {    /*     * Insert code here to handle the error. Be sure not to use the cursor! You may want to     * call android.util.Log.e() to log this error.     *     */// If the Cursor is empty, the provider found no matches} else if (mCursor.getCount() < 1) {    /*     * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily     * an error. You may want to offer the user the option to insert a new row, or re-type the     * search term.     */} else {    // Insert code here to do something with the results}

以上就是一个例子,注意,之所以用“ = ? ” 以及后面的参数集合来查询,是为了避免用户非法操作。


 

查询会返回结果,如果是NULL则表示出错,而查询正确则会返回Cursor,如果查询结果为0,那么Cursor.getCount()==0;


 

 

// Defines a list of columns to retrieve from the Cursor and load into an output rowString[] mWordListColumns ={    UserDictionary.Words.WORD,   // Contract class constant containing the word column name    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name};// Defines a list of View IDs that will receive the Cursor columns for each rowint[] mWordListItems = { R.id.dictWord, R.id.locale};// Creates a new SimpleCursorAdaptermCursorAdapter = new SimpleCursorAdapter(    getApplicationContext(),               // The application's Context object    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView    mCursor,                               // The result from the query    mWordListColumns,                      // A string array of column names in the cursor    mWordListItems,                        // An integer array of view IDs in the row layout    0);                                    // Flags (usually none are needed)// Sets the adapter for the ListViewmWordList.setAdapter(mCursorAdapter);

上面例子表示了如何和适配器搭配使用。

Note: To back a with a , the cursor must contain a column named _ID. Because of this, the query shown previously retrieves the _ID column for the "words" table, even though the doesn't display it. This restriction also explains why most providers have a _ID column for each of their tables.


// Determine the column index of the column named "word"int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);/* * Only executes if the cursor is valid. The User Dictionary Provider returns null if * an internal error occurs. Other providers may throw an Exception instead of returning null. */if (mCursor != null) {    /*     * Moves to the next row in the cursor. Before the first movement in the cursor, the     * "row pointer" is -1, and if you try to retrieve data at that position you will get an     * exception.     */    while (mCursor.moveToNext()) {        // Gets the value from the column.        newWord = mCursor.getString(index);        // Insert code here to process the retrieved word.        ...        // end of while loop    }} else {    // Insert code here to report an error if the cursor is null or the provider threw an exception.}

如果需要获取某列的值,需要先获得某列的index再去获取相关值,关于值的类型可以用 getType()获取。


 

// Defines a new Uri object that receives the result of the insertionUri mNewUri;...// Defines an object to contain the new values to insertContentValues mNewValues = new ContentValues();/* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value" */mNewValues.put(UserDictionary.Words.APP_ID, "example.user");mNewValues.put(UserDictionary.Words.LOCALE, "en_US");mNewValues.put(UserDictionary.Words.WORD, "insert");mNewValues.put(UserDictionary.Words.FREQUENCY, "100");mNewUri = getContentResolver().insert(    UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI    mNewValues                          // the values to insert);

通过新建一个ContentValues来进行插入数据的存储,然后后面直接用insert插入,注意_ID是自动插入的不需要自己添加,返回的uri可以通过ContentUris.parseId().获取_ID。


 

 

// Defines an object to contain the updated valuesContentValues mUpdateValues = new ContentValues();// Defines selection criteria for the rows you want to updateString mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";String[] mSelectionArgs = {"en_%"};// Defines a variable to contain the number of updated rowsint mRowsUpdated = 0;.../* * Sets the updated value and updates the selected words. */mUpdateValues.putNull(UserDictionary.Words.LOCALE);mRowsUpdated = getContentResolver().update(    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI    mUpdateValues                       // the columns to update    mSelectionClause                    // the column to select on    mSelectionArgs                      // the value to compare to);

插入代码如上。


 

 

// Defines selection criteria for the rows you want to deleteString mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";String[] mSelectionArgs = {"user"};// Defines a variable to contain the number of rows deletedint mRowsDeleted = 0;...// Deletes the words that match the selection criteriamRowsDeleted = getContentResolver().delete(    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI    mSelectionClause                    // the column to select on    mSelectionArgs                      // the value to compare to);

删除的代码。

 

接下来谈谈Content-provider如何设计开发

首先,基于这样的需求你才需要CP:

1、与其他的APP有一定数据的交互;

2、需要用到搜索那块;

设计流程:

1、考虑数据形式:如果是文件,建议存放在私有空间,然后通过CP获得手柄去访问,又或者,只是单纯的数据结构,那么就可以通过类似数据库等来解决。

2、实现CP的类;

3、Define the provider's authority string, its content URIs, and column names.

 

此外,还需要考虑:主键的问题;对于文件数据还是存为文件然后让CP提供访问较好;

 

接下来,你还要设计名字,权限,结构等问题比较主要的是ID问题。

接下来就是,URI的配对问题了。

 

To help you choose which action to take for an incoming content URI, the provider API includes the convenience class , which maps content URI "patterns" to integer values. You can use the integer values in a switch statement that chooses the desired action for the content URI or URIs that match a particular pattern.

A content URI pattern matches content URIs using wildcard characters:

  • *: Matches a string of any valid characters of any length.
  • #: Matches a string of numeric characters of any length.

As an example of designing and coding content URI handling, consider a provider with the authority com.example.app.provider that recognizes the following content URIs pointing to tables:

  • content://com.example.app.provider/table1: A table called table1.
  • content://com.example.app.provider/table2/dataset1: A table called dataset1.
  • content://com.example.app.provider/table2/dataset2: A table called dataset2.
  • content://com.example.app.provider/table3: A table called table3.

The provider also recognizes these content URIs if they have a row ID appended to them, as for example content://com.example.app.provider/table3/1 for the row identified by 1 in table3.

The following content URI patterns would be possible:

content://com.example.app.provider/*
Matches any content URI in the provider.
content://com.example.app.provider/table2/*:
Matches a content URI for the tables
dataset1 and
dataset2, but doesn't match content URIs for
table1 or
table3.
content://com.example.app.provider/table3/#: Matches a content URI for single rows in
table3, such as
content://com.example.app.provider/table3/6 for the row identified by
6.

The following code snippet shows how the methods in work. This code handles URIs for an entire table differently from URIs for a single row, by using the content URI pattern content://<authority>/<path> for tables, and content://<authority>/<path>/<id> for single rows.

The method maps an authority and path to an integer value. The method returns the integer value for a URI. A switch statement chooses between querying the entire table, and querying for a single record:

 

public class ExampleProvider extends ContentProvider {...    // Creates a UriMatcher object.    private static final UriMatcher sUriMatcher;...    /*     * The calls to addURI() go here, for all of the content URI patterns that the provider     * should recognize. For this snippet, only the calls for table 3 are shown.     */...    /*     * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used     * in the path     */    sUriMatcher.addURI("com.example.app.provider", "table3", 1);    /*     * Sets the code for a single row to 2. In this case, the "#" wildcard is     * used. "content://com.example.app.provider/table3/3" matches, but     * "content://com.example.app.provider/table3 doesn't.     */    sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);...    // Implements ContentProvider.query()    public Cursor query(        Uri uri,        String[] projection,        String selection,        String[] selectionArgs,        String sortOrder) {...        /*         * Choose the table to query and a sort order based on the code returned for the incoming         * URI. Here, too, only the statements for table 3 are shown.         */        switch (sUriMatcher.match(uri)) {            // If the incoming URI was for all of table3            case 1:                if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";                break;            // If the incoming URI was for a single row            case 2:                /*                 * Because this URI was for a single row, the _ID value part is                 * present. Get the last path segment from the URI; this is the _ID value.                 * Then, append the value to the WHERE clause for the query                 */                selection = selection + "_ID = " uri.getLastPathSegment();                break;            default:            ...                // If the URI is not recognized, you should do some error handling here.        }        // call the code to actually do the query    }

然后,继承CP需要重写一些方法:

 

 

query()

根据所提出的要求返回Cursor。Cursor有可能需要自己去实现

insert()

插入新数据,返回新数据的URI

update()
更新数据,返回更新的数目。
delete()
删除,返回删除数目。
getType()
    Return the MIME type corresponding to a content URI. This method is described in more detail in the section Implementing Content Provider MIME Types.
onCreate()

当ContentResolver使用的时候就会启动这个方法,初始化CP吧!

Your implementation of these methods should account for the following:

  • All of these methods except can be called by multiple threads at once, so they must be thread-safe. To learn more about multiple threads, see the topic .
  • Avoid doing lengthy operations in . Defer initialization tasks until they are actually needed. The section discusses this in more detail.
  • Although you must implement these methods, your code does not have to do anything except return the expected data type. For example, you may want to prevent other applications from inserting data into some tables. To do this, you can ignore the call to and return 0.

   除了oncreate之外,其他方法都要加上线程安全。

  不要再oncreate执行太多操作。

  并不是每个方法都要很认真的返回所要求的数据,根据需求看吧!

 

  接下来就是关于CP的权限了,是在manifest立马配置的,而且,有多种类型:

Single read-write provider-level permission
一次性定义读和写权限;
Separate read and write provider-level permission
分开定义android:readPermission and android:writePermission。
Path-level permission
定义特定路径下的路径,详细见官方。
Temporary permission   可赋予其他APP临时权限。

 

Like and components, a subclass of must be defined in the manifest file for its application, using the element. The Android system gets the following information from the element:

Authority ( )
Symbolic names that identify the entire provider within the system. This attribute is described in more detail in the section .
Provider class name (
)
The class that implements
. This class is described in more detail in the section .
Permissions
Attributes that specify the permissions that other applications must have in order to access the provider's data:
  • : Temporary permission flag.
  • : Single provider-wide read/write permission.
  • : Provider-wide read permission.
  • : Provider-wide write permission.

Permissions and their corresponding attributes are described in more detail in the section .

Startup and control attributes
These attributes determine how and when the Android system starts the provider, the process characteristics of the provider, and other run-time settings:
  • : Flag allowing the system to start the provider.
  • : Flag allowing other applications to use this provider.
  • : The order in which this provider should be started, relative to other providers in the same process.
  • : Flag allowing the system to start the provider in the same process as the calling client.
  • : The name of the process in which the provider should run.
  • : Flag indicating that the provider's data is to be sync'ed with data on a server.

The attributes are fully documented in the dev guide topic for the element.

Informational attributes
An optional icon and label for the provider:
  • : A drawable resource containing an icon for the provider. The icon appears next to the provider's label in the list of apps in Settings > Apps > All.
  • : An informational label describing the provider or its data, or both. The label appears in the list of apps in Settings > Apps > All.

The attributes are fully documented in the dev guide topic for the element.

 

 

 

 

 

转载于:https://www.cnblogs.com/yutoulck/p/3382406.html

你可能感兴趣的文章
Intellij IDEA(eclipse设置)常用快捷键
查看>>
深入理解Java:注解(Annotation)基本概念
查看>>
NAT基本原理
查看>>
Java Content Repository API 简介 转自(https://www.ibm.com/developerworks/cn/java/j-jcr/)
查看>>
visio二次开发——图纸解析
查看>>
Activity之间的跳转:
查看>>
iTunes Connect 开发者上手经验(转)
查看>>
vertical-align你为什么不生效
查看>>
C++ 实践总结
查看>>
composer 国内镜像配置
查看>>
软件是天时、地利、人和的产物!
查看>>
python定时清空本目录下除本脚本外的全部文件
查看>>
【PHP】在目标字符串指定位置插入字符串
查看>>
【JS】jQuery设置定时器,访问服务器(PHP示例)配合微信、支付宝原生支付,跳转web网页...
查看>>
实验四2
查看>>
VS2012+Win7网站发布详细步骤
查看>>
Android现学现用第十一天
查看>>
Bin Packing 装箱问题——NPH问题的暴力枚举 状压DP
查看>>
多路复用
查看>>
python 列表
查看>>