Android四大組件之ContentProvider

導讀

Android四大組件之ContentProvider簡介

Content provider管理android以結構化方式存放的數據。他以相對安全的方式封裝數據并且提供簡易的處理機制。Content provider提供不同進程(應用程序)間數據交互的標準化接口,它提供了一套完整的機制,允許一個程序訪問另一個程序中的數據,同時還能保證被訪問數據的安全性。
目前,使用內容提供器是Android實現跨進程共享數據的標準方式。
不同于文件存儲和SharedPreferences存儲中的兩種全局可讀寫操作模式,內容提供者可以選擇只對哪一部分數據進行共享,從而保證我們程序中的隱私數據不會有泄露的風險。

定義

  • 管理對結構化數據集的訪問,封裝數據并提供用于定義數據安全性的機制.
  • ContentProvider是Android系統中提供的專門用戶不同應用間進行數據共享的組件,提供了一套標準的接口用來獲取以及操作數據,準許開發者把自己的應用數據根據需求開放給其他應用進行增刪改查,而無須擔心直接開放數據庫權限而帶來的安全問題。

作用

  • 進程間進行數據交互 & 共享,就是跨進程通信.也可以進行進程內通信.
  • 跨進程通信示意圖

原理

  • 底層采用的是Android中的Binder機制

具體使用

統一資源標識符(URI)

標識數據的URI, URI包括整個提供程序的符號名稱(授權)和一個指向表的名稱(路徑)。

外界進程可以通過URI找到對應的ContentProvider&其表內的數據,然后再進行操作。

URI 分為 系統預置 和 自定義兩種 , 分別對應系統內置的數據(通訊錄,日歷等) 和自定義數據庫

  Uri uri = Uri.parse("content://com.carson.provider/User/1")
//標明 URI指向的資源是名為 "com.carson.provider"中ContentProvider中的表名為User,ID為1的數據
//特別注意: URI模式存在匹配通配符 * 和 #
// *:匹配任意長度的任何有效字符的字符串
// 以下的URI 表示 匹配provider的任何內容
  content://com.example.app.provider/* 
// #:匹配任意長度的數字字符的字符串
// 以下的URI 表示 匹配provider中的table表的所有行
  content://com.example.app.provider/table/# 

MIME 數據類型

指定某個擴展名的文件用某種應用程序來打開.比如.txt的文件用 text文件打開ContentProvider 根據 URI 返回MIME的類型。

ContentProvider.getType(uri);

每種MIME類型由2個部分組成= 類型+子類型, 就是說MIME類型是包含了2個部分的字符串:

text/html
application/pdf

ContentProvider類

ContentProvider 主要以 表格的形式 組織數據,同時也支持文件數據,ContentProvider的核心方法增刪改查數據(進程間共享數據的本質)

//<-- 4個核心方法 -->
// 外部進程向 ContentProvider 中添加數據
 public Uri insert(Uri uri, ContentValues values) 

// 外部進程 刪除 ContentProvider 中的數據
 public int delete(Uri uri, String selection, String[] selectionArgs) 

// 外部進程更新 ContentProvider 中的數據
 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

// 外部應用 獲取 ContentProvider 中的數據
 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  String sortOrder)  

// 注:
// 1. 上述4個方法由外部進程回調,并運行在ContentProvider進程的Binder線程池中(不是主線程)
// 2. 存在多線程并發訪問,需要實現線程同步
    // a. 若ContentProvider的數據存儲方式是使用SQLite & 一個,則不需要,因為SQLite內部實現好了線程同步,若是多個SQLite則需要,因為SQL對象之間無法進行線程同步
    // b. 若ContentProvider的數據存儲方式是內存,則需要自己實現線程同步

//<-- 2個其他方法 -->
// ContentProvider創建后 或 打開系統后其它進程第一次訪問該ContentProvider,運行在ContentProvider進程的主線程,故不能做耗時操作時 由系統進行調用
public boolean onCreate() 

// 得到數據類型,即返回當前 Url 所代表數據的MIME類型
public String getType(Uri uri)

ContentResolver類

ContentResolver統一管理不同ContentProvider間的操作

  1. 通過URI即可操作不同ContentProvider中的數據
  2. 外部進程通過ContentResolver類與ContentProvider進行交互.

