ContentProvider使用總結

  近日來學習ContentProvider相關的知識,作了一個demo,想和網友分享下。html

  首先說一點相關的知識:android

  一:做用sql

  ContentProvider是不一樣應用程序共享數據的接口,跟共享數據的別的方法相比,ContentProvider更好地提供了數據共享接口的統一性。CP(ContentProvider的簡稱)經過一張或者多張表的形式向外部應用程序提供數據(就像關係型數據庫中看見的表那樣)。數據庫

  二:Content URIS安全

  URI(Uniform Resource Identifier)統一資源標示符,能表示provider中的數據。由三部分組成,分別是scheme,authority,path.它的scheme,Android系統規定爲"content://",authority惟一表示了要訪問的CP名字,而path表示了要訪問的CP中的數據。在使用ContentResolver對象訪問CP時,須要一個Uri參數。構造Uri時可能會用到三個類:服務器

  Uri.Builder類可用來經過String構建Uri網絡

  ContentUris.withAppendedId()可在Uri後面加一個id多線程

  UriMatcher:在CP中這個類可幫你從接受到Uri中選擇出要執行的ACTION架構

  三:MIME typeide

  MIME,多用途互聯網郵件擴展,是一種互聯網標準。它的做用是:服務器會經過MIME值來告訴客戶端所傳送的多媒體數據的類型。CP也是一種C/S架構,因此須要使用到這個值。它的格式爲:type/subtype,好比text/html,表明所傳輸的文本爲html的格式。在Android中MIME可提供兩類值:統一的MIME數據類型和定製的MIME類型字符串。在CP中前者在傳輸文件時會用到,然後者更經常使用,發送結構化的數據,譬如SQLiteDatabase時會使用定製的MIME類型字符串。在android中後者有本身的規定:

  若是返回單行數據,type被設置爲"vnd.android.cursor.item"

  若是返回多行數據,type被設置爲"vnd.android.cursor.dir"

  而subtype部分由CP類本身設置。

  接下來進入正題,說代碼環節,這篇代碼是經過結構化數據來闡述CP的。

  一:客戶端

  在客戶端,是經過一個ContentResolver對象做爲client和CP交互的。ContentResolver對象可調用CP中的同名方法,可提供「增刪改查」的功能。

  Step1:在客戶端的manifest文件申請要訪問的CP的權限(該權限在CP端已向系統註冊,下文會講)

1 <uses-permission android:name="com.example.cpserver.permission" />
View Code

  Step1.在「增刪改查」以前須要判斷將要請求的Uri的有效性,代碼以下:

 1 //判斷所要使用的Provider是否有效
 2     private boolean checkValidProvider(Uri uri)
 3     { 
 4         ContentProviderClient client = getContentResolver().acquireContentProviderClient(uri);
 5         if(client == null)
 6         {
 7             System.out.println("provider is invalid!");
 8             return false;
 9         }
10         else
11         {
12             client.release();
13             return true;
14         }
15     }
View Code

  Step3:"增刪改查"功能,這些方法都是SQL中DDL語言的一個封裝,具體每一個方法的返回值,參數意義不展開講了,不然很長,可留言詢問。

 1 private int insert()
 2     {
 3         if(!checkValidProvider(Contract.CONTENT_URI))
 4             return -1;
 5         ContentValues values = new ContentValues();
 6         values.put(Contract.COLUMN_NAME_1, "小衛的春天");
 7         values.put(Contract.COLUMN_NAME_2, "翟衛華");
 8         values.put(Contract.COLUMN_NAME_3, "100");
 9         Uri uri = getContentResolver().insert(Contract.CONTENT_URI, values);
10         String lastPath = uri.getLastPathSegment();
11         if(TextUtils.isEmpty(lastPath)) 
12         {
13             System.out.println("insert failure!");
14         } 
15         else 
16         {
17             System.out.println("insert success! the id is " + lastPath);
18         }
19         return Integer.parseInt(lastPath);
20     }
21     
22     //刪除全部行
23     private int delete()
24     {
25         if(!checkValidProvider(Contract.CONTENT_URI))
26             return -1;
27         int count = getContentResolver().delete(Contract.CONTENT_URI, null, null);
28         return count;
29     }
30     
31     //將全部行的數據進行一個修改
32     private int update()
33     {
34         if(!checkValidProvider(Contract.CONTENT_URI))
35             return -1;
36         ContentValues values = new ContentValues();
37         values.put(Contract.COLUMN_NAME_1,"小寶的春天");
38         values.put(Contract.COLUMN_NAME_2, "翟小寶");
39         values.put(Contract.COLUMN_NAME_3, "200");
40         int count = getContentResolver().update(Contract.CONTENT_URI, values, null, null);
41         if(count == 0)
42         {
43             System.out.println("update failed!");
44         }
45         return count;
46     }
47     
48     //row:要查找的行號, "-1"表明查找多行
49     private void query(int row)
50     {
51         if(!checkValidProvider(Contract.CONTENT_URI))
52             return;
53         Cursor cursor = null;
54         if(row != -1)
55         {
56             
57         }
58         else
59         {
60             cursor = getContentResolver().query(Contract.CONTENT_URI,
61                     new String[]{Contract.COLUMN_NAME_1,Contract.COLUMN_NAME_2,Contract.COLUMN_NAME_3},null, null, null);
62         }
63         if(cursor == null)
64             System.out.println("query failure!");
65         else
66         {
67             String strDisplay = getDataFromCursor(cursor);    
68             tvDisplay.setText(strDisplay);
69             cursor.close();
70         }
71     }
View Code

  二:CP端

  在CP端要經過繼承ContentProvider來實現。CP是Android的四大組件之一,比較重要,並且系統自己就能提供不少的ContentProvider供開發者使用,好比可經過CP請求道全部的圖片,音頻,視頻等數據。在Android系統中CP默認是可被別的任何應用程序請求到的,若是你不設置權限加以限制的話。因此第一步應該設置一個permission字段,並把它添加到provider標籤裏面去。

 1 <permission 
 2     android:name="com.example.cpserver.permission"
 3     android:label="Example Data"
 4     android:protectionLevel="signature" />
 5 
 6 <provider
 7             android:name="provider.TestProvider"
 8             android:authorities="com.example.cpserver.provider"
 9             android:permission="com.example.cpserver.permission"
