现在的位置: 首页 > 综合 > 正文

AndroidPro4_002_Content Providers

2013年07月17日 ⁄ 综合 ⁄ 共 16102字 ⁄ 字号 评论关闭

# Android use Content Providers for abstracting data
into services.
# Android has built-in content providers. These SQLite database typically have an extension of .db and are accessible only from the implementation package.
Any access outside that package must go through the content-provider interface.
# 构筑content-provider
    \ Content provider 会在设备上注册一个authority(类似于域名). This authority registration occurs in the AndroidManifest.xml file.
    \ How to register providers in AndroidManifest.xml 
        方法一:
        <provider android:name="SomeProvider" android:authorities="com.your-company.SomeProvider"/>
        方法二:
        <provider android:name="NotePadProvider" android:authorities="com.google.provider.NotePad"/>
        如:content://com.your-company.SomeProvider/
        和 content://com.google.provider.NotePad/
# Content Provider also retrieve(检索) or manipulate(操作) date.
    The URI指向Notes目录或集合的是:content://com.google.provider.NotePad/Notes
    The URI indentify a specify note is : content://com.google.provider.NotePad/Notes/#
    # is a id of a particular note.
# Android 内置provider 不需要content 提供完整的authority
    如:content://media/internal/images
    content://contacts/people/23
    相应地,第三方provider需要content提供完整的authority。
# The output of the URI of a content provider is not a typed data,caller must know the structure of the rows and columns that are returned.
# MIME
    \ A content provider has a built-in mechanism that allows you to determine the Multipurpose Internet Mail Extensions(MIME)type of the data represented by
this URI.

    \ content provider 通过内置的机制让你可以确定URI所表示的MIME类型。
# URI calls against a content provider return a Cursor.
# Structure of Android Content URIs
    \ To retrieve data from a content provider, must invoke a URI.(returned a Cursor object)
    \ Cursor object is a set of rows and columns.
    \ Content Provider的格式:
        1/ content://*/*/*
        2/ content://authority-name/path-segment1/path-segment2/ETC...
        e.g. content://com.google.provider.NotePad/notes/23
        content之后,URI提供了一个authority的唯一标识,用于定位已注册的provider, /notes/23 称为 path-segments.
# Structure of Android MIME Types
    \ 通过MIME类型,可以指导系统使用何种程序打开数据。
    \ A MIME type has two parts : a type and a subtype
        type/subtype
        e.g. text/css, text/html, text/xml, text/vnd.curl, application/pdf, application/vnd.ms-excel
    \ 一些主要的TYPE包括:application, audio, example, image, message, model, multipart, text, video.
    \ Each primary type has subtype.
    \ Subtype 前缀 "vnd." 表示subtype是专有的数据格式; 前缀 "x-" 表示未注册的非标准subtype.
    \ 在Android中,vnd在MIME里表示type和subtype是非标准的由厂商定制的格式.
    \ Android MIME type 两种形式:
        1/ 单条特定记录 vnd.android.cursor.item/vnd.yourcompanyname.ContentType
        2/ 数据集合或行集合 vnd.android.cursor.dir/vnd.yourcompanyname.ContentType
        e.g.
        vnd.android.cursor.item/vnd.google.note        //One single note 
        vnd.android.cursor.dir/vnd.google.note        //A collection or a directory of notes
# MIME可用于intent, intent可以基于数据的MIME类型指定系统用哪个activity调用
# MIME types are invariably(总是) derived(派生) from their URIs through content provider.
    \ Three things keep in mind:
        1/ The type and subtype need to be unique for what they represent. Primary type主要包括dir和item.
        2/ The type and subtype need to be preceded with vnd if they are not standard.
        3/ They are typically name-spaced for your specific need.
    \ MIME中type一般只有 vnd.android.cursor.item 或 vnd.android.cursor.dir
    \ MIME中subtype中 "vnd."之后, you are free to subtype it with anything you like.
# Reading Data Using URI
    \ Android SDK有一个helper中有以下一条URI(const)
        ContactsContract.Contacts.CONTENT_URI 相当于 content://com.android.contacts/contacts/
    以下代码检索此ContentProvider中单行的数据:

