2025-02-09

一、簡介
Android使用一種稱為ContentProvider的概念來將數據抽象為服務,這種內容提供程序的理念看起來像啟用瞭REST的數據提供程序。
要從ContentProvider檢索數據或將數據保存到ContentProvider,需要使用一組類似REST的URI。例如,要從封裝圖書數據庫的ContentProvider獲取一組圖書,需要使用以下形式的URI:
[java]
content://com.android.book.BookProvider/books 
要從圖書數據庫獲得指定圖書(如編號為:88圖書),則要使用以下類似URI:
[java]
content://com.android.book.BookProvider/books/88 
設備上的任何應用程序都可以通過這些URI來訪問或操作數據,所以在應用程序之間的數據共享上ContentProvider扮演著非常主要的角色。
二、Android內置的ContentProvider
Android中存在大量的內置ContentProvider,他們記錄在SDK的android.provider包中,如:
Browser
Calllog
Contacts
  People
  Phones
  Photos
  Groups
MediaStore
  Audio
     Albums
     Artists
     Playlists
  Images
  Video
Settings
其中頂級項數據庫,較低級的項是表。所以Browser、CAllog,MediaStore和Settings都是封裝為ContentProvider的獨立SQLite數據庫.這些數據庫通常具有擴展名.db。僅能從實現包訪問,任何來自外部的訪問都要通過ContentProvider接口。
三、ContentProvider架構
與網站一樣,設備上的每個ContentProvider都會使用字符串註冊本身,這個字符串類似網站域名,但稱為:授權(Authority)。授權的註冊則AndroidManifest.xml中進行。如:
[java]
<provider android:name="BookProvider" android:authorities="com.myCompany.BookProvider"/> 
在進行瞭授權後,ContentProvider就擁有瞭以授權前綴開頭的URI:
[java]
content://com.myCompany.BookProvider/ 
註意:Android內置的ContentProvider可能沒有完全限定的授權名,隻有在使用第三方提供的時才使用完全限定名。這也就是為什麼有時候僅使用Contacts來引用聯系人ContentProvider。
1、Android內容URI的結構
Android中的內容URI類似於Http URL,但他們以content://開頭具有一下通用形式:
[java]
Content://*/*/ 
或 
content://authority-name/path-segment1/path-segment2/etc…… 
2、Android MIME類型的結構
就像網站返回給指定URI的MIME類型一樣,ContentProvider也負責返回給URI的MIME類型。MIME類型包括兩部分:類型和子類型。如:
[java]
text/html 
text/css 
text/xml 
application/pdf 
application/vnd.ms-excel 
類型和子類型對於他們所表示的內容必須是唯一的,且如果類型和子類型不是標準的,則需要在他們前面加上vnd。
3、使用URI讀取數據
要從ContentProvider獲取數據,需要使用該ContentProvider提供的URI。
如下:為從聯系人提供程序獲取單行聯系人信息
[java]
Uri baseUri = Contacts.People.CONTENT_URI; 
uri myPersonUri = baseUri.withAppendedPath(Contacts.People.CONTENT_URI,"80"); 
Cursor cur = manageQuery(myPersonUri,null,null,null); 
在上面的基礎上我們來看看,如何獲取一個遊標,這個遊標包含來自contactsContentProvider的People表的一組特定的列。
[java]
String[] projection = new String[]{ 
    People._ID, 
    People.NAME, 
    People.NUMBER 
}; 
 
