Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android應用開發入門(三十八)內容提供者ContentProvider的創建使用

Android應用開發入門(三十八)內容提供者ContentProvider的創建使用

編輯:Android開發實例

 前言

  本文講講ContentProvider,內容提供者。前面已經講過了數據持久化,但是除了共享內存(SDCard)的數據外,其他包括SQLite、SharedPreferences都是僅限於被當前所創建的應用訪問,而無法使它們的數據在應用程序之間交換數據,所以Android提供了ContentProvider,ContentProvider是不同應用程序之間進行數據交換的標准API。雖然Android附帶了需要有用的內容提供者,但是本文不涉及這方面的內容,而是專注講解如何創建自己的ContentProvider,並在其他應用中如何調用。

 

概述

  ContentProvider可以理解為一個Android應用對外開放的接口,只要是符合它所定義的Uri格式的請求,均可以正常訪問執行操作。其他的Android應用可以使用ContentResolver對象通過與ContentProvider同名的方法請求執行,被執行的就是ContentProvider中的同名方法。所以ContentProvider很多對外可以訪問的方法,在ContentResolver中均有同名的方法,是一一對應的,如圖:

 

 

Uri

  在Android中,Uri是一種比較常見的資源訪問方式。而對於ContentProvider而言,Uri也是有固定格式的:

    <srandard_prefix>://<authority>/<data_path>/<id>

  • <srandard_prefix>:ContentProvider的srandard_prefix始終是content://。
  • <authority>:ContentProvider的名稱。
  • <data_path>:請求的數據類型。
  • <id>:指定請求的特定數據。

 

