Android基礎 : ContentProvider和getContentResolver

        安卓系統中的數據庫SqlLite操做和java中mysql的數據庫操做很不同,形成這樣的緣由是由於在安卓中數據庫是屬於進程的不存在數據庫客戶端,也不存在數據庫服務器。html

 關於SqlLite數據庫的文章能夠參考 Android 使用SQLite數據庫詳解 : http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1111/540.htmljava

  ContentProvidermysql

 

由於在Android系統裏面,數據庫是私有的。通常狀況下外部應用程序是沒有權限讀取其餘應用程序的數據。若是你想公開你本身的數據,你有兩個選擇:你能夠建立你本身的內容提供器(一個ContentProvider子類)或者你能夠給已有的提供器添加數據-若是存在一個控制一樣類型數據的內容提供器且你擁有寫的權限。而外界根本看不到,也不用看到這個應用暴露的數據在應用當中是如何存儲的,或者是用數據庫存儲仍是用文件存儲,仍是經過網上得到,這些一切都不重要,重要的是外界能夠經過這一套標準及統一的接口和程序裏的數據打交道,能夠讀取程序的數據,也能夠刪除程序的數據,固然,中間也會涉及一些權限的問題。android

而如下方法是須要在子類實現的:web

boolean onCreate() Called when the provider is being started.    
Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) 經過Uri進行查詢,返回一個Cursor。 
Uri insert(Uri url, ContentValues values) 將一組數據插入到Uri 指定的地方,返回新inserted的URI。   
int update(Uri uri, ContentValues values, String where, String[] selectionArgs) 更新Uri指定位置的數據,返回所影響的行數。   
int delete(Uri url, String where, String[] selectionArgs) 刪除指定Uri而且符合必定條件的數據,返回所影響的行數。  
String getType (Uri uri) 獲取所查詢URI的MIME類型,若是沒有類型則返回null。

應用程序能夠在Content Provider中執行以下操做:
查詢數據sql

修改數據數據庫

添加數據數組

刪除數據服務器

當咱們不須要把數據提供給第三方應用程序使用的話,能夠不實現Content Provider。app

 ContentResolver / getContentResolver()

外界的程序經過ContentResolver接口能夠訪問ContentProvider提供的數據,在Activity當中經過getContentResolver()能夠獲得當前應用的 ContentResolver實例。ContentResolver提供的接口和ContentProvider中須要實現的接口對應,具體能夠查看API Doc,不過能夠發現ContentResolver裏面的方法不少都是final或者static的。在實際應用中,咱們不多實現ContentResolver抽象類,更多時候經過getContentResolver()從一個Activity或其它應用程序組件的實現裏獲取一個ContentResolver

ContentResolver cr = getContentResolver();

而後你可使用這個ContentResolver的方法來和你感興趣的任何內容提供器交互。

當初始化一個查詢時,Android系統識別查詢目標的內容提供器並確保它正在運行。系統實例化全部的ContentProvider對象;你歷來不須要本身作。事實上,你歷來不會直接處理ContentProvider對象。一般,對於每一個類型的ContentProvider只有一個簡單的實例。但它可以和不一樣應用程序和進程中的多個ContentProvider對象通信。進程間的交互經過ContentResolver和ContentProvider類處理。

 

查詢記錄:
        
在Content Provider中使用的查詢字符串有別於標準的SQL查詢。不少諸如select, add, delete, modify等操做咱們都使用一種特殊的URI來進行,這種URI由3個部分組成, 「content://」, 表明數據的路徑,和一個可選的標識數據的ID。如下是一些示例URI:
        content://media/internal/images  這個URI將返回設備上存儲的全部圖片
        content://contacts/people/  這個URI將返回設備上的全部聯繫人信息
        content://contacts/people/45 這個URI返回單個結果(聯繫人信息中ID爲45的聯繫人記錄)

        儘管這種查詢字符串格式很常見,可是它看起來仍是有點使人迷惑。爲此,Android提供一系列的幫助類(在android.provider包下),裏面包含了不少以類變量形式給出的查詢字符串,這種方式更容易讓咱們理解一點,參見下例:

        MediaStore.Images.Media.INTERNAL_CONTENT_URI
        Contacts.People.CONTENT_URI

        所以,如上面content://contacts/people/45這個URI就能夠寫成以下形式:

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

        而後執行數據查詢:

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

        這個查詢返回一個包含全部數據字段的遊標,咱們能夠經過迭代這個遊標來獲取全部的數據:

package com.wissen.testApp;         
                   
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());         
       }         
    }         
}

 上例示範了一個如何依次讀取聯繫人信息表中的指定數據列name和number。


修改記錄:
       
咱們可使用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」

 

添加記錄:
        要增長記錄,咱們能夠調用ContentResolver.insert()方法,該方法接受一個要增長的記錄的目標URI,以及一個包含了新記錄值的Map對象,調用後的返回值是新記錄的URI,包含記錄號。
        上面的例子中咱們都是基於聯繫人信息簿這個標準的Content Provider,如今咱們繼續來建立一個insertRecord() 方法以對聯繫人信息簿中進行數據的添加:

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);          
}

  這樣咱們就能夠調用insertRecords(name, phoneNo)的方式來向聯繫人信息簿中添加聯繫人姓名和電話號碼。


刪除記錄:
        
Content Provider中的getContextResolver.delete()方法能夠用來刪除記錄,下面的記錄用來刪除設備上全部的聯繫人信息:

private void deleteRecords() {          
    Uri uri = People.CONTENT_URI;          
    getContentResolver().delete(uri, null, null);          
}

 你也能夠指定WHERE條件語句來刪除特定的記錄:

        getContentResolver().delete(uri, 「NAME=」 + 「‘XYZ XYZ’」, null);

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

建立Content Provider:
        至此咱們已經知道如何使用Content Provider了,如今讓咱們來看下如何本身建立一個Content Provider。
        要建立咱們本身的Content Provider的話,咱們須要遵循如下幾步:
        1. 建立一個繼承了ContentProvider父類的類
        2. 定義一個名爲CONTENT_URI,而且是public static final的Uri類型的類變量,你必須爲其指定一個惟一的字符串值,最好的方案是以類的全名稱, 如:
        public static final Uri CONTENT_URI = Uri.parse( 「content://com.google.android.MyContentProvider」);
        3. 建立你的數據存儲系統。大多數Content Provider使用Android文件系統或SQLite數據庫來保持數據,可是你也能夠以任何你想要的方式來存儲。
        4. 定義你要返回給客戶端的數據列名。若是你正在使用Android數據庫,則數據列的使用方式就和你以往所熟悉的其餘數據庫同樣。可是,你必須爲其定義一個叫_id的列,它用來表示每條記錄的惟一性。
        5. 若是你要存儲字節型數據,好比位圖文件等,那保存該數據的數據列實際上是一個表示實際保存文件的URI字符串,客戶端經過它來讀取對應的文件數據,處理這種數據類型的Content Provider須要實現一個名爲_data的字段,_data字段列出了該文件在Android文件系統上的精確路徑。這個字段不只是供客戶端使用,並且也能夠供ContentResolver使用。客戶端能夠調用ContentResolver.openOutputStream()方法來處理該URI指向的文件資源,若是是ContentResolver自己的話,因爲其持有的權限比客戶端要高,因此它能直接訪問該數據文件。
        6. 聲明public static String型的變量,用於指定要從遊標處返回的數據列。
        7. 查詢返回一個Cursor類型的對象。全部執行寫操做的方法如insert(), update() 以及delete()都將被監聽。咱們能夠經過使用ContentResover().notifyChange()方法來通知監聽器關於數據更新的信息。
        8. 在AndroidMenifest.xml中使用<provider>標籤來設置Content Provider。
        9. 若是你要處理的數據類型是一種比較新的類型,你就必須先定義一個新的MIME類型,以供ContentProvider.geType(url)來返回。MIME類型有兩種形式:一種是爲指定的單個記錄的,還有一種是爲多條記錄的。這裏給出一種經常使用的格式:

        vnd.android.cursor.item/vnd.yourcompanyname.contenttype (單個記錄的MIME類型)
        好比, 一個請求列車信息的URI如content://com.example.transportationprovider/trains/122 可能就會返回typevnd.android.cursor.item/vnd.example.rail這樣一個MIME類型。

        vnd.android.cursor.dir/vnd.yourcompanyname.contenttype (多個記錄的MIME類型)
        好比, 一個請求全部列車信息的URI如content://com.example.transportationprovider/trains 可能就會返回vnd.android.cursor.dir/vnd.example.rail這樣一個MIME 類型。

        下列代碼將建立一個Content Provider,它僅僅是存儲用戶名稱並顯示全部的用戶名稱(使用 SQLLite數據庫存儲這些數據):

package com.wissen.testApp;         
                   
public class MyUsers {         
    public static final String AUTHORITY  = 「com.wissen.MyContentProvider」;         
                   
    // BaseColumn類中已經包含了 _id字段         
   public static final class User implements BaseColumns {         
        public static final Uri CONTENT_URI  = Uri.parse(」content://com.wissen.MyContentProvider」);         
        // 表數據列         
     public static final String  USER_NAME  = 「USER_NAME」;         
    }         
}

        上面的類中定義了Content Provider的CONTENT_URI,以及數據列。下面咱們將定義基於上面的類來定義實際的Content Provider類:

package com.wissen.testApp.android;         
                   
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);         
        }         
    }         
                   
    @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;         
    }         
}

 

一個名爲MyContentProvider的Content Provider建立完成了,它用於從Sqlite數據庫中添加和讀取記錄。
 Content Provider的入口須要在AndroidManifest.xml中配置:

 <provider android:name=」MyContentProvider」 android:authorities=」com.wissen.MyContentProvider」 />

 以後,讓咱們來使用這個定義好的Content Provider:

package com.wissen.testApp;       
               
public class MyContentDemo 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());       
       }       
    }       
}

 上面的類將先向數據庫中添加一條用戶數據,而後顯示數據庫中全部的用戶數據。
 至此,咱們已經明白如何來使用和建立Content Provider了。

相關文章
相關標籤/搜索