Android基础——ContentProvider

Content URI patterns


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.

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 UriMatcher 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 addURI() maps an authority and path to an integer value. The method match() 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
    }
Another class,  ContentUris , provides convenience methods for working with the  id  part of content URIs. The classes  Uri  and  Uri.Builder  include convenience methods for parsing existing  Uri  objects and building new ones.


Implementing the ContentProvider Class


Implementing the query() method

The ContentProvider.query() method must return a Cursor object, or if it fails, throw an Exception. If you are using an SQLite database as your data storage, you can simply return the Cursor returned by one of the query()methods of the SQLiteDatabase class. If the query does not match any rows, you should return a Cursor instance whose getCount() method returns 0. You should return null only if an internal error occurred during the query process.

If you aren't using an SQLite database as your data storage, use one of the concrete subclasses of Cursor. For example, the MatrixCursor class implements a cursor in which each row is an array of Object. With this class, useaddRow() to add a new row.

Remember that the Android system must be able to communicate the Exception across process boundaries. Android can do this for the following exceptions that may be useful in handling query errors:


Implementing the insert() method

The insert() method adds a new row to the appropriate table, using the values in the ContentValues argument. If a column name is not in the ContentValues argument, you may want to provide a default value for it either in your provider code or in your database schema.

This method should return the content URI for the new row. To construct this, append the new row's _ID (or other primary key) value to the table's content URI, using withAppendedId().


Implementing the update() method

The update() method takes the same ContentValues argument used by insert(), and the same selection andselectionArgs arguments used by delete() and ContentProvider.query(). This may allow you to re-use code between these methods.


Implementing the delete() method

The delete() method does not have to physically delete rows from your data storage. If you are using a sync adapter with your provider, you should consider marking a deleted row with a "delete" flag rather than removing the row entirely. The sync adapter can check for deleted rows and remove them from the server before deleting them from the provider.


Implementing the onCreate() method

The Android system calls onCreate() when it starts up the provider. You should perform only fast-running initialization tasks in this method, and defer database creation and data loading until the provider actually receives a request for the data. If you do lengthy tasks in onCreate(), you will slow down your provider's startup. In turn, this will slow down the response from the provider to other applications.


For example, if you are using an SQLite database you can create a new SQLiteOpenHelper object inContentProvider.onCreate(), and then create the SQL tables the first time you open the database. To facilitate this, the first time you call getWritableDatabase(), it automatically calls the SQLiteOpenHelper.onCreate() method.


The following two snippets demonstrate the interaction between  ContentProvider.onCreate()  and SQLiteOpenHelper.onCreate()
The first snippet is the implementation of  ContentProvider.onCreate() :
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();
    }
}

The next snippet is the implementation of  SQLiteOpenHelper.onCreate() , including a helper class:
...
// 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


The ContentProvider class has two methods for returning MIME types:

getType()
One of the required methods that you must implement for any provider.
getStreamTypes()
A method that you're expected to implement if your provider offers files.

MIME types for tables

The getType() method returns a String in MIME format that describes the type of data returned by the content URI argument. The Uri argument can be a pattern rather than a specific URI; in this case, you should return the type of data associated with content URIs that match the pattern.

·For common types of data such as as text, HTML, or JPEG,  getType()  should return the standard MIME type for that data. A full list of these standard types is available on the  IANA MIME Media Types  website.

·For content URIs that point to a row or rows of table data, getType() should return a MIME type in Android's 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.<name>.<type>

    You supply the <name> and <type>. The <name> value should be globally unique, and the <type> value should be unique to the corresponding URI pattern. A good choice for <name> is your company's name or some part of your application's Android package name. A good choice for the <type> is a string that identifies the table associated with the URI.

For example, if a provider's authority is com.example.app.provider, and it exposes a table named table1, the MIME type for multiple rows in table1 is:

vnd.android.cursor.dir/vnd.com.example.provider.table1

For a single row of table1, the MIME type is:

vnd.android.cursor.item/vnd.com.example.provider.table1

MIME types for files

If your provider offers files, implement getStreamTypes(). The method returns a String array of MIME types for the files your provider can return for a given content URI. You should filter the MIME types you offer by the MIME type filter argument, so that you return only those MIME types that the client wants to handle.

