Android 總結:ContentProvider 的使用

來源:互聯網
上載者:User

標籤:highlight   derby   ber   pat   插入資料   protect   let   刪除   路徑   

一、概述

ContentProvider:為儲存和擷取資料提供統一的介面,可以在不同的應用程式之間共用資料。 
Android內建的許多資料都是使用ContentProvider形式,供開發人員調用的 (如視頻,音頻,圖片,通訊錄等)。 
1. 使用表的形式來組織資料 
- 無論資料來源是什麼,ContentProvider都會認為是一種表。(把資料群組織成表格) 
2. 提供的方法 
- query:查詢 
- insert:插入 
- update:更新 
- delete:刪除 
- getType:得到資料類型 
- onCreate:建立資料時調用的回呼函數 
3. 公用的URI 
- 每個ContentProvider都有一個公用的URI,這個URI用於表示這個ContentProvider所提供的資料。 
- Android所提供的ContentProvider都存放在android.provider包當中。 
4. 對比分析 
雖然使用其他方法也可以對外共用資料,但資料訪問方式會因資料存放區的方式而不同。 
- 採用檔案方式對外共用資料,需要進行檔案操作讀寫資料; 
- 採用sharedpreferences共用資料,需要使用sharedpreferences API讀寫資料。 
- 使用ContentProvider共用資料的好處是統一了資料訪問方式。

二、ContentProvider 的實現步驟1. 定義一個 CONTENT_URI 常量
// CONTENT_URI 的字串必須是唯一  public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentprovider");  // 如果有子表,URI為:  public static final Uri CONTENT_URI = Uri.parse("content://com.WangWeiDa.MyContentProvider/users");
2. 定義一個繼承自ContentProvider的類
 public class MyContentProvider extends ContentProvider { ... }
3. 實現ContentProvider的所有方法

為ContentProvider提供一個常量類 MyContentProviderMetaData.java

