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。但是安全性上无法保证。