Uri myPersonUri = Uri.withAppendedPath(peopleBaseUri,"23");
//query for this record
//manageQuery is a method on Activity class
Cursor cur = manageQuery (myPersonUri,null,null,null);

# Retrieving a cursor from a content provider

//use this interface to see the constants
import ContactsContract.Contacts;

//An array specifying which columns to return
string[] projection = new string[]{
  Contacts.ID;
  Contacts.DISPLAY_NAME_PRIMARY
};
Uri mContactsUri = ContactsContract.Contacts.CONTENT_URI
// Best way to retrieve a query;returns a managed query
Cursor managedCursor = managedQuery( mContactsUri,
                  projection, //Which columns to return.
                  null//WHERE clause
                  Contacts.DISPLAY_NAME_PRIMARY + "ASC");// Order-by clause.

# The Android Cursor
    \ An Android Cursor :
        A cursor is a collection of rows.
        Use moveToFirst() before reading any data, because the cursor starts off positioned before the first row.
        Must know the column names.
        Must know the column types.
        All field-access methods are based on column number, so we must convert the column name to a column number first.
        The cursor is random ( U can move forward and backward,and U can jump).
        Because the cursor is random, u can ask it for a row count.
    \ Navigating Through a Cursor Using a while Loop

if (cur.moveToFirst() == false)
{
  //no rows empty cursor
  return;
}

//The cursor is already pointing to the first row
//lets access a few columns
int nameColumnIndex = cur.getColumnIndex(Contacts.DISPLAY_NAME_PRIMARY);
String name = cur.getString(nameColumnIndex);

// lets new see how we can loop through a cursor
while( cur.moveToNext())
{
  //cursor moved successfully
  
//access fields
}

    \ Android provides some methods to learn where the cursor is
        isBeforeFirst()
        isAfterLast()
        isClosed()
    \ Navigating Through a Cursor Using a for Loop

//Get your indexs first outside the for loop
int nameColumn = cur.getColumnIndex(Contacts.DISPLAY_NAME_PRIMARY);

//Walk the cursor now based on column indexs
for( cur.moveToFirst(); !cur.is AfterLast(); cur.moveToNext())
{
  String name = cur.getString(nameColumn);
}

    \ Get the indexes first from the cursor to avoid surprises.
    \ To find the number of rows in a cursor, Android provides a method on the cursor object called getCount().
# Passing where Clause
    \ Content Providers offer 2 ways of passing a where clause.
        1/ Through the URI.
        2/ Through the combination of a string clause and a set of replaceable string-array arguments.
    \ Through a URI

Activity someActivity
//..initialize someActivity
String noteUri = "content://com.google.provider.NotePad/notes/23";
Cursor managedCursor = someActivity.managedQuery( noteUri, 
                                                                         projection, // Which columns to return.
                                                                         null// WHERE clause
                                                                         null ); // Order-by clause

        The id we want is embedded in the URI itself.
    \ Using explicit where Clause
        Activity.managedQuery()的函数原型

public final Cursor managedQuery( Uri uri,
                                  String[] projection,
                                  String selection,
                                  String[] selectionArgs,
                                  String sortOrder)

        The selection string represents a where clause declaring which rows to return, 
        passing " null " will return all rows for the given URI,
        passing " ? " wiil be replaced by the values from selectionArgs, the values will be bound as Strings.
        e.g.

//URI method
managedQuery( "content://com.google.provider.NotePad/notes/23",
                       null,
                       null,
                       null,
                       null );

// explicit where clause
managedQuery( "content://com.google.provider.NotePad/notes",
                       null,
                       "_id=?",
                       new String[] {23},
                       null );


# Inserting Records
        \ Single record insert, use ContentValues.
            ContentValues is a key/value pairs.
            插入记录时,先将记录填充ContentValues,然后请求android.content.ContentResolver用一个URI插入该记录.
            在抽象层上,我们不能直接向database插入数据,而必须通过URI来确定provider,然后插入。
            ContentResolver正是这样一个角色,它通过URI指向正确的provider,然后将传给它的ContentValues插入特定的provider,最后返回指向新插入记录的URI。
            e.g.

