android 中MIME類型的理解

初始MIME類型,是在學習ContentProvider的時候。

      當在創建自己的ContentProvider的時,需要從抽象類ContentProvider中派生出自己的子類,並實現其中5個抽象方法:

       至於前四個方法,不是本文想要討論的重點,就不做冗餘的闡述了;有意思的是這個方法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平臺可以識別。

發佈了9 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章