初始MIME類型,是在學習ContentProvider的時候。
當在建立自己的ContentProvider的時,需要從抽象類ContentProvider中派生出自己的子類,并實作其中5個抽象方法:
- query(Uri, String[], String, String[], String) which returns data to the caller
- insert(Uri, ContentValues) which inserts new data into the content provider
- update(Uri, ContentValues, String, String[]) which updates existing data in the content provider
- delete(Uri, String, String[]) which deletes data from the content provider
- getType(Uri) which returns the MIME type of data in the content provider
至于前四個方法,不是本文想要讨論的重點,就不做備援的闡述了;有意思的是這個方法getType(Uri),根據幫助文檔的解釋,它傳回一個MIME類型。
首先,先百度了一下MIME類型,根據百度百科的解釋:MIME:全稱Multipurpose Internet Mail Extensions,多功能Internet 郵件擴充服務。它是一種多用途網際郵件擴充協定,在1992年最早應用于電子郵件系統,但後來也應用到浏覽器。MIME類型就是設定某種擴充名的檔案用一種應用程式來打開的方式類型,當該擴充名檔案被通路的時候,浏覽器會自動使用指定應用程式來打開。多用于指定一些用戶端自定義的檔案名,以及一些媒體檔案打開方式。
看完百度百科的解釋,相信大家和我一樣,仍然不解。結合一個例子,與老師交流之後,我的了解是這樣的:
在ContentProvider的getType(Uri)方法中,可以顯示的傳回一個MIME類型,該方法傳回一個字元串,可以是任意的字元串,當我們顯示的傳回該MIME類型的時候,相當于通過該方法的驗證,Provider可以識别自身其他方法傳回的Cursor的内容,不需要在進行更多的驗證;如果傳回其他的字元串(非android能夠識别的MIME類型,例如直接傳回目前的包名),則Provider在執行其他方法後,傳回Cursor類型的時候,需要再次進行驗證。
還是雲裡霧裡的?下面來看一個使用了MIME類型的自定義ContentProvider的例子:
import android.net.Uri;
public class Shopping {
// 定義資料庫的名字
public static final String DATABASE_NAME = "shopping_db";
// 定義資料庫的版本
public static final int DATABASE_VERSION = 1;
// 表的名字
public static final String TABLE_NAME = "t_shopping";
// 定義資料庫的字段
public static final String FIELD_ID = "_id";
public static final String FIELE_NAME = "product_name";
// 定義通路的類型
public static final int ITEM = 1;
public static final int ITEM_ID = 2;
// 定義MIME類型,通路單個記錄
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.stone.shopping";
// 通路資料集
public static final String CONTENT_ITEM = "vnd.android.cursor.dir/vnd.stone.shopping";
// 定義通路ContentProvider權限
public static final String AUTHORITY = "com.stone.shopping";
// 定義URI
public static final Uri URI = Uri.parse("content://" + AUTHORITY + "/item");
}
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
public class MyDbHelper extends SQLiteOpenHelper {
public MyDbHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "CREATE TABLE " + Shopping.TABLE_NAME + " ( " +
Shopping.FIELD_ID + " INTEGER primary key autoincrement, " + " " + Shopping.FIELE_NAME + " TEXT)";
db.execSQL(sql);
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "DROP TABLE IF EXISTS " + Shopping.TABLE_NAME;
onCreate(db);
}
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.text.TextUtils;
public class MyProvider extends ContentProvider {
private MyDbHelper myDbHelper;
private static final UriMatcher mUriMatcher; // 進行比對的Uri的設定
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(Shopping.AUTHORITY, "item", Shopping.ITEM);
mUriMatcher.addURI(Shopping.AUTHORITY, "item/#", Shopping.ITEM_ID);
public boolean onCreate() {
// 建立資料庫
myDbHelper = new MyDbHelper(getContext(), Shopping.DATABASE_NAME, null, Shopping.DATABASE_VERSION);
return true;
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = myDbHelper.getWritableDatabase();
int count = 0;
switch (mUriMatcher.match(uri)) {
case Shopping.ITEM:
ount = db.delete(Shopping.TABLE_NAME, selection, selectionArgs);
break;
case Shopping.ITEM_ID:
// 通過Uri擷取Id,根據主鍵進行删除
String id = uri.getPathSegments().get(1);
System.out.println(String.valueOf(uri.getPathSegments().size()));
count = db.delete(Shopping.TABLE_NAME,Shopping.FIELD_ID + "=" + id, selectionArgs);
default:
throw new IllegalArgumentException();
// 通知資料發生改變
getContext().getContentResolver().notifyChange(uri, null);
return count;
public Uri insert(Uri uri, ContentValues values) {
long row = 0;
if (mUriMatcher.match(uri) != Shopping.ITEM) {
row = db.insert(Shopping.TABLE_NAME, Shopping.FIELD_ID, values);
if (row > 0) {
Uri noteUri = ContentUris.withAppendedId(Shopping.URI, row);
return noteUri;
return null;
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = myDbHelper.getReadableDatabase();
Cursor cursor = null;
cursor = db.query(Shopping.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
cursor = db.query(Shopping.TABLE_NAME, projection, Shopping.FIELD_ID + "=" + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""),
selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
count = db.update(Shopping.TABLE_NAME, values, selection, selectionArgs);
count = db.update(Shopping.TABLE_NAME, values, Shopping.FIELD_ID + "=" + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""),
selectionArgs);
public String getType(Uri uri) { // 進行Uri比對完成不同的處理工作
return Shopping.CONTENT_ITEM;
return Shopping.CONTENT_ITEM_TYPE;
在上面的例子中,首先有一個Shopping類,定義了一系列的常量。包括通路的資料庫的相關資訊和URI的定義,其中最重要的就是下面的兩句,MIME類型的定義:
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.stone.shopping";
public static final String CONTENT_ITEM = "vnd.android.cursor.dir/vnd.stone.shopping";
其次是一個MyDbHelper類,繼承自SQLiteOpenHelper類,用于一些資料庫相關操作,這裡就不贅述了。
最後的MyProvider類使我們的重頭戲,首先我們來看這一段代碼:
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(Shopping.AUTHORITY, "item", Shopping.ITEM);
mUriMatcher.addURI(Shopping.AUTHORITY, "item/#", Shopping.ITEM_ID);
UriMatcher表示一個Uri的比對器,它會對我們請求的Uri進行比對,而比對的格式就是這裡我們通過addURI()方法添加格式。
接下來,首先執行的就是getType(Uri)方法,下面來看該方法體中的代碼:
}
當請求過來的Uri通過mUriMatcher.match(uri)方法進行比對,根據不同的比對值來傳回不同的MIME類型。下面我們來結合query(Uri, String[], String, String[], String)這個方法來解釋一下:
在該方法中,傳回一個Cursor遊标對象。而Cursor中是單條的記錄還是一個集合,需要和在getType()方法中傳回的類型保持一緻。當傳回的MIME類型是Shopping.CONTENT_ITEM時,Cursor應該是一個集合;當傳回的MIME類型是Shopping.CONTENT_ITEM_TYPE時,Cursor應該是單條記錄。
由于在getType()方法裡面,我們顯示的傳回了android平台可以識别的MIME類型,是以在執行query()方法傳回Cursor對象的時候,系統将不需要再進行驗證,進而可以說是節省了系統開銷。
話已至此,那麼何謂android平台可以識别的MIME類型呢?下面來分析一下MIME類型的結構:
其實,MIME類型其實就是一個字元串,中間有一個 “/” 來隔開,“/”前面的部分是系統識别的部分,就相當于我們定義一個變量時的變量資料類型,通過這個“資料類型”,系統能夠知道我們所要表示的是個什麼東西。至于 “/” 後面的部分就是我們自已來随便定義的“變量名”了。
那麼,既然MIME類型就是一個字元串,那麼我們的getType()自然也可以随便傳回一個系統不能識别的字元串啦?沒錯,有些時候我們确實也這樣處理,比如說可以這樣寫:
public String getType(Uri uri) {
return getContext().getPackageName();
這裡,我們把目前上下文的包名傳回了。這樣處理的結果是怎樣的呢?