For example, consider a provider that offers photo images as files in .jpg.png, and .gif format. If an application calls ContentResolver.getStreamTypes() with the filter string image/* (something that is an "image"), then theContentProvider.getStreamTypes() method should return the array:

{ "image/jpeg", "image/png", "image/gif"}

If the app is only interested in .jpg files, then it can call ContentResolver.getStreamTypes() with the filter string*\/jpeg, and ContentProvider.getStreamTypes() should return:

{"image/jpeg"}

If your provider doesn't offer any of the MIME types requested in the filter string, getStreamTypes() should returnnull.





the important points are:

  • By default, data files stored on the device's internal storage are private to your application and provider.
  • SQLiteDatabase databases you create are private to your application and provider.
  • By default, data files that you save to external storage are public and world-readable. You can't use a content provider to restrict access to files in external storage, because other applications can use other API calls to read and write them.
  • The method calls for opening or creating files or SQLite databases on your device's internal storage can potentially give both read and write access to all other applications. If you use an internal file or database as your provider's repository, and you give it "world-readable" or "world-writeable" access, the permissions you set for your provider in its manifest won't protect your data. The default access for files and databases in internal storage is "private", and for your provider's repository you shouldn't change this.

If you want to use content provider permissions to control access to your data, then you should store your data in internal files, SQLite databases, or the "cloud" (for example, on a remote server), and you should keep files and databases private to your application.


permissions

All applications can read from or write to your provider, even if the underlying data is private, because by default your provider does not have permissions set. To change this, set permissions for your provider in your manifest file, using attributes or child elements of the <provider> element. You can set permissions that apply to the entire provider, or to certain tables, or even to certain records, or all three.

You define permissions for your provider with one or more <permission> elements in your manifest file. To make the permission unique to your provider, use Java-style scoping for the android:name attribute. For example, name the read permission com.example.app.provider.permission.READ_PROVIDER.


The following list describes the scope of provider permissions, starting with the permissions that apply to the entire provider and then becoming more fine-grained. More fine-grained permissions take precedence over ones with larger scope:

The following list describes the scope of provider permissions, starting with the permissions that apply to the entire provider and then becoming more fine-grained. More fine-grained permissions take precedence over ones with larger scope:

Single read-write provider-level permission
One permission that controls both read and write access to the entire provider, specified with the android:permission attribute of the  <provider> element.
Separate read and write provider-level permission
A read permission and a write permission for the entire provider. You specify them with the android:readPermission and  android:writePermission attributes of the  <provider> element. They take precedence over the permission required by  android:permission.
Path-level permission
Read, write, or read/write permission for a content URI in your provider. You specify each URI you want to control with a  <path-permission> child element of the  <provider> element. For each content URI you specify, you can specify a read/write permission, a read permission, or a write permission, or all three. The read and write permissions take precedence over the read/write permission. Also, path-level permission takes precedence over provider-level permissions.
Temporary permission
A permission level that grants temporary access to an application, even if the application doesn't have the permissions that are normally required. The temporary access feature reduces the number of permissions an application has to request in its manifest. When you turn on temporary permissions, the only applications that need "permanent" permissions for your provider are ones that continually access all your data.

Consider the permissions you need to implement an email provider and app, when you want to allow an outside image viewer application to display photo attachments from your provider. To give the image viewer the necessary access without requiring permissions, set up temporary permissions for content URIs for photos. Design your email app so that when the user wants to display a photo, the app sends an intent containing the photo's content URI and permission flags to the image viewer. The image viewer can then query your email provider to retrieve the photo, even though the viewer doesn't have the normal read permission for your provider.

To turn on temporary permissions, either set the android:grantUriPermissions attribute of the <provider>element, or add one or more <grant-uri-permission> child elements to your <provider> element. If you use temporary permissions, you have to call Context.revokeUriPermission() whenever you remove support for a content URI from your provider, and the content URI is associated with a temporary permission.

The attribute's value determines how much of your provider is made accessible. If the attribute is set to true, then the system will grant temporary permission to your entire provider, overriding any other permissions that are required by your provider-level or path-level permissions.

If this flag is set to false, then you must add <grant-uri-permission> child elements to your <provider>element. Each child element specifies the content URI or URIs for which temporary access is granted.

To delegate temporary access to an application, an intent must contain theFLAG_GRANT_READ_URI_PERMISSION or the FLAG_GRANT_WRITE_URI_PERMISSION flags, or both. These are set with the setFlags() method.

If the android:grantUriPermissions attribute is not present, it's assumed to be false.

The <provider> Element


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

Authority ( android:authorities)
Symbolic names that identify the entire provider within the system. This attribute is described in more detail in the section  Designing Content URIs.
Provider class name (  android:name )
The class that implements  ContentProvider. This class is described in more detail in the section Implementing the ContentProvider Class.
Permissions
Attributes that specify the permissions that other applications must have in order to access the provider's data:

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

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:
  • 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.

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

Informational attributes
An optional icon and label for the provider:
  • android:icon: 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.
  • android:label: 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 <provider> element.




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android Studio ContentProvider 是一种在 Android 应用程序中提供数据访问的机制。ContentProvider 通过实现标准接口,允许应用程序将数据共享给其他应用程序。例如,一个应用程序可以使用 ContentProvider 来提供它的数据给其他应用程序使用,而不需要直接暴露数据库或文件。ContentProviderAndroid 应用程序中非常有用的一个组件,它可以帮助应用程序更好地管理和共享数据。 ### 回答2: Android Studio ContentProviderAndroid 框架中的一个关键组件,它提供了一种标准化的方式访问应用程序数据存储系统。ContentProvider 允许一个应用程序共享数据给其他应用程序,同时也提供了一种集中管理数据访问的方式。 ContentProviders 通过封装数据的方式,抽象出数据的访问接口,为应用程序提供了一种标准的、统一的数据访问方式。应用程序可以使用 ContentResolver 通过 Uri 访问提供程序中的数据。ContentSupplier 将数据存储在持久性存储介质(例如 SQLite 数据库、内存、文件、网络、 content provider service 等)中,提供客户端应用程序通过 ContentResolver 访问的统一接口,无论数据如何存储,使用方式都是一致的。 在 Android Studio 中,创建 ContentProvider 可以使用 Android Studio 提供的模板,可以在创建应用程序时选择“空 Activity”,然后在下一个页签中选择“Content Provider”模板。通过模板创建的 ContentProvider 包含了两个部分:Contract 和 Provider 实现。 Contract 定义了该 ContentProvider 中的数据类型,包括数据表、列名等信息。Provider 实现包括了 ContentProvider 中的基本功能,比如实现 insert()、delete()、update()、query() 等方法,实现 UriMatcher 解析,处理来自外部应用程序的数据请求。 ContentProvider 可以实现在不暴露底层数据存储方式的前提下,提供独立的、可扩展的数据提供服务,这为多个应用程序之间共享数据提供了一种很好的机制。在应用开发过程中,需要注意数据访问权限、数据安全、Uri 注册等问题,以确保 ContentProvider 提供的数据能够被合法访问和使用。 ### 回答3: Android Studio ContentProviderAndroid平台提供的一种数据共享方式,它允许多个应用程序访问同一份数据而不会相互干扰。每个ContentProvider对应一个特定的数据源,开发者可以在ContentProvider中定义URI(Uniform Resource Identifier),用于标识不同的数据源。 在Android Studio中创建ContentProvider非常简单,只需通过New->Other->Content Provider来创建即可。创建ContentProvider完成后,需要定义provider的各种属性,如权限、支持的数据类型等。定义完成后,就可以在应用程序中访问数据了。开发者可以使用ContentResolver类来访问ContentProvider中定义的数据。ContentResolver是Android中提供的类,用于管理应用程序和ContentProvider之间的数据交互。 ContentProviderAndroid中非常重要的一个组件,它可以实现数据共享、数据提供和数据访问等功能。开发者需要谨慎地设计ContentProvider,特别是在涉及敏感数据时,需要仔细考虑安全性和权限控制。此外,ContentProvider还需要考虑数据格式的兼容性和数据的有效性等问题。 总之,Android Studio ContentProviderAndroid平台提供的一种强大的数据共享方式,可以实现多个应用程序之间的数据交换和共享。开发者需要根据具体需求仔细设计ContentProvider,确保其安全性和数据的有效性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值