ContentValues values = new ContentValues();
values.put( "title", "New note");
values.put( "note", "This is a new note");

//values object is now ready to be inserted
//U can get a reference to a ContentResolver by asking the Activity class
ContentResolver contentResolver = activity.getContentResolver();

//Tell ContentResolver a URI to insert the row.
Uri uri = contentResolver.insert( Notepad.Notes.CONTENT_URI, values);
//This call returns a URI pointing to the newly inserted record.
//This returned URI match the structure: Notepad.Notes.CONTENT_URI/new_id

# Adding a File to a Content Provider
        \ ContentResolver create a internal file and stors the reference to the file name to a column "_data".

//insert some record to return URI
ContentResolver contentResolver = activity.getContentResolver();
Uri newUri = contentResover.insert( Notepad.Notes.CONTENT_URI, values);

// Once we have the URI of the record, ask the ContentResolver to get a reference to the file output stream
//ContentResolver hides the access to the _data filed where it stores the real file reference
OutputStream outStream = activity.getContentResolver().openOutputStream( newUri);
someSourceBitmap.compress( Bitmap.CompressFormat.JGEG, 50, outStream);
outStream.close();
// The code then uses that output stream to write to

# Update and Delete
    \ An update is similar to an insert, changed columns values pass through ContentValues:

int numberOfRowsUpdated = activity.getContentResolver().update( 
                                     Uri uri,
                                     ContentValues values,
                                     String whereClause,
                                     String[] selectionArgs )

    \ Delete method:

int numberOfRowsDeleted = activity.getContentResolver().delete(
                                     Uri uri,
                                     String whereClause,
                                     String[] selectionArgs )

# Implementing Content Providers
    \ First, plan a database, URIs, column names, and so on, and create a metadata class(元数据类) that defines constants for all these metadata elements.
    \ Second, extend androd.content.ContentProvider.
    \ Then, implement key methods: query, insert, update, delete, getType.
    \ Finally, register the provider in the manifest.xml.
# Plan a database
    \ Define Metabase class for Database:

public class BookProviderMetaData
{
    public static final String AUTHORITY = "com.androidbook.provider.BookProvider"; //use this String to register in manifest.xml
    public static final String DATABASE_NAME = "book.db";
    public static final int DATABASE_VERSION = 1;
    public static final String BOOKS_TABLE_NAME = "books";

    private BookProviderMetaData() {} //空构造函数
    
    
//inner class BookTable
    public static final class BookTableMetaBata implements BaseColumns // The BaseColumns class provides the standard _id field,which represents the row ID.
    {
        private BookTableMetaData() {} //空构造函数
        public static final String TABLE_NAME = "books";
        
        //uri and MIME type definitions
        public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY + "/books");
        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.androidbook.book";
        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.androidbook.book";
        public static final String DEFAULT_SORT_ORDER = "modified DESC";
        //Columns, string type
        public static final String BOOK_NAME = "name";
        public static final String BOOK_ISBN = "isbn";
        public static final String BOOK_AUTHOR = "author";
        //Integer from System.currentTimeMillis()
        public static final String CREATED_DATE = "created";
        public static final String MODIFIED_DATE = "modified";
    }
}

# Implementing the BookProvider Content Provider

public class BookProvider extends ContentProvider
{
    //Setup projection Map
    private static HashMap<String, String> sBooksProjectionMap;//not final
    static
    {
        sBooksProjectionMap = new HashMap<String, String>();
        sBooksProjectionMap.put(BookTableMetaData._ID, BookTableMetaData._ID); // use HashMap.put()
        
//name,isbn,author
        sBooksProjectionMap.put(BookTableMetaData.BOOK_NAME, BookTableMetaData.BOOK_NAME);
        sBooksProjectionMap.put(BookTableMetaData.BOOK_ISBN, BookTableMetaData.BOOK_ISBN);
        sBooksProjectionMap.put(BookTableMetaData.BOOK_AUTHOR, BookTableMetaData.BOOK_AUTHOR);
        //created date, modified date
        sBooksProjectionMap.put(BookTableMetaData.CREATE_DATE, BookTableMetaData.CREATE_DATE);
        sBooksprojectionMap.put(BookTableMetaData.MODIFIED_DATE, BookTableMetaData.MODIFIED_DATE);
    }
    