public class MyContentProviderMetaData {   //URI的指定,此處的字串必須和聲明的authorities一致   public static final String AUTHORITIES = "com.zhuanghongji.app.MyContentProvider";   //資料庫名稱   public static final String DATABASE_NAME = "myContentProvider.db";   //資料庫的版本   public static final int DATABASE_VERSION = 1;   //表名    public static final String USERS_TABLE_NAME = "user";      public static final class UserTableMetaData implements BaseColumns{       //表名       public static final String TABLE_NAME = "user";       //訪問該ContentProvider的URI       public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user");       //該ContentProvider所返回的資料類型的定義       public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.myprovider.user";       public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.myprovider.user";       //列名       public static final String USER_NAME = "name";       //預設的排序方法       public static final String DEFAULT_SORT_ORDER = "_id desc";   }  }

  

public class MyContentProvider extends ContentProvider {   //訪問表的所有列   public static final int INCOMING_USER_COLLECTION = 1;   //訪問單獨的列   public static final int INCOMING_USER_SINGLE = 2;   //操作URI的類   public static final UriMatcher uriMatcher;   //為UriMatcher添加自訂的URI   static{       uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);       uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user",           INCOMING_USER_COLLECTION);       uriMatcher.addURI(MyContentProviderMetaData.AUTHORITIES,"/user/#",           INCOMING_USER_SINGLE);   }      private DatabaseHelper dh;   //為資料庫表欄位起別名   public static HashMap userProjectionMap;   static   {       userProjectionMap = new HashMap();       userProjectionMap.put(UserTableMetaData._ID,UserTableMetaData._ID);       userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME);   }   /**   * 刪除表資料   */   @Override   public int delete(Uri uri, String selection, String[] selectionArgs) {       System.out.println("delete");       //得到一個可寫的資料庫       SQLiteDatabase db = dh.getWritableDatabase();       //執行刪除,得到刪除的行數       int count = db.delete(UserTableMetaData.TABLE_NAME, selection, selectionArgs);              return count;   }      /**   * 資料庫訪問類型   */   @Override   public String getType(Uri uri) {       System.out.println("getType");       //根據使用者請求,得到資料類型       switch (uriMatcher.match(uri)) {           case INCOMING_USER_COLLECTION:               return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE;           case INCOMING_USER_SINGLE:               return MyContentProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM;           default:               throw new IllegalArgumentException("UnKnown URI"+uri);       }   }      /**   * 插入資料   */   @Override   public Uri insert(Uri uri, ContentValues values) {       //得到一個可寫的資料庫       SQLiteDatabase db = dh.getWritableDatabase();       //向指定的表插入資料,得到返回的Id       long rowId = db.insert(UserTableMetaData.TABLE_NAME, null, values);       if(rowId > 0){ // 判斷插入是否執行成功           //如果添加成功,利用新添加的Id和           Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId);           //通知監聽器,資料已經改變           getContext().getContentResolver().notifyChange(insertedUserUri, null);              return insertedUserUri;       }          return uri;   }      /**   * 建立ContentProvider時調用的回呼函數   */   @Override   public boolean onCreate() {       System.out.println("onCreate");       //得到資料庫協助類       dh = new DatabaseHelper(getContext(),MyContentProviderMetaData.DATABASE_NAME);       return false;   }      /**   * 查詢資料庫   */   @Override   public Cursor query(Uri uri, String[] projection, String selection,   String[] selectionArgs, String sortOrder) {       //建立一個執行查詢的Sqlite       SQLiteQueryBuilder qb = new SQLiteQueryBuilder();       //判斷使用者請求,查詢所有還是單個       switch(uriMatcher.match(uri)){           case INCOMING_USER_COLLECTION:               //設定要查詢的表名               qb.setTables(UserTableMetaData.TABLE_NAME);               //設定表欄位的別名               qb.setProjectionMap(userProjectionMap);               break;           case INCOMING_USER_SINGLE:               qb.setTables(UserTableMetaData.TABLE_NAME);               qb.setProjectionMap(userProjectionMap);               // 追加條件,getPathSegments()得到使用者請求的Uri地址截取的數組,               // get(1)得到去掉地址中/以後的第二個元素               qb.appendWhere(UserTableMetaData._ID + "=" + uri.getPathSegments().get(1));               break;       }       //設定排序       String orderBy;       if(TextUtils.isEmpty(sortOrder)){           orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;       } else {           orderBy = sortOrder;       }       //得到一個可讀的資料庫       SQLiteDatabase db = dh.getReadableDatabase();       //執行查詢,把輸入傳入       Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);       //設定監聽       c.setNotificationUri(getContext().getContentResolver(), uri);       return c;   }      /**   * 更新資料庫   */   @Override   public int update(Uri uri, ContentValues values, String selection,   String[] selectionArgs) {       System.out.println("update");       //得到一個可寫的資料庫       SQLiteDatabase db = dh.getWritableDatabase();       //執行更新語句,得到更新的條數       int count = db.update(UserTableMetaData.TABLE_NAME, values, selection, selectionArgs);       return count;   }  }
4. 在AndroidMinifest.xml中進行聲明
<provider     android:name=".MyContentProvider"   android:authorities="com.zhuanghongji.app.MyContentProvider" />
三、另外一個執行個體說明1. Uri類簡介
Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")

  

在Content Provider中使用的查詢字串有別於標準的SQL查詢。 
諸如select, add, delete, modify等操作我們都使用一種特殊的URI來進行,這種URI由3個部分組成:

    • “content://”
    • 代表資料的路徑,
    • 一個可選的標識資料的ID
 content://media/internal/images  這個URI將返回裝置上儲存的所有圖片     content://contacts/people/       這個URI將返回裝置上的所有連絡人資訊     content://contacts/people/45     這個URI返回單個結果(連絡人資訊中ID為45的連絡人記錄)

儘管這種查詢字串格式很常見,但是它看起來還是有點令人迷惑。 
為此,Android提供一系列的協助類(在android.provider包下),裡麵包含了很多以類變數形式給出的查詢字串,這種方式更容易讓我們理解一點,因此,如上面 content://contacts/people/45 這個URI就可以寫成如下形式:

Uri person = ContentUris.withAppendedId(People.CONTENT_URI,  45);

然後執行資料查詢:

 Cursor cursor = managedQuery(person, null, null, null);

這個查詢返回一個包含所有資料欄位的遊標,我們可以通過迭代這個遊標來擷取所有的資料。

2. 範例

如何依次讀取連絡人資訊表中的指定資料列name和number。

 

public class ContentProviderDemo extends Activity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        displayRecords();    }    private void displayRecords() {        //該數組中包含了所有要返回的欄位        String columns[] = new String[] { People.NAME, People.NUMBER };        Uri mContacts = People.CONTENT_URI;        Cursor cur = managedQuery(              mContacts,              columns,    // 要返回的資料欄位             null,      // WHERE子句             null,      // WHERE 子句的參數             null       // Order-by子句        );       if (cur.moveToFirst()) {           String name = null;           String phoneNo = null;           do {              // 擷取欄位的值            name = cur.getString(cur.getColumnIndex(People.NAME));             phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER));             Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show();          } while (cur.moveToNext());       }    }}
3. 修改記錄

可以使用ContentResolver.update()方法來修改資料:

private void updateRecord(int recNo, String name) {    Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo);    ContentValues values = new ContentValues();    values.put(People.NAME, name);    getContentResolver().update(uri, values, null, null);}

現在你可以調用上面的方法來更新指定記錄:

updateRecord(10, ”XYZ”);   // 更改第10條記錄的name欄位值為“XYZ”
4. 添加記錄

可以調用ContentResolver.insert()方法:

  • 該方法接受一個要增加的記錄的目標URI,以及一個包含了新記錄值的Map對象,
  • 調用後的傳回值是新記錄的URI,包含記錄號。

上面的例子中我們都是基於連絡人資訊簿這個標準的Content Provider 
下面建立 insertRecords(name, phoneNo) 的方式來向連絡人資訊簿中新增連絡人...姓名和電話號碼。

private void insertRecords(String name, String phoneNo) {    ContentValues values = new ContentValues();    values.put(People.NAME, name);    Uri uri = getContentResolver().insert(People.CONTENT_URI, values);    Log.d(”ANDROID”, uri.toString());    Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);    values.clear();    values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);    values.put(People.NUMBER, phoneNo);    getContentResolver().insert(numberUri, values);
5. 刪除記錄
private void deleteRecords() {    Uri uri = People.CONTENT_URI;    getContentResolver().delete(uri, null, null);}

你也可以指定WHERE條件陳述式來刪除特定的記錄:

getContentResolver().delete(uri, "NAME=" + "‘XYZ XYZ‘", null);

這將會刪除name為‘XYZ XYZ’的記錄。

6. 建立ContentProvider1. 建立一個繼承了ContentProvider父類的類2. 定義一個名為CONTENT_URI,並且是public static final的Uri類型的類變數
  • 你必須為其指定一個唯一的字串值,最好的方案是以類的全名稱, 如:
public static final Uri CONTENT_URI = Uri.parse( "content://com.google.android.MyContentProvider");
3. 定義你要返回給用戶端的資料列名
  • 如果你正在使用Android資料庫,必須為其定義一個叫_id的列,它用來表示每條記錄的唯一性。
4. 建立你的資料存放區系統
  • 大多數Content Provider使用 Android檔案系統 或 SQLite資料庫 來保持資料,但是你也可以以任何你想要的方式來儲存。
  • 如果你要儲存位元組型資料,比如位元影像檔案等。 
    a. 資料列其實是一個表示實際儲存檔案的URI字串,通過它來讀取對應的檔案資料。 
    b. 處理這種資料類型的Content Provider需要實現一個名為_data的欄位,_data欄位列出了該檔案在Android檔案系統上的精確路徑。 
    c. 這個欄位不僅是供用戶端使用,而且也可以供 ContentResolver 使用。 
    d. 用戶端可以調用 ContentResolver.openOutputStream() 方法來處理該URI指向的檔案資源。 
    e. 如果是 ContentResolver 本身的話,由於其持有的許可權比用戶端要高,所以它能直接存取該資料檔案。
5. 聲明public static String型的變數
  • 用於指定要從遊標處返回的資料列。
6. 查詢返回一個Cursor類型的對象
  • 所有執行寫操作的方法如insert(), update() 以及delete()都將被監聽。
7. 在AndroidMenifest.xml中使用< provider />標籤來設定ContentProvider8. 如果要處理的資料類型是一種比較新的類型
  • 你就必須先定義一個新的MIME類型,以供ContentProvider.geType(url)來返回。
  • MIME類型有兩種形式: 
    1. 一種是為指定的單個記錄的
    2. 另一種是為多條記錄的。

這裡給出一種常用的格式:

vnd.android.cursor.item/vnd.yourcompanyname.contenttype // 單個記錄的MIME類型比如, 一個請求列車資訊的URIcontent://com.example.transportationprovider/trains/122 可能就會返回typevnd.android.cursor.item/vnd.example.rail這樣一個MIME類型
vnd.android.cursor.dir/vnd.yourcompanyname.contenttype // 多個記錄的MIME類型比如, 一個請求所有列車資訊的URIcontent://com.example.transportationprovider/trains 可能就會返回vnd.android.cursor.dir/vnd.example.rail這樣一個MIME 類型
9. 建立ContentProvider

下面代碼將建立一個 ContentProvider,它僅僅是儲存使用者名稱稱並顯示所有的使用者名稱稱。 
(使用 SQLLite資料庫儲存這些資料): 
(1)下面這個類中定義了ContentProvider的 CONTENT_URI,以及資料列

public class MyUsers {    public static final String AUTHORITY  = "com.zhuanghongji.MyContentProvider";    // BaseColumn類中已經包含了 _id欄位   public static final class User implements BaseColumns {        public static final Uri CONTENT_URI  = Uri.parse("content://com.zhuanghongji.MyContentProvider");        // 表資料列        public static final String  USER_NAME  = "USER_NAME";    }}

(2) 接著定義基於上面的類來定義實際的Content Provider類:

public class MyContentProvider extends ContentProvider {    private SQLiteDatabase     sqlDB;    private DatabaseHelper     dbHelper;    private static final String  DATABASE_NAME = “Users.db”;    private static final int  DATABASE_VERSION= 1;    private static final String TABLE_NAME= “User”;    private static final String TAG = “MyContentProvider”;    private static class DatabaseHelper extends SQLiteOpenHelper {        DatabaseHelper(Context context) {            super(context, DATABASE_NAME, null, DATABASE_VERSION);        }        @Override        public void onCreate(SQLiteDatabase db) {            //建立用於儲存資料的表            db.execSQL("Create table " + TABLE_NAME + "( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);");        }        @Override        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);            onCreate(db); // 調用onCreate()再進行建立        }    }    @Override    public int delete(Uri uri, String s, String[] as) {        return 0;    }    @Override    public String getType(Uri uri) {        return null;    }    @Override    public Uri insert(Uri uri, ContentValues contentvalues) {        sqlDB = dbHelper.getWritableDatabase();        long rowId = sqlDB.insert(TABLE_NAME,"",contentvalues);        if (rowId > 0) {            Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build();            getContext().getContentResolver().notifyChange(rowUri, null);            return rowUri;        }        throw new SQLException("Failed to insert row into " + uri);    }    @Override    public boolean onCreate() {        dbHelper = new DatabaseHelper(getContext());        return (dbHelper == null) ? false : true;    }    @Override    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();        SQLiteDatabase db = dbHelper.getReadableDatabase();        qb.setTables(TABLE_NAME);        Cursor c = qb.query(db, projection, selection, null, null, null, sortOrder);        c.setNotificationUri(getContext().getContentResolver(), uri);        return c;    }    @Override    public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {        return 0;    }}

一個名為MyContentProviderContent Provider建立完成了,它用於從Sqlite資料庫中添加和讀取記錄。

10. 在AndroidManifest.xml中配置:
<provider     android:name=”MyContentProvider”     android:authorities=”com.zhuanghongji.MyContentProvider” />
11. 使用剛才定義好的ContentProvider
  1. 為應用程式添加ContentProvider的存取權限。
  2. 通過getContentResolver()方法得到ContentResolver對象。
  3. 調用ContentResolver類的query()方法查詢資料,該方法會返回一個Cursor對象。
  4. 對得到的Cursor對象進行分析,得到需要的資料。
  5. 調用Cursor類的close()方法將Cursor對象關閉。
public class MyContentProviderDemo extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        insertRecord(”MyUser”);        displayRecords();    }    private void insertRecord(String userName) {        ContentValues values = new ContentValues();        values.put(MyUsers.User.USER_NAME, userName);        getContentResolver().insert(MyUsers.User.CONTENT_URI, values);    }    private void displayRecords() {        String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };        Uri myUri = MyUsers.User.CONTENT_URI;        Cursor cur = managedQuery(myUri, columns,null, null, null );        if (cur.moveToFirst()) {            String id = null;            String userName = null;            do {                id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));                userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));                Toast.makeText(this, id + ” ” + userName, Toast.LENGTH_LONG).show();           } while (cur.moveToNext());       }    }}

  

Android 總結:ContentProvider 的使用

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.