Uri mContactsURi = Contacts.People.CONTENT_URI; 
Cursor managedCursor = managedQuery( 
    projection, 
    null, 
    Contacts.People.NAME + "ASC" 
); 
4、使用遊標
在使用Android遊標前,先瞭解一些關於遊標的的知識。
遊標是一個行集合;
讀取數據之前,需要使用moveToFirst(),因為遊標放在第一行之前;
需要知道列的名稱和類型;
所有字段訪問都基於列編號,所以必須首先將列名稱轉換為列編號;
遊標可以隨意移動;
可以使用遊標獲取行計數;
使用while循環導航遊標:
[java]
if (cur.moveToFirst == false){ 
    return; 

int nameColumnIndex = cur.getColumnIndex(People.NAME); 
String name = cur.getString(nameColumnIndex); 
 
while (cur.moveToNext()){ 
    //獲取屬性值 

使用for循環導航遊標
[java]
int nameColumn = cur.getColumnIndex(People.NAME); 
int ploneColumn = cur.getColumnIndex(People.NUMBER); 
 
for(cur.moveToFirst();!cur.isAfterLast();cur.moveToNext()){ 
    String name = cur.getString(nameColumn); 
    String phoneNumber = cur.getString(phoneColumn); 

5、使用Where子句
ContentProvider提供兩種方式來傳遞where子句:
通過URI;
[java]
String noteUri = "content://com.google.provider.NotePad.notes.80"; 
Cursor managedCursor = managedQuery(noteUri, 
    projection,//返回的列 
    null,//where子句 
    Contacts.People.NAME + "ASC"//排序方式 
); 
通過String子句與一組可替換的字符串數組參數的組合。
[java]
String noteUri = "content://com.google.provider.NotePad.notes"; 
Cursor managedCursor = managedQuery(noteUri, 
    projection,//返回的列 
    "_id=?",//where子句 
    new String[]{80}, 
    Contacts.People.NAME + "ASC"//排序方式 
); 
6、插入記錄
android使用ContentValues來保存將插入的單一記錄的值,要插入記錄,由android.content.ContentResolver使用URI插入記錄。下面是個例子:
[java]
ContentValues values = new ContentValues(); 
values.put("title","new Note"); 
values.put("note","this is a new Note"); 
ContentResolver cr = activity.getContentResolver(); 
Uri uri = cr.insert(Notepad.Notes.CONTENT_URI,values); 
7、更新和刪除
更新類似插入,方法簽名為:
[java]
int numberOfRowsUpdated = activity.getContentResolver().update( 
   Uri uri, 
   ContentValues values, 
   String whereClause, 
   String[] selectionArgs 
); 
類似的刪除的方法為:
[java]
int numberOfRowsDeleted = activity.getContentResolver().delete( 
   Uri uri, 
   String whereClause, 
   String[] selectionArgs 
); 
8、下面我們自己實現一個ContentProvider,並進行CRUD操作;
我們將按如下流程來實現:
(1)計劃數據庫、URI、列名稱,創建元數據類來定義所有的這些元數據元素的常量;
(2)擴展抽象類ContentProvider
(3)實現方法:query、insert、update、delete和getType
(4)在描述文件中註冊提供程序
具體代碼如下:
佈局文件:
[java]
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    > 
<TextView   
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello" 
    /> 
</LinearLayout> 
元數據類:(NotePad.java)
[java]
public class NotePad { 
    //授權 
    public static final String AUTHORITY = "com.google.provider.NotePad"; 
    private NotePad(){ 
         
    } 
    public static final class Notes implements BaseColumns{ 
        private Notes(){ 
             
        } 
        //uri = "content://" + AUTHORITY + "/notes" 
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes"); 
         
        //新的MiMe類型-多個 
        public static final String CONTENT_ITEM = "vnd.android.cursor.dir/vnd.google.note"; 
        //新的MiMe類型-單個 
        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.note"; 
         
        //排序方式 
        public static final String DEFAULT_SORT_ORDER = "modified DESC"; 
         
        //字段 
        public static final String TITLE = "title"; 
        public static final String NOTE = "note"; 
        public static final String CREATEDDATE = "created"; 
        public static final String MODIFIEDDATE = "modified"; 
    } 

擴展ContentProvider類(NotePadProvider.java)
[java]
public class NotePadProvider extends ContentProvider{ 
    private static final String TAG = "NotePadProvider"; 
     
    //數據庫名 
    private static final String DATABASE_NAME = "notepad.db"; 
    private static final int DATABASE_VERSION = 2; 
     
    //表名 
    private static final String TABLE_NAME = "notes"; 
    private static HashMap<String,String> noteProjectionMap; 
    private static final int NOTES = 1; 
    private static final int NOTE_ID = 2; 
     
    //uri匹配器 
    private static final  UriMatcher urimatcher; 
    private DatabaseHelper openHelper; 
    private static final String CREATE_TABLE = "CREATE TABLE " 
                                                    + TABLE_NAME 
                                                    + "(" +Notes._ID 
                                                    + " INTEGER PRIMARY KEY," 
                                                    + Notes.TITLE + " TEXT," 
                                                    + Notes.NOTE + " TEXT," 
                                                    + Notes.CREATEDDATE + " INTEGER," 
                                                    + Notes.MODIFIEDDATE + " INTEGER" + ")"; 
    static{ 
        urimatcher = new UriMatcher(UriMatcher.NO_MATCH); 
        urimatcher.addURI(NotePad.AUTHORITY, "notes",NOTES); 
        urimatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID); 
         
        //列別名 
        noteProjectionMap = new HashMap<String,String>(); 
        noteProjectionMap.put(Notes._ID,Notes._ID); 
        noteProjectionMap.put(Notes.TITLE, Notes.TITLE); 
        noteProjectionMap.put(Notes.NOTE, Notes.NOTE); 
        noteProjectionMap.put(Notes.CREATEDDATE, Notes.CREATEDDATE); 
        noteProjectionMap.put(Notes.MODIFIEDDATE,Notes.MODIFIEDDATE); 
    } 
     
    private static class DatabaseHelper extends SQLiteOpenHelper{ 
        public DatabaseHelper(Context context){ 
            super(context, DATABASE_NAME, null, DATABASE_VERSION); 
        } 
        //創建表 
        @Override 
        public void onCreate(SQLiteDatabase db) { 
            Log.i(TAG, CREATE_TABLE); 
            db.execSQL(CREATE_TABLE); 
        } 
        //更新數據庫 
        @Override 
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
            db.execSQL("drop table if exists notes"); 
            onCreate(db); 
        } 
         
    } 
    //刪除數據 
    @Override 
    public int delete(Uri uri, String selection, String[] selectionArgs) { 
         
        return 0; 
    } 
    //獲取MIME類型 
    @Override 
    public String getType(Uri uri) { 
        switch (urimatcher.match(uri)){ 
            case NOTES: 
                return Notes.CONTENT_ITEM; 
            case NOTE_ID: 
                return Notes.CONTENT_ITEM_TYPE; 
            default: 
                throw new IllegalArgumentException("Unknow Uri" + uri); 
        } 
    } 
 
    @Override 
    public Uri insert(Uri uri, ContentValues values) { 
        if (urimatcher.match(uri) != NOTES){ 
            throw new IllegalArgumentException("Unknow Uri" + uri); 
        } 
        ContentValues contentValues; 
        if (null != values){ 
            contentValues = new ContentValues(values); 
        }else{ 
            contentValues = new ContentValues(); 
        } 
        Long now = Long.valueOf(System.currentTimeMillis()); 
        if (!contentValues.containsKey(NotePad.Notes.CREATEDDATE)){ 
            contentValues.put(NotePad.Notes.CREATEDDATE, now); 
        } 
        if (contentValues.containsKey(NotePad.Notes.MODIFIEDDATE) == false){ 
            contentValues.put(NotePad.Notes.MODIFIEDDATE, now); 
        } 
        if (contentValues.containsKey(NotePad.Notes.TITLE) == false){ 
            Resources r = Resources.getSystem(); 
            contentValues.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled)); 
        } 
        if (contentValues.containsKey(NotePad.Notes.NOTE) == false){ 
            contentValues.put(NotePad.Notes.NOTE, ""); 
        } 
        SQLiteDatabase db = openHelper.getWritableDatabase(); 
        long rowId = db.insert(TABLE_NAME, Notes.NOTE, contentValues); 
        System.out.println(rowId); 
        if (rowId > 0){ 
            Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId); 
            getContext().getContentResolver().notifyChange(noteUri, null); 
            return noteUri; 
        } 
        throw new SQLException("insert row into failed " + uri); 
    } 
 
    @Override 
    public boolean onCreate() { 
        openHelper = new DatabaseHelper(getContext()); 
        return false; 
    } 
 
    @Override 
    public Cursor query(Uri uri, String[] projection, String selection, 
            String[] selectionArgs, String sortOrder) { 
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 
        switch (urimatcher.match(uri)){ 
            case NOTES: 
                qb.setTables(TABLE_NAME); 
                qb.setProjectionMap(noteProjectionMap); 
                break; 
            case NOTE_ID: 
                qb.setTables(TABLE_NAME); 
                qb.setProjectionMap(noteProjectionMap); 
                qb.appendWhere(Notes._ID + "=" + uri.getPathSegments().get(1)); 
                break; 
            default: 
                throw new IllegalArgumentException("Unknow Uri" + uri); 
        } 
        String orderBy; 
        if (TextUtils.isEmpty(sortOrder)){ 
            orderBy = NotePad.Notes.DEFAULT_SORT_ORDER; 
        }else{ 
            orderBy = sortOrder; 
        } 
        SQLiteDatabase db = openHelper.getReadableDatabase(); 
        Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); 
        cursor.setNotificationUri(getContext().getContentResolver(),uri); 
        return cursor; 
    } 
 
    @Override 
    public int update(Uri uri, ContentValues values, String selection, 
            String[] selectionArgs) { 
        return 0; 
    } 