    //Setup URIs
    
//Provide a mechanism to indentify all the incoming uri patterns.
    private static final UriMatcher sUriMatcher;
    //define ids for each uri type
    private static final int INCOMING_BOOK_COLLECTION_URI_INDICATOR = 1;
    private static final int INCOMING_SINGLE_BOOK_URI_INDICATOR = 2;
    static{
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);// UriMatcher returns NO_MATCH when don't match.
        
// Register pattern for the books
        sUriMatcher.addURI( BookProviderMetaData.AUTHORITY, "books", INCOMING_BOOK_COLLECTION_URI_INDICATOR);
        // Register pattern for a single book
        sUriMatcher.addURI( BookProviderMetaData.AUTHORITY, "books/#", INCOMING_SINGLE_BOOK_URI_INDICATOR);
    }
    
    // Setup/Create Database
    
// This class helps open, create, and upgrade the database file.
    private static class DatabaseHelper extends SQLiteOpenHelper
    {
        DatabaseHelper(Context context){
            super(
            context, 
            BookProviderMetaData.DATABASE_NAME, 
            null
            BookProviderMetaData.DATABASE_VERSION);
        }
        
        @Override
        public void onCreate(SQLiteDatabase db)
        {
            Log.d(TAG,"inner oncreate called");
            db.execSQL("CREATE TABLE " + BookTableMetaData.TABLE_NAME + " (" 
            + BookTableMetaData._ID + " INTEGER PRIMARY KEY,"
            + BookTableMetaData.BOOK_NAME + " TEXT,"
            + BookTableMetaData.BOOK_ISBN + " TEXT,"
            + BookTableMetaData.CREATED_DATE + " INTEGER,"
            + BookTableMetaData.MODIFIED_DATE + " INTEGER"
            + ");");
        }
        @Override
        public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion)
        {
            db.execSQL("DROP TABLE IF EXISTS " + BookTableMetaData.TABLE_NAME);
            onCreate(db);
        }
    }
    private DatabaseHelper mOpenHelper;
    
    //Component creation callback
    @Override
    public boolean onCreate(){
        mOpenHelper = new DatabaseHelper(getContext());
        return ture;
    }
    
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder)
    {
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); // The query() method use SQLiteQueryBuilder() to execute the query.
        switch (sUriMatcher.match(uri)){
        case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
            qb.setTables(BookTableMetaData.TABLE_NAME);
            qb.setProjectionMap(sBooksProjectionMap);
            break;
        case INCOMING_SINGLE_BOOK_URI_INDICATOR:
            qb.setTables(BookTableMetaData.TABLE_NAME);
            qb.setProjectionMap(sBooksProjectionMap);
            qb.appendWhere(BookTableMetaData._ID + "=" + uri.getPathSegments().get(1));
            break;
        default:
            throw new IllegalArgumentException("Unknow URI" + uri);
        }
        
        // If no sort order is specified use the default
        String orderBy;
        if (TextUtils.isEmpty(sortOrder)){
            orderBy = BookTableMetaData.DEFAULT_SORT_ORDER;
        }else {
            orderBy = sortOrder;
        }
        
        // Get the database and run the query
        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
        Cursor c = qb.query(db, projection, selection, selectionArgs, nullnull, orderBy);
        
        // example of getting a count
        int i = c.getCount();
        
        // Tell the cursor what uri to watch, so it knows when its source data changes
        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c; // query() method returns a cursor to caller.
    }
    
    @Override
    public String getType(Uri uri){ //Content Provider must implement getType() method to return a MIME type for a given URI
        switch (sUriMatcher.match(uri)){
        case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
            return BookTableMetaData.CONTENT_TYPE;
        case INCOMING_SINGLE_BOOK_URI_INDICATOR:
            return BookTableMetaData.CONTENT_ITEM_TYPE;
        default:
            throw new IllegalArgumentException("Unknow URI " + uri);
        }
    }
    
    @Override
    public Uri insert(Uri uri, ContentValues initialValues){
        // Validate(验证) the requested uri
【上篇】
【下篇】

抱歉!评论已关闭.