Content Provider 之Creating a Content Provider

Android API Guides


需求:
1. 需要提供复杂数据或者文件给其他应用。
2. 允许用户从你的App复制复杂数据到其他Apps
3. 使用搜索框架提供自定义的搜索建议

步骤:
1. 定义原始数据
来源:
File data:私有的照片,音频,视频。
“Structured” data:数据库,数组或者其他结构化数据。
2. 继承Content Provider,这是一个介于数据与系统其他部分的中介。
3. 定义provider的authority string, content URI 以及column names. 此外,还需要定义权限,如果需要,还可以定义intents以及extras.
4. 添加其他可选数据: 设计sample数据,实现AbstractThreadedSyncAdapter(便于provider与云端数据同步)。

Design Data Storage

Table Data: 提供_ID列,作为主键或者外键。使用BaseColumns._ID作为该列的名称是最佳选择,尤其是在使用ListView的时候。
存储面向文件的数据,如图片,应避免将其直接存储至数据库,而是将其存为文件,然后存储该文件的路径等方式。
使用BLOB类型存储具有不同大小的数据或者具有不同结构的数据,诸如JSON,protocal buffers.

Designing Content URIs

Authority: 使用包名 + provider方式来定义为最好,如: com.kylin.test.provider
Path: 表名,因此全限定名的格式,举个例子:com.kylin.test.provoder/table
By convention, provider提供访问单行的方法,即ContentURI with _ID. 同时,provider匹配该_ID to the table’s _ID.
使用UriMatcher将content URI “patterns” 映射为integer values,方便使用switch来选择uri。
通配符:
*: 匹配任意长度任意有效字符
#: 匹配任意长度数字字符
Pattern:
content:/// for tables
content: for single rows.

方法addURI 映射 authority 和path 到一个integer。

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
     * 多行查询,匹配到数字1上。
     */
    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.
     * table3下的所有单行查询都匹配到数字2上。
     */
    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
    }

Implementing the ContentProvider Class

6个方法需要实现
- query(), 返回Cursor类型的数据, 若是SQLite数据,则不用不多。若不是,可使用MatrixCursor来封装数据,使用addRow添加新数据,然后将其作为Cursor类型返回。可抛出IllegalArgumentException和NullPointerException
- update(), 返回更新的行数,
- insert(),返回新插入行的content uri. To construct this, append the new row’s _ID (or other primary key) value to the table’s content URI, using withAppendedId().
- delete(),返回被删除行数,不需要真正的删除。若使用sysnc adapter,可标记这些行,然后sync adapter会自动检查,并在provider删除之前先删除server端的。
- getType(), 返回MIME type.
- onCreate(), 初始化provider,只有在client在尝试访问时,才会进行create。在onCreate中应执行一些快速运行的任务,而不是lengthy task。如将数据库的创建加载推迟到client请求时操作,否则将降低provider的启动速度。

public class ExampleProvider extends ContentProvider

    /*
     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
     * in a following snippet.
     */
    private MainDatabaseHelper mOpenHelper;

    // Defines the database name
    private static final String DBNAME = "mydb";

    // Holds the database object
    private SQLiteDatabase db;

    public boolean onCreate() {

        /*
         * Creates a new helper object. This method always returns quickly.
         * Notice that the database itself isn't created or opened
         * until SQLiteOpenHelper.getWritableDatabase is called
         */
        mOpenHelper = new MainDatabaseHelper(
            getContext(),        // the application context
            DBNAME,              // the name of the database)
            null,                // uses the default SQLite cursor
            1                    // the version number
        );

        return true;
    }

    ...

    // Implements the provider's insert method
    public Cursor insert(Uri uri, ContentValues values) {
        // Insert code here to determine which table to open, handle error-checking, and so forth

        ...

        /*
         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
         *
         */
        db = mOpenHelper.getWritableDatabase();
    }
}

数据库的创建

// A string that defines the SQL statement for creating a table
private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
    "main " +                       // Table's name
    "(" +                           // The columns in the table
    " _ID INTEGER PRIMARY KEY, " +
    " WORD TEXT"
    " FREQUENCY INTEGER " +
    " LOCALE TEXT )";