ContentProvider

  ContentProvider也是Android應用的四大組件之一,所以也需要在AndroidManifest.xml文件中進行配置。而且某個應用程序通過ContentProvider暴露了自己的數據操作接口,那麼不管該應用程序是否啟動,其他應用程序都可以通過這個接口來操作它的內部數據。

  Android附帶了許多有用的ContentProvider,但是本篇博客不會涉及到這些內容的,以後有時間會再講解。Android附帶的ContentProvider包括:

  • Browser:存儲如浏覽器的信息。
  • CallLog:存儲通話記錄等信息。
  • Contacts:存儲聯系人等信息。
  • MediaStore:存儲媒體文件的信息。
  • Settings:存儲設備的設置和首選項信息。

  在Android中,如果要創建自己的內容提供者的時候,需要擴展抽象類ContentProvider,並重寫其中定義的各種方法。然後在AndroidManifest.xml文件中注冊該ContentProvider即可。

  ContentProvider是內容提供者,實現Android應用之間的數據交互,對於數據操作,無非也就是CRUD而已。下面是ContentProvider必須要實現的幾個方法:

  • onCreate():初始化提供者。
  • query(Uri, String[], String, String[], String):查詢數據,返回一個數據Cursor對象。
  • insert(Uri, ContentValues):插入一條數據。
  • update(Uri, ContentValues, String, String[]):根據條件更新數據。
  • delete(Uri, String, String[]):根據條件刪除數據。
  • getType(Uri) 返回MIME類型對應內容的URI。

  除了onCreate()和getType()方法外,其他的均為CRUD操作,這些方法中,Uri參數為與ContentProvider匹配的請求Uri,剩下的參數可以參見SQLite的CRUD操作,基本一致,SQLite的內容在另外一篇中有講解:http://www.fengfly.com/plus/view-213507-1.html。

  Tips:還有兩個非常有意思的方法,必須要提一下,call()和bulkInsert()方法,使用call,理論上可以在ContentResolver中執行ContentProvider暴露出來的任何方法,而bulkInsert()方法用於插入多條數據。

 

  在ContentProvider的CRUD操作,均會傳遞一個Uri對象,通過這個對象來匹配對應的請求。那麼如何確定一個Uri執行哪項操作呢?需要用到一個UriMatcher對象,這個對象用來幫助內容提供者匹配Uri。它所提供的方法非常簡單,僅有兩個:

  • void addURI(String authority,String path,int code):添加一個Uri匹配項,authority為AndroidManifest.xml中注冊的ContentProvider中的authority屬性;path為一個路徑,可以設置通配符,#表示任意數字,*表示任意字符;code為自定義的一個Uri代碼。
  • int match(Uri uri):匹配傳遞的Uri,返回addURI()傳遞的code參數。

 

  在創建好一個ContentProvider之後,還需要在AndroidManifest.xml文件中對ContentProvider進行配置,使用一個<provider.../>節點,一般只需要設置兩個屬性即可訪問,一些額外的屬性就是為了設置訪問權限而存在的,後面會詳細講解:

  • android:name:provider的響應類。
  • android:authorities:Provider的唯一標識,用於Uri匹配,一般為ContentProvider類的全名。

  下面通過一個示例來講解一下ContentProvider,在這個例子中,需要用到SQLite數據庫來存儲數據,定義了一個StudentDAO類,用於進行對SQLite的CRUD操作,這裡就不提供數據訪問的源碼了,有興趣的朋友可以在下載源碼查看:

  ContentProvider實現:

  1. package com.example.contentproviderdemo;  
  2.  
  3. import com.example.dao.StudentDAO;  
  4. import android.content.ContentProvider;  
  5. import android.content.ContentUris;  
  6. import android.content.ContentValues;  
  7. import android.content.UriMatcher;  
  8. import android.database.Cursor;  
  9. import android.net.Uri;  
  10. import android.os.Bundle;  
  11. import android.util.Log;  
  12.  
  13. public class StudentProvider extends ContentProvider {  
  14.  
  15.     private final String TAG = "main";  
  16.     private StudentDAO studentDao = null;  
  17.     private static final UriMatcher URI_MATCHER = new UriMatcher(  
  18.             UriMatcher.NO_MATCH);  
  19.     private static final int STUDENT = 1;  
  20.     private static final int STUDENTS = 2;  
  21.     static {  
  22.         //添加兩個URI篩選  
  23.         URI_MATCHER.addURI("com.example.contentproviderdemo.StudentProvider",  
  24.                 "student", STUDENTS);  
  25.         //使用通配符#,匹配任意數字  
  26.         URI_MATCHER.addURI("com.example.contentproviderdemo.StudentProvider",  
  27.                 "student/#", STUDENT);          
  28.     }  
  29.  
  30.     public StudentProvider() {  
  31.  
  32.     }      
  33.       
  34.     @Override 
  35.     public boolean onCreate() {  
  36.         // 初始化一個數據持久層  
  37.         studentDao = new StudentDAO(getContext());  
  38.         Log.i(TAG, "---->>onCreate()被調用");  
  39.         return true;  
  40.     }  
  41.  
  42.     @Override 
  43.     public Uri insert(Uri uri, ContentValues values) {  
  44.         Uri resultUri = null;  
  45.         //解析Uri,返回Code  
  46.         int flag = URI_MATCHER.match(uri);  
  47.         if (flag == STUDENTS) {  
  48.             long id = studentDao.insertStudent(values);  
  49.             Log.i(TAG, "---->>插入成功, id="+id);  
  50.             resultUri = ContentUris.withAppendedId(uri, id);  
  51.         }  
  52.         return resultUri;  
  53.     }  
  54.  
  55.     @Override 
  56.     public int delete(Uri uri, String selection, String[] selectionArgs) {  
  57.         int count = -1;  
  58.         try {  
  59.             int flag = URI_MATCHER.match(uri);  
  60.             switch (flag) {  
  61.             case STUDENT:  
  62.                 // delete from student where id=?  
  63.                 //單條數據,使用ContentUris工具類解析出結尾的Id  
  64.                 long id = ContentUris.parseId(uri);  
  65.                 String where_value = "id = ?";  
  66.                 String[] args = { String.valueOf(id) };  
  67.                 count = studentDao.deleteStudent(where_value, args);  
  68.                 break;  
  69.             case STUDENTS:  
  70.                 count = studentDao.deleteStudent(selection, selectionArgs);                  
  71.                 break;  
  72.             }  
  73.         } catch (Exception e) {  
  74.             e.printStackTrace();  
  75.         }  
  76.         Log.i(TAG, "---->>刪除成功,count="+count);  
  77.         return count;  
  78.     }  
  79.  
  80.     @Override 
  81.     public int update(Uri uri, ContentValues values, String selection,  
  82.             String[] selectionArgs) {  
  83.         int count = -1;  
  84.         try {              
  85.             int flag = URI_MATCHER.match(uri);  
  86.             switch (flag) {  
  87.             case STUDENT:  
  88.                 long id = ContentUris.parseId(uri);  
  89.                 String where_value = " id = ?";  
  90.                 String[] args = { String.valueOf(id) };  
  91.                 count = studentDao.updateStudent(values, where_value, args);  
  92.                 break;  
  93.             case STUDENTS:  
  94.                 count = studentDao.updateStudent(values, selection,  
  95.                         selectionArgs);  
  96.                 break;  
  97.             }  
  98.         } catch (Exception e) {  
  99.             e.printStackTrace();  
  100.         }  
  101.         Log.i(TAG, "---->>更新成功,count="+count);  
  102.         return count;  
  103.     }  
  104.  
  105.     @Override 
  106.     public Cursor query(Uri uri, String[] projection, String selection,  
  107.             String[] selectionArgs, String sortOrder) {  
  108.         Cursor cursor = null;  
  109.         try {  
  110.             int flag = URI_MATCHER.match(uri);  
  111.             switch (flag) {  
  112.             case STUDENT:  
  113.                 long id = ContentUris.parseId(uri);  
  114.                 String where_value = " id = ?";  
  115.                 String[] args = { String.valueOf(id) };  
  116.                 cursor = studentDao.queryStudents(where_value, args);  
  117.                 break;  
  118.             case STUDENTS:  
  119.                 cursor = studentDao.queryStudents(selection, selectionArgs);  
  120.                 break;  
  121.             }  
  122.         } catch (Exception e) {  
  123.             e.printStackTrace();  
  124.         }  
  125.         Log.i(TAG, "---->>查詢成功,Count="+cursor.getCount());  
  126.         return cursor;  
  127.     }  
  128.  
  129.  
  130.     @Override 
  131.     public String getType(Uri uri) {  
  132.         int flag = URI_MATCHER.match(uri);  
  133.         String type = null;  
  134.         switch (flag) {  
  135.         case STUDENT:  
  136.             type = "vnd.android.cursor.item/student";  
  137.             Log.i(TAG, "----->>getType return item");  
  138.             break;  
  139.         case STUDENTS:  
  140.             type = "vnd.android.cursor.dir/students";  
  141.             Log.i(TAG, "----->>getType return dir");  
  142.             break;  
  143.         }  
  144.         return type;  
  145.     }  
  146.     @Override 
  147.     public Bundle call(String method, String arg, Bundle extras) {  
  148.         Log.i(TAG, "------>>"+method);  
  149.         Bundle bundle=new Bundle();  
  150.         bundle.putString("returnCall", "call被執行了");  
  151.         return bundle;  
  152.     }  

  在AndroidManifest.xml中<application>節點中增加:

  1. <provider 
  2.     android:name=".StudentProvider"   
  3.     android:authorities="com.example.contentproviderdemo.StudentProvider" > 
  4. </provider> 

ContentResolver

  ContentResolver,內容訪問者。可以通過ContentResolver來操作ContentProvider所暴露處理的接口。一般使用Content.getContentResolver()方法獲取ContentResolver對象。上面已經提到ContentResolver的很多方法與ContentProvider一一對應,所以它也存在insert、query、update、delete等方法。

  下面就通過一個簡單的Demo來演示ContentResolver的使用,這裡就不另外新建一個項目了,在原有項目上使用Android JUnit新建一個測試類,用於測試。在另外一個項目中的操作也是一樣的。關於JUnit,不了解的朋友可以參見另外一篇:http://www.fengfly.com/plus/view-213360-1.html。

  JUnit測試類:

 

  1. package com.example.contentproviderdemo;  
  2.  
  3. import android.content.ContentResolver;  
  4. import android.content.ContentValues;  
  5. import android.database.Cursor;  
  6. import android.net.Uri;  
  7. import android.os.Bundle;  
  8. import android.test.AndroidTestCase;  
  9. import android.util.Log;  
  10.  
  11. public class MyTest extends AndroidTestCase {  
  12.  
  13.     public MyTest() {  
  14.         // TODO Auto-generated constructor stub  
  15.  
  16.     }  
  17.  
  18.     public void insert() {  
  19.         ContentResolver contentResolver = getContext().getContentResolver();  
  20.         Uri uri = Uri  
  21.                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");  
  22.         ContentValues values = new ContentValues();  
  23.         values.put("name", "Demo");  
  24.         values.put("address", "HK");  
  25.         Uri returnuir = contentResolver.insert(uri, values);  
  26.         Log.i("main", "-------------->" + returnuir.getPath());  
  27.     }  
  28.  
  29.     public void delete() {  
  30.         ContentResolver contentResolver = getContext().getContentResolver();  
  31.         // 刪除多行:content://com.example.contentproviderdemo.StudentProvider/student  
  32.         Uri uri = Uri  
  33.                 .parse("content://com.example.contentproviderdemo.StudentProvider/student/2");  
  34.         contentResolver.delete(uri, null, null);  
  35.     }  
  36.  
  37.     public void deletes() {  
  38.         ContentResolver contentResolver = getContext().getContentResolver();  
  39.         Uri uri = Uri  
  40.                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");  
  41.         String where = "address=?";  
  42.         String[] where_args = { "HK" };  
  43.         contentResolver.delete(uri, where, where_args);  
  44.     }  
  45.  
  46.     public void update() {  
  47.         ContentResolver contentResolver = getContext().getContentResolver();  
  48.         Uri uri = Uri  
  49.                 .parse("content://com.example.contentproviderdemo.StudentProvider/student/2");  
  50.         ContentValues values = new ContentValues();  
  51.         values.put("name", "李四");  
  52.         values.put("address", "上海");  
  53.         contentResolver.update(uri, values, null, null);  
  54.     }  
  55.  
  56.     public void updates() {  
  57.         ContentResolver contentResolver = getContext().getContentResolver();  
  58.         Uri uri = Uri  
  59.                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");  
  60.         ContentValues values = new ContentValues();  
  61.         values.put("name", "王五");  
  62.         values.put("address", "深圳");  
  63.         String where = "address=?";  
  64.         String[] where_args = { "beijing" };  
  65.         contentResolver.update(uri, values, where, where_args);  
  66.     }  
  67.  
  68.     public void query() {  
  69.         ContentResolver contentResolver = getContext().getContentResolver();  
  70.         Uri uri = Uri  
  71.                 .parse("content://com.example.contentproviderdemo.StudentProvider/student/2");  
  72.         Cursor cursor = contentResolver.query(uri, null, null, null, null);  
  73.         while (cursor.moveToNext()) {  
  74.             Log.i("main",  
  75.                     "-------------->" 
  76.                             + cursor.getString(cursor.getColumnIndex("name")));  
  77.         }  
  78.     }  
  79.  
  80.     public void querys() {  
  81.         ContentResolver contentResolver = getContext().getContentResolver();  
  82.         Uri uri = Uri  
  83.                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");  
  84.         String where = "address=?";  
  85.         String[] where_args = { "深圳" };  
  86.         Cursor cursor = contentResolver.query(uri, null, where, where_args,  
  87.                 null);  
  88.         while (cursor.moveToNext()) {  
  89.             Log.i("main",  
  90.                     "-------------->" 
  91.                             + cursor.getString(cursor.getColumnIndex("name")));  
  92.         }  
  93.     }  
  94.  
  95.     public void calltest() {  
  96.         ContentResolver contentResolver = getContext().getContentResolver();  
  97.         Uri uri = Uri  
  98.                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");  
  99.         Bundle bundle = contentResolver.call(uri, "method", null, null);  
  100.         String returnCall = bundle.getString("returnCall");  
  101.         Log.i("main", "-------------->" + returnCall);  
  102.     }  
  103.  

 

  效果演示:

  執行insert(),數據庫在創建的時候存在初始數據:

  執行update():

   執行updates():

  執行query()

  執行delete()

getType()中的MIME

  MIME類型就是設定某種擴展名的文件用一種應用程序來打開的方式類型。在ContentProvider中的getType方法,返回的就是一個MIME類型的字符串。如果支持需要使用ContentProvider來訪問數據,就上面這個Demo,getType()完全可以只返回一個Null,並不影響效果,但是覆蓋ContentProvider的getType方法對於用new Intent(String action, Uri uri)方法啟動activity是很重要的,如果它返回的MIME type和activity在<intent filter>中定義的data的MIME type不一致,將造成activity無法啟動。這就涉及到Intent和Intent-filter的內容了,以後有機會再說,這裡不再詳解。

  從官方文檔了解到,getType返回的字符串,如果URI針對的是單條數據,則返回的字符串以vnd.android.cursor.item/開頭;如果是多條數據,則以vnd.adroid.cursor.dir/開頭。

 

訪問權限

  對於ContentProvider暴露出來的數據,應該是存儲在自己應用內存中的數據,對於一些存儲在外部存儲器上的數據,並不能限制訪問權限,使用ContentProvider就沒有意義了。對於ContentProvider而言,有很多權限控制,可以在AndroidManifest.xml文件中對<provider>節點的屬性進行配置,一般使用如下一些屬性設置:

  • android:grantUriPermssions:臨時許可標志。
  • android:permission:Provider讀寫權限。
  • android:readPermission:Provider的讀權限。
  • android:writePermission:Provider的寫權限。
  • android:enabled:標記允許系統啟動Provider。
  • android:exported:標記允許其他應用程序使用這個Provider。
  • android:multiProcess:標記允許系統啟動Provider相同的進程中調用客戶端。

  源碼下載

總結

  以上就講解了內容提供者的簡單使用,關於內容提供者的內容涉及面很多,一篇文章無法一一說明,這裡只是簡單的定義一個內容提供者,並且說明如何操作它。這個文章的示例源碼中,聲明的內容提供者和內容訪問者均在一個項目中,可能不太利於大家理解,但是在另外一個項目中的操作,與JUnit類中的操作一致,所以這裡就沒有另外創建一個項目去說明它,不過源碼中有另外一個小項目,只是實現了Insert的操作,有興趣的朋友對照看一下就會發現其實是一樣的。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved