Android中資料共用機制的實現——ContentProvider的應用從入門到精通

來源:互聯網
上載者:User
Android中的Content Provider可以實現在許可權許可的情況下,在多個應用程式之間共用資料。Android還提供了一些主要資料類型如音頻、視頻、圖片和私人通訊錄等現成的Content provider類。當然,如果我們想讓我們應用程式的資料公有化,使其它應用也可以訪問,那麼,我們可以實現一個自己的ContentProvider類。一種類型的ContentProvider只允許有一個執行個體,但它可以和多個不同的應用程式或進程的ContentResolver進行通訊。進程間的互動就是由ContentResolver和ContentProvider一起完成的。我們可以通過getContentResolver()方法來獲得 ContentResolver對象。 資料模型: ContentProvider以資料表的形式對外暴露,其中每一行資料都有一個唯一的_ID值。每次查詢資料時都會返回一個Cursor 對象。 不過使用Cursor對象擷取資料前一定要Crowdsourced Security Testing道資料的類型。 Android中通過URI來定位各種資源,每一個可以調用的資源都有一個唯一的URI,片,視頻等。同樣,ContentProvider中的每一個表也各有一個唯一的URI。ContentProvider提供的URI都是以content://開頭的。在所有ContentProvider互動中都要用到URI,而且,每個ContentResolver方法的第一個參數就是URI,它告訴ContentResolver方法要訪問的ContentProvider對象,以及操作的目標表。 因此,為了方便,給自訂ContentProvider中的URI指定一個專門的CONTENT_URI常量是很有必要的。通過Content Provider查詢資料前要準備的: 1,要查詢資料的Provider的URI; 2,要查詢的資料的列名以及它們的資料類型;其中_ID和_COUNT列唯一某行資料以及總資料行數。 3,如果是某一行資料,還需要知道它的ID值;如果_ID=23 則為“content://. . . ./23” 查詢資料: 可以用ContentResolver.query()方法或Activity.managedQuery()方法來擷取結果Cursor。後者得到的Cursor的生命週期由Activity來管理,而不需要我們再手動關閉遊標,我們還可以方便的精確控制它的載入與釋放。Activity.startManagingCursor()方法可以將未受管理的遊標加入到管理列表中(註:2.3版以後推薦使用getLoaderManager()得到LoaderManger對象來管理遊標)stopManagingCursor(Cursor)可以停止自動管理功能。 1,擷取URI: ContentUris.withAppendedId() Uri.withAppendedPath() 以上兩個方法都可以實現將參數(ID)添加到查詢URI當中去。 而ContentUris.parseId(uri)方法可以擷取URI中的ID部分。樣本: import android.provider.Contacts.People; import android.content.ContentUris; import android.net.Uri; import android.database.Cursor; Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI, 23); Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI, "23"); Cursor cur = managedQuery(myPerson, null, null, null, null); 2,讀取資料: private void getColumnData(Cursor cur){     if (cur.moveToFirst()) {         String name;         String phoneNumber;         int nameColumn = cur.getColumnIndex(People.NAME);         int phoneColumn = cur.getColumnIndex(People.NUMBER);         String imagePath;             do {             // Get the field values             name = cur.getString(nameColumn);             phoneNumber = cur.getString(phoneColumn);                         // Do something with the values.             ...         } while (cur.moveToNext());     } } 如果是較小的位元據,可以直接像普通資料一樣儲存到表中去,但如果位元據量較大,且表的URI是content:URI形式的,則我們需要採用ContentResolver.openInputStream()方法來獲得資料流,以便讀取資料。 所有資料的更改都需要調用ContentResolver的方法來完成。 首先,將要更改的資料儲存到ContentValues對象中,然後調用ContentResolver的相應方法面完成更新操作,樣本: 增加新記錄: ContentValues values = new ContentValues(); values.put(People.NAME, "Abraham Lincoln"); values.put(People.STARRED, 1); Uri uri = getContentResolver().insert(People.CONTENT_URI, values); 在原有記錄上增加新值: Uri phoneUri = null; phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY); values.clear(); values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE); values.put(People.Phones.NUMBER, "1233214567"); getContentResolver().insert(phoneUri, values); 其中的CONTENT_DIRECTORY是一個常量,表明這是追加的資料。 如果資料是content:URI形式的二進位大對象,調用ContentResolver.openOutputStream()來儲存。 大位元據(多媒體資料)處理樣本: import android.provider.MediaStore.Images.Media; import android.content.ContentValues; import java.io.OutputStream; // Save the name and description of an image in a ContentValues map.   ContentValues values = new ContentValues(3); values.put(Media.DISPLAY_NAME, "road_trip_1"); values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles"); values.put(Media.MIME_TYPE, "image/jpeg"); // Add a new record without the bitmap, but with the values just set. // insert() returns the URI of the new record. Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values); // Now get a handle to the file for that record, and save the data into it. // Here, sourceBitmap is a Bitmap object representing the file to save to the database. try {     OutputStream outStream = getContentResolver().openOutputStream(uri);     sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);     outStream.close(); } catch (Exception e) {     Log.e(TAG, "exception while writing image", e); } 實現我們自己的Content Provider: 1,需要建立一個類並且繼承ContentProvider類,並實現這個抽象類別中的六個方法。 public int delete(Uri uri, String selection, String[] selectionArgs){} public Uri insert(Uri uri, ContentValues values) {} public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {}  //SQLiteCursor,MatrixCursor public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {} public boolean onCreate() {} public String getType(Uri uri) {} 2,定義URI常量: public static final Uri CONTENT_URI; CONTENT_URI=Uri.parse("content://com.example.codelab.transportationprovider"); 3,定義列名,不要忘記_ID列。 public static String ColumnName 4,如果我們的資料在是新的類型,必須在getType ()中返回這個定義好的MIME 類型,自訂類型的名稱格式為: vnd.android.cursor.item/vnd.yourcompanyname.contenttype vnd.android.cursor.dir/vnd.yourcompanyname.contenttype 5,當儲存大位元據時,列名下實際儲存的是content:URI,另聲明一個“_data”列,儲存檔案的真實路徑,以供ContentResolver.openInputStream()時訪問。 6,當資料發生改變時,最好還要調用ContentResolver.notifyChange()方法,來通知調用程式。 7,在AndroidManifest.xml中聲明ContentProvider。 樣本: <provider android:name=".AutoInfoProvider"           android:authorities="com.example.autos.autoinfoprovider"           . . . /> </provider> <provider>標籤的屬性中還可以設定ContentProvider的更多參數。 一個完整的自訂ContentProvider及共用調用與事件偵聽的例子:資料結構User.java:Java代碼 1. package com.yaku.pojo;  2.   3. public class User {  4.     private int id;  5.     private String name;  6.     private int age;  7.       8.     public User(int id, String name, int age) {  9.         super();  10.         this.id = id;  11.         this.name = name;  12.         this.age = age;  13.     }  14.     public int getId() {  15.         return id;  16.     }  17.     public void setId(int id) {  18.         this.id = id;  19.     }  20.     public String getName() {  21.         return name;  22.     }  23.     public void setName(String name) {  24.         this.name = name;  25.     }  26.     public int getAge() {  27.         return age;  28.     }  29.     public void setAge(int age) {  30.         this.age = age;  31.     }  32.     @Override  33.     public String toString() {  34.         return "User [age=" + age + ", id=" + id + ", name=" + name + "]";  35.     }  36. }   資料庫操作DBOpenHelper.java:Java代碼1. package com.yaku.db;  2.   3. import android.content.Context;  4. import android.database.sqlite.SQLiteDatabase;  5. import android.database.sqlite.SQLiteOpenHelper;  6.   7. public class DBOpenHelper extends SQLiteOpenHelper {  8.     private static final String DBNAME = "yaku.db"; //資料庫名稱  9.     private static final int DBVER = 1;//資料庫版本  10.       11.     public DBOpenHelper(Context context) {  12.         super(context, DBNAME, null, DBVER);  13.     }  14.   15.     @Override  16.     public void onCreate(SQLiteDatabase db) {  17.         String sql = "CREATE TABLE user (userid integer primary key autoincrement, name varchar(20), age integer)";  18.         db.execSQL(sql);//執行有更改的sql語句  19.     }  20.   21.     @Override  22.     public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {  23.         db.execSQL("DROP TABLE IF EXISTS user");  24.         onCreate(db);  25.     }  26.   27. }   對外共用處理類ContentProviderUser.java:Java代碼 1. package com.yaku.ContentProvider;  2.   3. import com.yaku.db.DBOpenHelper;  4.   5. import android.content.ContentProvider;  6. import android.content.ContentUris;  7. import android.content.ContentValues;  8. import android.content.UriMatcher;  9. import android.database.Cursor;  10. import android.database.sqlite.SQLiteDatabase;  11. import android.net.Uri;  12.   13. public class ContentProviderUser extends ContentProvider {  14.     private DBOpenHelper dbOpenHelper;  15.     //常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼  16.     private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);  17.     private static final int USERS = 1;  18.     private static final int USER = 2;  19.     static{  20.         //如果match()方法匹配content://com.yaku.ContentProvider.userprovider/user路徑,返回匹配碼為1  21.         MATCHER.addURI("com.yaku.ContentProvider.userprovider", "user", USERS);  22.         //如果match()方法匹配content://com.yaku.ContentProvider.userprovider/user/123路徑,返回匹配碼為2  23.         MATCHER.addURI("com.yaku.ContentProvider.userprovider", "user/#", USER);//#號為萬用字元  24.     }     25.     @Override  26.     public int delete(Uri uri, String selection, String[] selectionArgs) {  27.         SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  28.         int count = 0;  29.         switch (MATCHER.match(uri)) {  30.         case USERS:  31.             count = db.delete("user", selection, selectionArgs);  32.             return count;  33.         case USER:  34.             //ContentUris類用於擷取Uri路徑後面的ID部分  35.             long id = ContentUris.parseId(uri);  36.             String where = "userid = "+ id;  37.             if(selection!=null && !"".equals(selection)){  38.                 where = selection + " and " + where;  39.             }  40.             count = db.delete("user", where, selectionArgs);  41.             return count;  42.         default:  43.             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());  44.         }  45.     }  46.   47.     /** 48.      * 該方法用於返回當前Url所代表資料的MIME類型。 49.      * 如果操作的資料屬於集合類型,那麼MIME類型字串應該以vnd.android.cursor.dir/開頭 50.      * 如果要操作的資料屬於非集合類型資料,那麼MIME類型字串應該以vnd.android.cursor.item/開頭 51.      */  52.     @Override  53.     public String getType(Uri uri) {  54.         switch (MATCHER.match(uri)) {  55.         case USERS:           56.             return "vnd.android.cursor.dir/user";  57.               58.         case USER:            59.             return "vnd.android.cursor.item/user";  60.               61.         default:  62.             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());  63.         }  64.     }  65.   66.     @Override  67.     public Uri insert(Uri uri, ContentValues values) {  68.         SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  69.         switch (MATCHER.match(uri)) {  70.         case USERS:  71.             long rowid = db.insert("user", "name", values);   72.             Uri insertUri = ContentUris.withAppendedId(uri, rowid);//得到代表新增記錄的Uri  73.             this.getContext().getContentResolver().notifyChange(uri, null);  74.             return insertUri;  75.   76.         default:  77.             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());  78.         }  79.     }  80.   81.     @Override  82.     public boolean onCreate() {  83.         this.dbOpenHelper = new DBOpenHelper(this.getContext());  84.         return false;  85.     }  86.   87.     @Override  88.     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  89.             String sortOrder) {  90.         SQLiteDatabase db = dbOpenHelper.getReadableDatabase();  91.         switch (MATCHER.match(uri)) {  92.         case USERS:  93.             return db.query("user", projection, selection, selectionArgs, null, null, sortOrder);  94.         case USER:  95.             long id = ContentUris.parseId(uri);  96.             String where = "userid = "+ id;  97.             if(selection!=null && !"".equals(selection)){  98.                 where = selection + " and " + where;  99.             }  100.             return db.query("user", projection, where, selectionArgs, null, null, sortOrder);  101.         default:  102.             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());  103.         }  104.     }  105.   106.     @Override  107.     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {  108.         SQLiteDatabase db = dbOpenHelper.getWritableDatabase();  109.         int count = 0;  110.         switch (MATCHER.match(uri)) {  111.         case USERS:  112.             count = db.update("person", values, selection, selectionArgs);  113.             return count;  114.         case USER:  115.             long id = ContentUris.parseId(uri);  116.             String where = "userid = "+ id;  117.             if(selection!=null && !"".equals(selection)){  118.                 where = selection + " and " + where;  119.             }  120.             count = db.update("user", values, where, selectionArgs);  121.             return count;  122.         default:  123.             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());  124.         }  125.     }  126. }   單元測試類(在另一個應用中): Java代碼1. package com.yaku.ContentProvider;  2.   3. import android.content.ContentResolver;  4. import android.content.ContentValues;  5. import android.database.Cursor;  6. import android.net.Uri;  7. import android.test.AndroidTestCase;  8. import android.util.Log;  9.   10. /** 11.  * 對ContentProvider工程中的ContentProviderActivity進行單元測試 12.  */  13. public class ContentProviderActivityTest extends AndroidTestCase {  14.     private static final String TAG = "ContentProvider";  15.     //往內容提供者添加資料  16.     public void testInsert() throws Throwable{  17.         ContentResolver contentResolver = this.getContext().getContentResolver();  18.         Uri insertUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user");  19.         ContentValues values = new ContentValues();  20.         values.put("name", "道長");  21.         values.put("age", 86);  22.         Uri uri = contentResolver.insert(insertUri, values);  23.         Log.i(TAG, uri.toString());  24.     }  25.       26.     //更新內容提供者中的資料  27.     public void testUpdate() throws Throwable{  28.         ContentResolver contentResolver = this.getContext().getContentResolver();  29.         Uri updateUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user/1");  30.         ContentValues values = new ContentValues();  31.         values.put("name", "青眉道長");  32.         contentResolver.update(updateUri, values, null, null);  33.     }  34.       35.     //從內容提供者中刪除資料  36.     public void testDelete() throws Throwable{  37.         ContentResolver contentResolver = this.getContext().getContentResolver();  38.         Uri deleteUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user/1");  39.         contentResolver.delete(deleteUri, null, null);  40.     }  41.       42.     //擷取內容提供者中的資料  43.     public void testFind() throws Throwable{  44.         ContentResolver contentResolver = this.getContext().getContentResolver();  45.         Uri selectUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user");  46.         Cursor cursor = contentResolver.query(selectUri, null, null, null, "userid desc");  47.         while(cursor.moveToNext()){  48.             int id = cursor.getInt(cursor.getColumnIndex("userid"));  49.             String name = cursor.getString(cursor.getColumnIndex("name"));  50.             int age = cursor.getInt(cursor.getColumnIndex("age"));  51.             Log.i(TAG, "id="+ id + ",name="+ name+ ",age="+ age);  52.         }  53.     }  54.       55. }   監聽資料的變化:Java代碼1. <span style="font-size: medium;">package com.yaku.ContentProvider;  2.   3. import android.content.ContentResolver;  4. import android.content.ContentValues;  5. import android.database.Cursor;  6. import android.net.Uri;  7. import android.test.AndroidTestCase;  8. import android.util.Log;  9.   10. /** 11.  * 監聽資料變化 12.  */  13. public class OtherContentProviderTest extends AndroidTestCase {  14.     private static final String TAG = "OtherContentProvider";  15.       16.     @Override  17.     public void onCreate(Bundle savedInstanceState) {  18.         super.onCreate(savedInstanceState);  19.         setContentView(R.layout.main);  20.           21.         Uri insertUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user");  22.         ContentResolver contentResolver = this.getContentResolver();  23.         //對指定uri進行監聽,如果該uri代表的資料發生變化,就會調用PersonObserver中的onChange()  24.         contentResolver.registerContentObserver(insertUri, true, new PersonObserver(new Handler()));  25.     }  26.       27.     private final class PersonObserver extends ContentObserver{  28.         public PersonObserver(Handler handler) {  29.             super(handler);  30.         }  31.   32.         @Override  33.         public void onChange(boolean selfChange) {  34.             ContentResolver contentResolver = getContentResolver();  35.             Uri selectUri = Uri.parse("content://com.yaku.ContentProvider.userprovider/user");  36.             Cursor cursor = contentResolver.query(selectUri, null, null, null, "userid desc");  37.             while(cursor.moveToNext()){  38.                 int id = cursor.getInt(cursor.getColumnIndex("userid"));  39.                 String name = cursor.getString(cursor.getColumnIndex("name"));  40.                 int age = cursor.getInt(cursor.getColumnIndex("age"));  41.                 Log.i(TAG, "id="+ id + ",name="+ name+ ",age="+ age);  42.             }  43.         }  44.     }  45. }  《完》
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.