初始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);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "DROP TABLE IF EXISTS " + Shopping.TABLE_NAME;
db.execSQL(sql);
onCreate(db);
}
}
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
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);
}
@Override
public boolean onCreate() {
// 創建數據庫
myDbHelper = new MyDbHelper(getContext(), Shopping.DATABASE_NAME, null, Shopping.DATABASE_VERSION);
return true;
}
@Override
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);
break;
default:
throw new IllegalArgumentException();
}
// 通知數據發生改變
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = myDbHelper.getWritableDatabase();
long row = 0;
if (mUriMatcher.match(uri) != Shopping.ITEM) {
throw new IllegalArgumentException();
}
row = db.insert(Shopping.TABLE_NAME, Shopping.FIELD_ID, values);
if (row > 0) {
Uri noteUri = ContentUris.withAppendedId(Shopping.URI, row);
getContext().getContentResolver().notifyChange(uri, null);
return noteUri;
}
return null;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = myDbHelper.getReadableDatabase();
Cursor cursor = null;
switch (mUriMatcher.match(uri)) {
case Shopping.ITEM:
cursor = db.query(Shopping.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
case Shopping.ITEM_ID:
String id = uri.getPathSegments().get(1);
cursor = db.query(Shopping.TABLE_NAME, projection, Shopping.FIELD_ID + "=" + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""),
selectionArgs, null, null, sortOrder);
break;
default:
throw new IllegalArgumentException();
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = myDbHelper.getWritableDatabase();
int count = 0;
switch (mUriMatcher.match(uri)) {
case Shopping.ITEM:
count = db.update(Shopping.TABLE_NAME, values, selection, selectionArgs);
break;
case Shopping.ITEM_ID:
String id = uri.getPathSegments().get(1);
count = db.update(Shopping.TABLE_NAME, values, Shopping.FIELD_ID + "=" + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""),
selectionArgs);
break;
default:
throw new IllegalArgumentException();
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public String getType(Uri uri) { // 進行Uri匹配完成不同的處理工作
switch (mUriMatcher.match(uri)) {
case Shopping.ITEM:
return Shopping.CONTENT_ITEM;
case Shopping.ITEM_ID:
return Shopping.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException();
}
}
}
在上面的例子中,首先有一個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類使我們的重頭戲,首先我們來看這一段代碼:
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);
}
UriMatcher表示一個Uri的匹配器,它會對我們請求的Uri進行匹配,而匹配的格式就是這裏我們通過addURI()方法添加格式。
接下來,首先執行的就是getType(Uri)方法,下面來看該方法體中的代碼:
switch (mUriMatcher.match(uri)) {
case Shopping.ITEM:
return Shopping.CONTENT_ITEM;
case Shopping.ITEM_ID:
return Shopping.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException();
}
當請求過來的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();
}
這裏,我們把當前上下文的包名返回了。這樣處理的結果是怎樣的呢?
簡單的說,系統不能夠識別它了,也就不會做任何處理。仍然以query()方法來說,當執行完方法體(這裏需要注意一下:在這種情況下,即使我們沒有通過返回MIME類型字符串來進行驗證處理,但是在query()方法中再次對Uri進行了匹配並根據不同的Uri類型進行了不同的操作)返回Cursor對象的時候,這時候系統不能肯定返回的Cursor對象是否合法,因此需要對其進行驗證,這樣對系統資源算是一個浪費了吧。所以,我們最好還是顯示的返回一個MIME類型吧,當然要寫正確了,讓我們android平臺可以識別。