...
/**
 * Helper class that actually creates and manages the provider's underlying data repository.
 */
protected static final class MainDatabaseHelper extends SQLiteOpenHelper {

    /*
     * Instantiates an open helper for the provider's SQLite data repository
     * Do not do database creation and upgrade here.
     */
    MainDatabaseHelper(Context context) {
        super(context, DBNAME, null, 1);
    }

    /*
     * Creates the data repository. This is called when the provider attempts to open the
     * repository and SQLite reports that it doesn't exist.
     */
    public void onCreate(SQLiteDatabase db) {

        // Creates the main table
        db.execSQL(SQL_CREATE_MAIN);
    }
}

Implementing ContentProvider MIME Types

返回MIME类型
getType(): 必须实现的。
getStreamTypes():若提供文件,则也必须实现。

getType,返回一个MIME的String类型数据。常见的text,html or JPEG等,返回标准的MIME。完整的MIME类型见IANA MIME Media Types.

vendor-specific MIME format:

  • Type part: vnd
  • Subtype part:
    If the URI pattern is for a single row: android.cursor.item/
    If the URI pattern is for more than one row: android.cursor.dir/
  • Provider-specific part: vnd..

多行:
vnd.android.cursor.dir/vnd.com.example.provider.table1
单行:
vnd.android.cursor.item/vnd.com.example.provider.table1

MIME types for files
需要实现getStreamType()。当client对image感兴趣,可在getStreamType()指定过滤器,如”image/jpg”。

Implementing a Contract Class

Contract Class: a public final class that contains constant definitions for the URIs, column names, MIME types, and other meta-data that pertain to the provider.

Implementing Content Provider Permissions

权限管理只对私有的文件或者数据库有效,若存在external Strage则默认为public and world-readable.
精细化到entire provider, or to certain tables, or even to certain records, or all three.

权限名称自定义:一串字符串,最好是包名,以便于独一无二。如
‘com.example.app.provider.permission.READ_PROVIDER’读取权限,并使用scope : android:name来限定。

  • provider-level permission
    控制读写:android:permission
    读或写分开: android:readPermission, writePermission, 优先级比android:permission高。
  • Path-level permission
    You specify each URI you want to control with a < path-permission> child element of the < provider> element. 优先级高于Provider level
  • Temporary permission
    set the android:grantUriPermissions attribute of the < provider> element, or add one or more < grant-uri-permission> child elements to your < provider> element.
    当移除对某个content uri 支持时,必须调用Context.revokeUriPermission()。

若android:grantUriPermissions 设为true,则保证临时权限到整个Provider,若设为false,则必须设定provider或者path-level的grantUriPermissions.
若需要将临时权限委托给其他App,则需要在设定Intent的flag:FLAG_GRANT_READ_URI_PERMISSION or the FLAG_GRANT_WRITE_URI_PERMISSION。

The < provider> Element

在manifest中,需指定provider元素项。Android System将可从该元素下获得
- Authority(android:authority)
- provider class name( android:name)
- Permissions:

  • android:grantUriPermssions: Temporary permission flag.
  • android:permission: Single provider-wide read/write permission.
  • android:readPermission: Provider-wide read permission.
  • android:writePermission: Provider-wide write permission.


  • Startup and control attributes

  • android:enabled: Flag allowing the system to start the provider.
  • android:exported: Flag allowing other applications to use this provider.
  • android:initOrder: The order in which this provider should be started, relative to other providers in the same process.
  • android:multiProcess: Flag allowing the system to start the provider in the same process as the calling client.
  • android:process: The name of the process in which the provider should run.
  • android:syncable: Flag indicating that the provider’s data is to be sync’ed with data on a server.

  • Informational attributes
    android:label
    android:icon

Intents and Data Access

类似于访问Service一样,在Provider所在进程中,接收Intent并根据Extras等请求数据进行处理,无需使用ContentResolver。但是安全性上无法保证。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值