10             android:exported="true"
11             android:enabled="true" />
View Code

  Step1:實現一個SQLiteOpenHelper做爲Provider的數據存儲庫

 1 package db;
 2 
 3 import com.example.cpserver.Contract;
 4 
 5 import android.content.Context;
 6 import android.database.sqlite.SQLiteDatabase;
 7 import android.database.sqlite.SQLiteOpenHelper;
 8 /*
 9  * 實現一個SQLiteOpenHelper做爲Provider的數據存儲庫
10  */
11 public final class MainDatabaseHelper extends SQLiteOpenHelper {
12 
13     //建立一張表的SQL語句
14     private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
15     Contract.TABLE_NAME + "(" +                           
16     " _ID INTEGER PRIMARY KEY, " +
17     Contract.COLUMN_NAME_1 + " TEXT," +
18     Contract.COLUMN_NAME_2 + " TEXT," +
19     Contract.COLUMN_NAME_3 + " INTEGER )";
20     
21     public MainDatabaseHelper(Context context) 
22     {
23         super(context, Contract.DB_NAME, null, 1);
24     }
25 
26     /*
27      *當Provider設法打開數據存儲庫,而且數據庫不存在時該方法被調用
28      */
29     @Override
30     public void onCreate(SQLiteDatabase db) {
31         // Creates the main table
32         db.execSQL(SQL_CREATE_MAIN);
33     }
34 
35     @Override
36     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
37         // TODO Auto-generated method stub
38         
39     }
40 }
View Code

  Step2:實現ContentProvider的子類,而且擴展抽象方法,代碼註釋很詳細。

  1 package provider;
  2 
  3 import com.example.cpserver.Contract;
  4 import db.MainDatabaseHelper;
  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.SQLException;
 11 import android.database.sqlite.SQLiteDatabase;
 12 import android.net.Uri;
 13 import android.text.TextUtils;
 14 
 15 /*
 16  * 1.除了onCreate方法,別的方法均可能會被多線程調用,因此這些方法要設計成線程安全的
 17  * 2.在onCreate中避免耗時操做
 18  * 3.雖然如下方法都要被繼承,但沒必要重寫每一個方法,除了getType
 19  */
 20 public class TestProvider extends ContentProvider 
 21 {
 22     //建立一個UriMatcher對象,該對象幫你從接受的URI中選擇出要執行的動做
 23     private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 24     private MainDatabaseHelper mOpenHelper;
 25     private SQLiteDatabase db = null;
 26     
 27     //調用addURI方法添加provider能夠識別的全部URI類型
 28     static
 29     {
 30         sUriMatcher.addURI(Contract.authority, Contract.TABLE_NAME, 1);
 31         sUriMatcher.addURI(Contract.authority, Contract.TABLE_NAME+"/#", 2);
 32     }
 33     /*
 34      * 該方法做用:初始化該Provider
 35      * 注意:直到一個ContentResolver對象訪問時,該方法才被調用
 36      * 這個方法裏不該作耗時的操做,由於這樣可能延緩Provider對別的應用程序的相應
 37      */
 38     @Override
 39     public boolean onCreate() {
 40         mOpenHelper = new MainDatabaseHelper(getContext());
 41         return true;
 42     }
 43 
 44     @Override
 45     public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) 
 46     {
 47         int type = sUriMatcher.match(uri);
 48         switch (type)
 49         {
 50         //多行請求的URI
 51         case 1:
 52             if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
 53             break;
 54         case 2:
 55             //單行請求的URI
 56             selection = selection + "_ID = " + uri.getLastPathSegment();
 57             break;
 58         default:
 59            //若是URI不匹配,作一些錯誤提示,返回null或者拋出異常
 60             throw new IllegalArgumentException("Unknown URI " + uri);
 61         }
 62         if(db == null)
 63             db = mOpenHelper.getWritableDatabase();
 64         Cursor cursor = db.query(Contract.TABLE_NAME, projection, selection, selectionArgs, null, null,sortOrder);
 65         return cursor;
 66     }
 67 
 68     //返回content URI相應的MIME 類型
 69     @Override
 70     public String getType(Uri uri) 
 71     {
 72         int type = sUriMatcher.match(uri);
 73         switch (type) 
 74         {
 75         case 1:
 76             return Contract.CONTENT_TYPE;
 77         case 2:
 78             return Contract.CONTENT_ITEM_TYPE;
 79         default:
 80             throw new IllegalArgumentException("Unknown URI " + uri);
 81         }
 82     }
 83 
 84     @Override
 85     public Uri insert(Uri uri, ContentValues initialValues) 
 86     {
 87         int type = sUriMatcher.match(uri);
 88         if(type != 1)
 89             throw new IllegalArgumentException("Unknown URI " + uri);
 90         
 91         //建立一個可寫數據庫,將調用MainDatabaseHelper的onCreate方法,若是數據庫還不存在的話
 92         if(db == null)
 93             db = mOpenHelper.getWritableDatabase();
 94         
 95         //確保全部的域都被設置
 96         ContentValues values;
 97         if (initialValues != null) 
 98             values = new ContentValues(initialValues);
 99         else 
100             values = new ContentValues();
101         if (values.containsKey(Contract.COLUMN_NAME_1) == false) {
102             values.put(Contract.COLUMN_NAME_1, "");
103         }
104         if (values.containsKey(Contract.COLUMN_NAME_2) == false) {
105             values.put(Contract.COLUMN_NAME_2, "");
106         }
107         if (values.containsKey(Contract.COLUMN_NAME_3) == false) {
108             values.put(Contract.COLUMN_NAME_3, "");
109         }
110         
111         long rowId = db.insert(Contract.TABLE_NAME,null, values);
112         if(rowId > 0)
113         {
114             Uri noteUri = ContentUris.withAppendedId(Contract.CONTENT_URI, rowId);
115             getContext().getContentResolver().notifyChange(noteUri, null);
116             return noteUri;
117         }
118         throw new SQLException("Failed to insert row into " + uri);
119     }
120 
121     @Override
122     public int delete(Uri uri, String selection, String[] selectionArgs) 
123     {
124         if(db == null)
125             db = mOpenHelper.getWritableDatabase();
126         int type = sUriMatcher.match(uri);
127         int count;
128         switch (type)
129         {
130             case 1:
131                 count = db.delete(Contract.TABLE_NAME, selection, selectionArgs);
132                 break;
133             case 2:
134                 String noteId = uri.getLastPathSegment();
135                 count = db.delete(Contract.TABLE_NAME, "_ID" + "=" + noteId +
136                         (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
137                 break;
138             default:
139                 throw new IllegalArgumentException("Unknown URI " + uri);
140         }
141         getContext().getContentResolver().notifyChange(uri, null);
142         return count;
143     }
144 
145     @Override
146     public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) 
147     {
148         if(db == null)
149             db = mOpenHelper.getWritableDatabase();
150         int type = sUriMatcher.match(uri);
151         int count;
152         switch (type)
153         {
154             case 1:
155                 count = db.update(Contract.TABLE_NAME, values, selection, selectionArgs);
156                 break;
157             case 2:
158                 String noteId = uri.getLastPathSegment();
159                 count = db.update(Contract.TABLE_NAME, values,"_ID" + "=" + noteId +
160                         (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
161                 break;
162             default:
163                 throw new IllegalArgumentException("Unknown URI " + uri);
164         }
165         getContext().getContentResolver().notifyChange(uri, null);
166         return count;
167     }
168 
169     //若是Provider提供file數據,要用這個方法返回MIME類型
170     @Override
171     public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
172         // TODO Auto-generated method stub
173         return super.getStreamTypes(uri, mimeTypeFilter);
174     }
175 
176 }
View Code

 

  我認爲CP之因此不那麼容易理解,是由於它涉及到的東西較多,還涉及到計算機網絡中的的URI,MIME等概念,確實不是那麼容易,做爲碼農的咱們只能去啃了,無別的辦法。總結一下,CP機制中主要涉及到這麼幾點知識:permission權限,uri,MIME,使用UriMatcher來匹配uri的類型,還要掌握一些基本的SQL語言等。關於使用CP來實現File分享,又是很長的一個篇幅,等得空再研究吧。個人項目用百度雲作個下載連接吧,有需求的朋友可拿去用。歡迎留言交流。

http://pan.baidu.com/s/1gdkPia3

 

  Author:Andy Zhai

  2014-02-11    20:44:00

相關文章
相關標籤/搜索