問:為什么不直接只用ContentProvider進行交互,中間還加了一層ContenResolver?

  1. 如果app要和多個ContentProvider進行交互,實現不同的ContentProvider再完成數據交互,操作成本高難度也大, 所以在ContentProvider類上再加上一層ContentResolver類對所有的ContentProvider類進行統一管理.

核心方法:

// 外部進程向 ContentProvider 中添加數據
public Uri insert(Uri uri, ContentValues values)  

// 外部進程 刪除 ContentProvider 中的數據
public int delete(Uri uri, String selection, String[] selectionArgs)

// 外部進程更新 ContentProvider 中的數據
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)  

// 外部應用 獲取 ContentProvider 中的數據
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

使用時:

// 使用ContentResolver前,需要先獲取ContentResolver
// 可通過在所有繼承Context的類中 通過調用getContentResolver()來獲得ContentResolver
ContentResolver resolver =  getContentResolver(); 

// 設置ContentProvider的URI
Uri uri = Uri.parse("content://cn.scu.myprovider/user"); 

// 根據URI 操作 ContentProvider中的數據
// 此處是獲取ContentProvider中 user表的所有記錄 
Cursor cursor = resolver.query(uri, null, null, null,"userid desc"); 

ContentUris類

ContentUris的作用就是操作URI,核心方法:withAppendedId()、parseId()。

// withAppendedId()作用:向URI追加一個id
Uri uri = Uri.parse("content://cn.scu.myprovider/user") 
Uri resultUri = ContentUris.withAppendedId(uri, 7);  
// 最終生成后的Uri為:content://cn.scu.myprovider/user/7

// parseId()作用:從URL中獲取ID
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7") 
long personid = ContentUris.parseId(uri); 
//獲取的結果為:7 

UriMatcher類

UriMatcher的作用是:

  • 在ContentProvider注冊 URI
  • 根據URI匹配ContentProvider中對應的數據表
// 步驟1:初始化UriMatcher對象
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 
//常量UriMatcher.NO_MATCH  = 不匹配任何路徑的返回碼
// 即初始化時不匹配任何東西

// 步驟2:在ContentProvider 中注冊URI(addURI())
int URI_CODE_a = 1;
int URI_CODE_b = 2;
matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a); 
matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b); 
// 若URI資源路徑 = content://cn.scu.myprovider/user1 ,則返回注冊碼URI_CODE_a
// 若URI資源路徑 = content://cn.scu.myprovider/user2 ,則返回注冊碼URI_CODE_b

// 步驟3:根據URI 匹配 URI_CODE,從而匹配ContentProvider中相應的資源(match())

@Override   
public String getType(Uri uri) {   
  Uri uri = Uri.parse(" content://cn.scu.myprovider/user1");   

  switch(matcher.match(uri)){   
 // 根據URI匹配的返回碼是URI_CODE_a
 // 即matcher.match(uri) == URI_CODE_a
  case URI_CODE_a:   
    return tableNameUser1;   
    // 如果根據URI匹配的返回碼是URI_CODE_a,則返回ContentProvider中的名為tableNameUser1的表
  case URI_CODE_b:   
    return tableNameUser2;
    // 如果根據URI匹配的返回碼是URI_CODE_b,則返回ContentProvider中的名為tableNameUser2的表
}   
}

ContentObserver類

內容觀察者,觀察ContentProvider中的數據變化(增刪改) 、通知外界。

// 步驟1:注冊內容觀察者ContentObserver
getContentResolver().registerContentObserver(uri);
// 通過ContentResolver類進行注冊,并指定需要觀察的URI

// 步驟2:當該URI的ContentProvider數據發生變化時,通知外界(即訪問該ContentProvider數據的訪問者)
public class UserContentProvider extends ContentProvider { 
  public Uri insert(Uri uri, ContentValues values) { 
  db.insert("user", "userid", values); 
  getContext().getContentResolver().notifyChange(uri, null); 
  // 通知訪問者
 } 
}

// 步驟3:解除觀察者
getContentResolver().unregisterContentObserver(uri);
// 同樣需要通過ContentResolver類進行解除

創建自己的內容提供器

public class MyContentProvider extends ContentProvider {
    public MyContentProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。