活動類(ContentProviderTest.java) 
public class ContentProviderTest extends Activity { 
    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
         
        //插入數據 
        ContentValues contentValues = new ContentValues(); 
        contentValues.put(NotePad.Notes.TITLE, "title1"); 
        contentValues.put(NotePad.Notes.NOTE, "NOTENOTE1"); 
        getContentResolver().insert(NotePad.Notes.CONTENT_URI, contentValues); 
        contentValues.clear(); 
        contentValues.put(NotePad.Notes.TITLE, "title2"); 
        contentValues.put(NotePad.Notes.NOTE, "NOTENOTE2"); 
        getContentResolver().insert(NotePad.Notes.CONTENT_URI, contentValues); 
        displayNote(); 
    } 
    private void displayNote(){ 
        String[] columns = new String[]{NotePad.Notes._ID 
                ,NotePad.Notes.TITLE 
                ,NotePad.Notes.NOTE 
                ,NotePad.Notes.CREATEDDATE 
                ,NotePad.Notes.MODIFIEDDATE}; 
        Uri myUri = NotePad.Notes.CONTENT_URI; 
        Cursor cursor = managedQuery(myUri, columns, null, null, null); 
        if (cursor.moveToFirst()){ 
            String id = null; 
            String title = null; 
            do{ 
                id = cursor.getString(cursor.getColumnIndex(NotePad.Notes._ID)); 
                title = cursor.getString(cursor.getColumnIndex(NotePad.Notes.TITLE)); 
                Toast toast = Toast.makeText(this, "TITLE:" + id + "NOTES" + title, Toast.LENGTH_SHORT); 
                toast.setGravity(Gravity.TOP|Gravity.CENTER, 0, 40); 
                toast.show(); 
            }while(cursor.moveToNext()); 
        } 
    } 

在描述文件中註冊提供程序:AndroidManifest.xml
[html]
<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
      package="com.test.contentProvider" 
      android:versionCode="1" 
      android:versionName="1.0"> 
 
    <application android:icon="@drawable/icon" android:label="@string/app_name"> 
        <provider android:name="NotePadProvider" 
                  android:authorities="com.google.provider.NotePad"/> 
        <activity android:name=".ContentProviderTest" 
                  android:label="@string/app_name"> 
            <intent-filter> 
                <action android:name="android.intent.action.MAIN" /> 
                <category android:name="android.intent.category.LAUNCHER" /> 
            </intent-filter> 
            <intent-filter> 
                <data android:mimeType="vnd.android.cursor.dir/vnd.google.note"></data> 
            </intent-filter> 
            <intent-filter> 
                <data android:mimeType="vnd.android.cursor.item/vnd.google.note"></data> 
            </intent-filter> 
        </activity> 
 
    </application> 
</manifest> 
運行結果如下圖所示:

摘自 fwwdn的專欄

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *