Android Studio——Android中对MIME类型的理解

原文链接:http://blog.csdn.net/h3g2010/article/details/6093366


小弟也是android的初学者,该文完全代表了个人的理解,错误之处望各位凛然指出,必当虚心纠正;若能对懵懂中人起到些许提示作用,小弟深感欣慰!

正文:

初识MIME类型,是在学习ContentProvider的时候。

当在创建自己的ContentProvider的时候,需要从抽象类ContentProvider中派生出自己的子类,并实现其中5个抽象方法:

[java]  view plain copy
  1. query(Uri, String[], String, String[], String) which returns data to the caller  
  2. insert(Uri, ContentValues) which inserts new data into the content provider  
  3. update(Uri, ContentValues, String, String[]) which updates existing data in the content provider  
  4. delete(Uri, String, String[]) which deletes data from the content provider  
  5. 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类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

看完百度百科的解释,相信大家和我一样,仍然不解。结合一个例子,与老师交流之后,我的理解是这样的:

在ContentProvidergetType(Uri)方法中,可以显示的返回一个MIME类型,该方法返回一个字符串,可以是任意的字符串,当我们显示的返回该MIME类型的时候,相当于通过该方法的验证,Provider可以识别自身其他方法返回的Cursor的内容,不需要在进行更多的验证;如果返回其他的字符串(android能够识别的MIME类型,例如直接返回当前的包名),则Provider在执行其他方法后,返回Cursor类型的时候,需要再次进行验证。

还是云里雾里的?下面来看一个使用了MIME类型的自定义ContentProvider的例子:

[java]  view plain copy
  1. import android.net.Uri;  
  2. public class Shopping {  
  3.    
  4. // 定义数据库的名字  
  5. public static final String DATABASE_NAME = "shopping_db";  
  6. // 定义数据库的版本  
  7. public static final int DATABASE_VERSION = 1;  
  8. // 表的名字  
  9. public static final String TABLE_NAME = "t_shopping";  
  10. // 定义数据库的字段  
  11. public static final String FIELD_ID = "_id";  
  12. public static final String FIELE_NAME = "product_name";  
  13. // 定义访问的类型  
  14. public static final int ITEM = 1;  
  15. public static final int ITEM_ID = 2;  
  16. // 定义MIME类型,访问单个记录  
  17. public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.stone.shopping";  
  18. // 访问数据集  
  19. public static final String CONTENT_ITEM = "vnd.android.cursor.dir/vnd.stone.shopping";  
  20. // 定义访问ContentProvider权限  
  21. public static final String AUTHORITY = "com.stone.shopping";  
  22. // 定义URI  
  23. public static final Uri URI = Uri.parse("content://" + AUTHORITY + "/item");  
  24. }   
  25. import android.content.Context;  
  26. import android.database.sqlite.SQLiteDatabase;  
  27. import android.database.sqlite.SQLiteOpenHelper;  
  28. import android.database.sqlite.SQLiteDatabase.CursorFactory;  
  29. public class MyDbHelper extends SQLiteOpenHelper {  
  30. public MyDbHelper(Context context, String name, CursorFactory factory, int version) {  
  31. super(context, name, factory, version);  
  32. }  
  33. @Override  
  34. public void onCreate(SQLiteDatabase db) {  
  35. String sql = "CREATE TABLE " + Shopping.TABLE_NAME + " ( " +   
  36. Shopping.FIELD_ID + " INTEGER primary key autoincrement, " + " " + Shopping.FIELE_NAME + " TEXT)";  
  37. db.execSQL(sql);  
  38. }  
  39. @Override  
  40. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
  41. String sql = "DROP TABLE IF EXISTS " + Shopping.TABLE_NAME;  
  42. db.execSQL(sql);  
  43. onCreate(db);  
  44. }  
  45. }    
  46. import android.content.ContentProvider;  
  47. import android.content.ContentUris;  
  48. import android.content.ContentValues;  
  49. import android.content.UriMatcher;  
  50. import android.database.Cursor;  
  51. import android.database.sqlite.SQLiteDatabase;  
  52. import android.net.Uri;  
  53. import android.text.TextUtils;  
  54. public class MyProvider extends ContentProvider {  
  55. private MyDbHelper myDbHelper;  
  56. private static final UriMatcher mUriMatcher; // 进行匹配的Uri的设定  
  57. static {  
  58. mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);  
  59. mUriMatcher.addURI(Shopping.AUTHORITY, "item", Shopping.ITEM);  
  60. mUriMatcher.addURI(Shopping.AUTHORITY, "item/#", Shopping.ITEM_ID);  
  61. }  
  62. @Override  
  63. public boolean onCreate() {  
  64. // 创建数据库  
  65. myDbHelper = new MyDbHelper(getContext(), Shopping.DATABASE_NAME, null, Shopping.DATABASE_VERSION);  
  66. return true;  
  67. }  
  68. @Override  
  69. public int delete(Uri uri, String selection, String[] selectionArgs) {  
  70. SQLiteDatabase db = myDbHelper.getWritableDatabase();  
  71. int count = 0;  
  72. switch (mUriMatcher.match(uri)) {  
  73. case Shopping.ITEM:  
  74. ount = db.delete(Shopping.TABLE_NAME, selection, selectionArgs);  
  75. break;  
  76. case Shopping.ITEM_ID:  
  77. // 通过Uri获取Id,根据主键进行删除  
  78. String id = uri.getPathSegments().get(1);  
  79. System.out.println(String.valueOf(uri.getPathSegments().size()));  
  80. count = db.delete(Shopping.TABLE_NAME,Shopping.FIELD_ID + "=" + id, selectionArgs);  
  81. break;  
  82. default:  
  83. throw new IllegalArgumentException();  
  84. }  
  85. // 通知数据发生改变  
  86. getContext().getContentResolver().notifyChange(uri, null);  
  87. return count;  
  88. }  
  89. @Override  
  90. public Uri insert(Uri uri, ContentValues values) {  
  91. SQLiteDatabase db = myDbHelper.getWritableDatabase();  
  92. long row = 0;  
  93. if (mUriMatcher.match(uri) != Shopping.ITEM) {  
  94. throw new IllegalArgumentException();  
  95. }  
  96. row = db.insert(Shopping.TABLE_NAME, Shopping.FIELD_ID, values);  
  97. if (row > 0) {  
  98. Uri noteUri = ContentUris.withAppendedId(Shopping.URI, row);  
  99. getContext().getContentResolver().notifyChange(uri, null);  
  100. return noteUri;  
  101. }  
  102. return null;  
  103. }  
  104. @Override  
  105. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {  
  106. SQLiteDatabase db = myDbHelper.getReadableDatabase();  
  107. Cursor cursor = null;  
  108. switch (mUriMatcher.match(uri)) {  
  109. case Shopping.ITEM:  
  110. cursor = db.query(Shopping.TABLE_NAME, projection, selection, selectionArgs, nullnull, sortOrder);  
  111. break;  
  112. case Shopping.ITEM_ID:  
  113. String id = uri.getPathSegments().get(1);  
  114. cursor = db.query(Shopping.TABLE_NAME, projection, Shopping.FIELD_ID + "=" + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""),  
  115. selectionArgs, nullnull, sortOrder);  
  116. break;  
  117. default:  
  118. throw new IllegalArgumentException();  
  119. }  
  120. cursor.setNotificationUri(getContext().getContentResolver(), uri);  
  121. return cursor;  
  122. }  
  123. @Override  
  124. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {  
  125. SQLiteDatabase db = myDbHelper.getWritableDatabase();  
  126. int count = 0;  
  127. switch (mUriMatcher.match(uri)) {  
  128. case Shopping.ITEM:  
  129. count = db.update(Shopping.TABLE_NAME, values, selection, selectionArgs);  
  130. break;  
  131. case Shopping.ITEM_ID:  
  132. String id = uri.getPathSegments().get(1);  
  133. count = db.update(Shopping.TABLE_NAME, values, Shopping.FIELD_ID + "=" + id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""),  
  134. selectionArgs);  
  135. break;  
  136. default:  
  137. throw new IllegalArgumentException();  
  138. }  
  139. getContext().getContentResolver().notifyChange(uri, null);  
  140. return count;  
  141. }  
  142. @Override  
  143. public String getType(Uri uri) { // 进行Uri匹配完成不同的处理工作  
  144. switch (mUriMatcher.match(uri)) {  
  145. case Shopping.ITEM:  
  146. return Shopping.CONTENT_ITEM;  
  147. case Shopping.ITEM_ID:  
  148. return Shopping.CONTENT_ITEM_TYPE;  
  149. default:  
  150. throw new IllegalArgumentException();  
  151. }  
  152. }  
  153. }  

在上面的例子中,首先有一个Shopping类,定义了一系列的常量。包括访问的数据库的相关信息和URI的定义,其中最重要的就是下面的两句,MIME类型的定义:

[java]  view plain copy
  1. public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.stone.shopping";  
  2. public static final String CONTENT_ITEM = "vnd.android.cursor.dir/vnd.stone.shopping";   
其次是一个MyDbHelper类,继承自SQLiteOpenHelper类,用于一些数据库相关操作,这里就不赘述了。

最后的MyProvider类使我们的重头戏,首先我们来看这一段代码:

[java]  view plain copy
  1. private static final UriMatcher mUriMatcher; // 进行匹配的Uri的设定  
  2. static {  
  3.    mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);  
  4.    mUriMatcher.addURI(Shopping.AUTHORITY, "item", Shopping.ITEM);  
  5.    mUriMatcher.addURI(Shopping.AUTHORITY, "item/#", Shopping.ITEM_ID);  
  6. }  
UriMatcher 表示一个 Uri 的匹配器,它会对我们请求的 Uri 进行匹配,而匹配的格式就是这里我们通过 addURI() 方法添加格式。

接下来,首先执行的就是getType(Uri)方法,下面来看该方法体中的代码:

[java]  view plain copy
  1. switch (mUriMatcher.match(uri)) {  
  2. case Shopping.ITEM:  
  3. return Shopping.CONTENT_ITEM;  
  4. case Shopping.ITEM_ID:  
  5. return Shopping.CONTENT_ITEM_TYPE;  
  6. default:  
  7. throw new IllegalArgumentException();  
  8. }   
当请求过来的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()自然也可以随便返回一个系统不能识别的字符串啦?没错,有些时候我们确实也这样处理,比如说可以这样写:

[java]  view plain copy
  1. public String getType(Uri uri) {  
  2. return getContext().getPackageName();   
  3. }  
这里,我们把当前上下文的包名返回了。这样处理的结果是怎样的呢?

简单的说,系统不能够识别它了,也就不会做任何处理。仍然以query()方法来说,当执行完方法体(这里需要注意一下:在这种情况下,即使我们没有通过返回MIME类型字符串来进行验证处理,但是在query()方法中再次对Uri进行了匹配并根据不同的Uri类型进行了不同的操作)返回Cursor对象的时候,这时候系统不能肯定返回的Cursor对象是否合法,因此需要对其进行验证,这样对系统资源算是一个浪费了吧。所以,我们最好还是显示的返回一个MIME类型吧,当然要写正确了,让我们android平台可以识别。


你可能感兴趣的:(Android Studio——Android中对MIME类型的理解)