android之内容提供者解析

时间:2024-12-13 13:05:44

android之内容提供者解析

该系统有两个应用,比较繁琐。但是内容提供者是android里非常非常重要的一个内容,我们得好好学习下哦。先看效果图,我们提供了四个按钮,点击按钮便会进行相应的操作。

我们先看内容提供者所在的应用,代码结构:

android之内容提供者解析

activity代码:

  1. package cn.com.contentProvider;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.widget.TextView;
  5. public class ContentProviderAcitivity extends Activity {
  6. @Override
  7. public void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.main);
  10. }
  11. }

MyContentProvider.java代码

  1. package cn.com.contentProvider;
  2. import android.content.ContentProvider;
  3. import android.content.ContentUris;
  4. import android.content.ContentValues;
  5. import android.content.Context;
  6. import android.content.UriMatcher;
  7. import android.database.Cursor;
  8. import android.database.sqlite.SQLiteDatabase;
  9. import android.database.sqlite.SQLiteOpenHelper;
  10. import android.database.sqlite.SQLiteDatabase.CursorFactory;
  11. import android.net.Uri;
  12. /**
  13. *
  14. * @author chenzheng_java
  15. * @description 自定义的内容提供者.
  16. *  总结下访问内容提供者的主要步骤:
  17. * 第一:我们要有一个uri,这就相当于我们的网址,我们有了网址才能去访问具体的网站
  18. * 第二:我们去系统中寻找该uri中的authority(可以理解为主机地址),
  19. *      只要我们的内容提供者在manifest.xml文件中注册了,那么系统中就一定存在。
  20. * 第三:通过内容提供者内部的uriMatcher对请求进行验证(你找到我了,还不行,我还得看看你有没有权限访问我呢)。
  21. * 第四:验证通过后,就可以调用内容提供者的增删查改方法进行操作了
  22. */
  23. public class MyContentProvider extends ContentProvider {
  24. // 自己实现的数据库操作帮助类
  25. private MyOpenHelper myOpenHelper;
  26. // 数据库相关类
  27. private SQLiteDatabase sqLiteDatabase;
  28. // uri匹配相关
  29. private static UriMatcher uriMatcher;
  30. // 主机名称(这一部分是可以随便取得)
  31. private static final String authority = "cn.com.chenzheng_java.hello";
  32. // 注册该内容提供者匹配的uri
  33. static {
  34. uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  35. /*
  36. * path_chenzheng部分的字符串是随便取得,1代表着如果请求的uri与当前加入
  37. * 的匹配uri正好吻合,uriMathcher.match()方法返回的值.#代表任意数字,*代表任意字符串
  38. */
  39. uriMatcher.addURI(authority, "path_chenzheng", 1);// 代表当前表中的所有的记录
  40. uriMatcher.addURI(authority, "path_chenzheng/#", 2);// 代表当前表中的某条特定的记录,记录id便是#处得数字
  41. }
  42. // 数据表中的列名映射
  43. private static final String _id = "id";
  44. private static final String name = "name";
  45. private static final String age = "age";
  46. private static final String isMan = "isMan";
  47. /**
  48. * @description 当内容提供者第一次创建时执行
  49. */
  50. @Override
  51. public boolean onCreate() {
  52. try {
  53. myOpenHelper = new MyOpenHelper(getContext(), DB_Name, null,
  54. Version_1);
  55. } catch (Exception e) {
  56. return false;
  57. }
  58. return true;
  59. }
  60. /**
  61. * @description 对数据库进行删除操作的时候执行
  62. *              android.content.ContentUri为我们解析uri相关的内容提供了快捷方便的途径
  63. */
  64. @Override
  65. public int delete(Uri uri, String selection, String[] selectionArgs) {
  66. int number = 0;
  67. sqLiteDatabase = myOpenHelper.getWritableDatabase();
  68. int code = uriMatcher.match(uri);
  69. switch (code) {
  70. case 1:
  71. number = sqLiteDatabase
  72. .delete(Table_Name, selection, selectionArgs);
  73. break;
  74. case 2:
  75. long id = ContentUris.parseId(uri);
  76. /*
  77. * 拼接where子句用三目运算符是不是特烦人啊? 实际上,我们这里可以用些技巧的.
  78. * if(selection==null||"".equals(selection.trim())) selection =
  79. * " 1=1 and "; selection+=_id+"="+id;
  80. * 拼接where子句中最麻烦的就是and的问题,这里我们通过添加一个1=1这样的恒等式便将问题解决了
  81. */
  82. selection = (selection == null || "".equals(selection.trim())) ? _id
  83. + "=" + id
  84. : selection + " and " + _id + "=" + id;
  85. number = sqLiteDatabase
  86. .delete(Table_Name, selection, selectionArgs);
  87. break;
  88. default:
  89. throw new IllegalArgumentException("异常参数");
  90. }
  91. return number;
  92. }
  93. /**
  94. *@description 获取当前内容提供者的MIME类型 集合类型必须添加前缀vnd.android.cursor.dir/(该部分随意)
  95. *              单条记录类型添加前缀vnd,android.cursor.item/(该部分随意)
  96. *              定义了该方法之后,系统会在第一次请求时进行验证,验证通过则执行crub方法时不再重复进行验证,
  97. *              否则如果没有定义该方法或者验证失败,crub方法执行的时候系统会默认的为其添加类型验证代码。
  98. */
  99. @Override
  100. public String getType(Uri uri) {
  101. int code = uriMatcher.match(uri);
  102. switch (code) {
  103. case 1:
  104. return "vnd.android.cursor.dir/chenzheng_java";
  105. case 2:
  106. return "vnd.android.cursor.item/chenzheng_java";
  107. default:
  108. throw new IllegalArgumentException("异常参数");
  109. }
  110. }
  111. /**
  112. * @description 对数据表进行insert时执行该方法
  113. */
  114. @Override
  115. public Uri insert(Uri uri, ContentValues values) {
  116. sqLiteDatabase = myOpenHelper.getWritableDatabase();
  117. int code = uriMatcher.match(uri);
  118. switch (code) {
  119. case 1:
  120. sqLiteDatabase.insert(Table_Name, name, values);
  121. break;
  122. case 2:
  123. long id = sqLiteDatabase.insert(Table_Name, name, values);
  124. // withAppendId将id添加到uri的最后
  125. ContentUris.withAppendedId(uri, id);
  126. break;
  127. default:
  128. throw new IllegalArgumentException("异常参数");
  129. }
  130. return uri;
  131. }
  132. /**
  133. * 当执行查询时调用该方法
  134. */
  135. @Override
  136. public Cursor query(Uri uri, String[] projection, String selection,
  137. String[] selectionArgs, String sortOrder) {
  138. Cursor cursor = null;
  139. sqLiteDatabase = myOpenHelper.getReadableDatabase();
  140. int code = uriMatcher.match(uri);
  141. switch (code) {
  142. case 1:
  143. cursor = sqLiteDatabase.query(Table_Name, projection, selection,
  144. selectionArgs, null, null, sortOrder);
  145. break;
  146. case 2:
  147. // 从uri中解析出ID
  148. long id = ContentUris.parseId(uri);
  149. selection = (selection == null || "".equals(selection.trim())) ? _id
  150. + "=" + id
  151. : selection + " and " + _id + "=" + id;
  152. cursor = sqLiteDatabase.query(Table_Name, projection, selection,
  153. selectionArgs, null, null, sortOrder);
  154. break;
  155. default:
  156. throw new IllegalArgumentException("参数错误");
  157. }
  158. return cursor;
  159. }
  160. /**
  161. * 当执行更新操作的时候执行该方法
  162. */
  163. @Override
  164. public int update(Uri uri, ContentValues values, String selection,
  165. String[] selectionArgs) {
  166. int num = 0;
  167. sqLiteDatabase = myOpenHelper.getWritableDatabase();
  168. int code = uriMatcher.match(uri);
  169. switch (code) {
  170. case 1:
  171. num = sqLiteDatabase.update(Table_Name, values, selection, selectionArgs);
  172. break;
  173. case 2:
  174. long id = ContentUris.parseId(uri);
  175. selection = (selection == null || "".equals(selection.trim())) ? _id
  176. + "=" + id
  177. : selection + " and " + _id + "=" + id;
  178. num = sqLiteDatabase.update(Table_Name, values, selection, selectionArgs);
  179. break;
  180. default:
  181. break;
  182. }
  183. return num;
  184. }
  185. // 数据库名称
  186. private final String DB_Name = "chenzheng_java.db";
  187. // 数据表名
  188. private final String Table_Name = "chenzheng_java";
  189. // 版本号
  190. private final int Version_1 = 1;
  191. private class MyOpenHelper extends SQLiteOpenHelper {
  192. public MyOpenHelper(Context context, String name,
  193. CursorFactory factory, int version) {
  194. super(context, name, factory, version);
  195. }
  196. /**
  197. * @description 当数据表无连接时创建新的表
  198. */
  199. @Override
  200. public void onCreate(SQLiteDatabase db) {
  201. String sql = " create table if not exists " + Table_Name
  202. + "(id INTEGER,name varchar(20),age integer,isMan boolean)";
  203. db.execSQL(sql);
  204. }
  205. /**
  206. * @description 当版本更新时触发的方法
  207. */
  208. @Override
  209. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  210. String sql = " drop table if exists " + Table_Name;
  211. db.execSQL(sql);
  212. onCreate(db);
  213. }
  214. }
  215. }

androidManifest.xml代码

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="cn.com.contentProvider" android:versionCode="1"
  4. android:versionName="1.0">
  5. <uses-sdk android:minSdkVersion="8" />
  6. <application android:icon="@drawable/icon" android:label="@string/app_name">
  7. <activity android:name=".ContentProviderAcitivity"
  8. android:label="@string/app_name">
  9. <intent-filter>
  10. <action android:name="android.intent.action.MAIN" />
  11. <category android:name="android.intent.category.LAUNCHER" />
  12. </intent-filter>
  13. </activity>
  14. <provider android:name=".MyContentProvider"
  15. android:authorities="cn.com.chenzheng_java.hello"
  16. android:multiprocess="true" android:permission="cn.com.chenzheng_java.permission"></provider>
  17. </application>
  18. <!--
  19. permission中的android:name的值与provider中的android:permission的值是一样的
  20. android:protectionLevel 则代表了权限等级
  21. -->
  22. <permission android:name="cn.com.chenzheng_java.permission"
  23. android:protectionLevel="normal"></permission>
  24. </manifest>

main.xml为默认。

----------------------------------------------------------------------------------------------------------------

第二个应用(用于访问内容提供者的应用)

android之内容提供者解析

activity代码

  1. package cn.com.chenzheng_java;
  2. import android.app.Activity;
  3. import android.content.ContentResolver;
  4. import android.content.ContentValues;
  5. import android.database.Cursor;
  6. import android.net.Uri;
  7. import android.os.Bundle;
  8. import android.util.Log;
  9. import android.view.View;
  10. import android.widget.Button;
  11. import android.widget.TextView;
  12. /**
  13. *
  14. * @author chenzheng_java
  15. * @description 通过访问内容提供者进行增删查改.注意本程序中为了方便阅读,
  16. * 在需要数据库列名的地方直接写上了数据库中字段的名称,实际上这是不合理的,
  17. * 作为内容提供者的使用者,我们不可能在使用这个内容提供者之前先去了解sqlite
  18. * 中表的结构。比较适宜的做法是,在内容提供者中将愿意提供给外部访问的字段名称(列名)
  19. * 定义为string final 的常量!
  20. */
  21. public class ContentAccessActivity extends Activity {
  22. private final static String tag = "通知";
  23. private TextView textView;
  24. String result = "结果:/n";
  25. ContentResolver reslover;
  26. Uri uri;
  27. @Override
  28. public void onCreate(Bundle savedInstanceState) {
  29. super.onCreate(savedInstanceState);
  30. setContentView(R.layout.main);
  31. /**
  32. * 这里我们一定要搞清楚,uri的内容到底和内容提供者中哪个地方一一对应
  33. * 在MyContentProvider中我们有如下片段
  34. * uriMatcher.addURI(authority, "path_chenzheng", 1);// 代表当前表中的所有的记录
  35. uriMatcher.addURI(authority, "path_chenzheng/#", 2);// 代表当前表中的某条特定的记录,记录id便是#处得数字
  36. 其中authority为cn.com.chenzheng_java.hello。
  37. */
  38. uri = Uri.parse("content://cn.com.chenzheng_java.hello/path_chenzheng");
  39. /**
  40. * 内容提供者是什么?内容提供者相当于一个封装好了增删改查操作的接口,这个接口有一把锁,只有携带钥匙的访问者才能访问。
  41. * ContentResolver是什么?ContentResolver是一个开锁匠,他携带者钥匙(钥匙上有标签显示他是那个门得钥匙,如path_chenzheng)
  42. * 去寻找内容提供者,然后访问内容提供者的增删查改方法
  43. * 我们这里调用contentResolver的增删查改就相当于将任务交给了锁匠,
  44. * 然后让锁匠去找能打开的内容提供者,并且执行里面相应的方法,并将结果返回.
  45. * ContentResolver的好处在于,我们可以无视CotentProvider的具体实现,无论contentProvider里面是如何实现的,我想执行
  46. * 某一个操作时,所要书写的代码都是一样的。
  47. */
  48. reslover = this.getContentResolver();
  49. textView = (TextView) findViewById(R.id.textView);
  50. Button insertButton = (Button) findViewById(R.id.insertButton);
  51. insertButton.setOnClickListener(new View.OnClickListener() {
  52. @Override
  53. public void onClick(View v) {
  54. insert(reslover, uri);
  55. }
  56. });
  57. Button deleteButton = (Button) findViewById(R.id.deleteButton);
  58. deleteButton.setOnClickListener(new View.OnClickListener() {
  59. @Override
  60. public void onClick(View v) {
  61. delete(reslover, uri);
  62. }
  63. });
  64. Button updateButton = (Button) findViewById(R.id.updateButton);
  65. updateButton.setOnClickListener(new View.OnClickListener() {
  66. @Override
  67. public void onClick(View v) {
  68. update(reslover, uri);
  69. }
  70. });
  71. Button queryButton = (Button) findViewById(R.id.queryButton);
  72. queryButton.setOnClickListener(new View.OnClickListener() {
  73. @Override
  74. public void onClick(View v) {
  75. query(reslover, uri);
  76. }
  77. });
  78. }
  79. private void insert(ContentResolver resolver, Uri uri) {
  80. ContentValues contentValues = new ContentValues();
  81. contentValues.put("name", "张小凡");
  82. contentValues.put("age", 22);
  83. contentValues.put("isMan", true);
  84. Uri uri2 = resolver.insert(uri, contentValues);
  85. Log.i(tag, "插入成功!");
  86. result += "成功插入了一条记录,uri为" + uri2;
  87. textView.setText(result);
  88. result = "";
  89. }
  90. private void update(ContentResolver resolver, Uri uri) {
  91. ContentValues contentValues = new ContentValues();
  92. contentValues.put("age", 122);
  93. int number = resolver.update(uri, contentValues, null, null);
  94. Log.i(tag, "更新成功!");
  95. result += "成功更新了" + number+"条记录";
  96. textView.setText(result);
  97. result = "";
  98. }
  99. private void delete(ContentResolver resolver, Uri uri) {
  100. String where = " 1=1 and isMan=?";
  101. //这里要注意哦,sqlite数据库中是没有boolean的,true会被转成1存储
  102. String[] selectionArgs = new String[] { "1" };
  103. int number = resolver.delete(uri, where, selectionArgs);
  104. Log.i(tag, "删除成功!");
  105. textView.setText(result + "成功删除了" + number + "条记录");
  106. result = "";
  107. }
  108. private void query(ContentResolver resolver, Uri uri) {
  109. String[] projection = new String[] { "id", "name", "age", "isMan" };
  110. Cursor cursor = resolver.query(uri, projection, null, null, null);
  111. int count = cursor.getCount();
  112. Log.i(tag, "总记录数" + count);
  113. int idIndex = cursor.getColumnIndex("id");
  114. int nameIndex = cursor.getColumnIndex("name");
  115. int ageIndex = cursor.getColumnIndex("age");
  116. int isManIndex = cursor.getColumnIndex("isMan");
  117. cursor.moveToFirst();
  118. while (!cursor.isAfterLast()) {
  119. int id = cursor.getInt(idIndex);
  120. String name = cursor.getString(nameIndex);
  121. int age = cursor.getInt(ageIndex);
  122. int isMan = cursor.getInt(isManIndex);
  123. Log.i(tag, "id=" + id + " name=" + name + " age=" + age + " isMan="
  124. + isMan);
  125. result += "id=" + id + " name=" + name + " age=" + age + " isMan="
  126. + isMan;
  127. cursor.moveToNext();
  128. }
  129. textView.setText(result);
  130. result = "";
  131. }
  132. }

manifest.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="cn.com.chenzheng_java"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6. <uses-sdk android:minSdkVersion="8" />
  7. <application android:icon="@drawable/icon" android:label="@string/app_name">
  8. <activity android:name=".ContentAccessActivity"
  9. android:label="@string/app_name">
  10. <intent-filter>
  11. <action android:name="android.intent.action.MAIN" />
  12. <category android:name="android.intent.category.LAUNCHER" />
  13. </intent-filter>
  14. </activity>
  15. </application>
  16. <!-- 添加对内容提供者访问的权限,该权限是有我们自己定义的哦 -->
  17. <uses-permission android:name="cn.com.chenzheng_java.permission"></uses-permission>
  18. </manifest>

main.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. >
  7. <TextView
  8. android:id="@+id/textView"
  9. android:layout_width="fill_parent"
  10. android:layout_height="wrap_content"
  11. android:text="@string/hello"
  12. />
  13. <Button
  14. android:id="@+id/insertButton"
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:text="insert"
  18. ></Button>
  19. <Button
  20. android:id="@+id/deleteButton"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"
  23. android:text="delete"
  24. ></Button>
  25. <Button
  26. android:id="@+id/updateButton"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:text="update"
  30. ></Button>
  31. <Button
  32. android:id="@+id/queryButton"
  33. android:layout_width="wrap_content"
  34. android:layout_height="wrap_content"
  35. android:text="query"
  36. ></Button>
  37. </LinearLayout>

--------------------------------------------------------------------------------

想说的话,在代码的注释中已经说的很清晰了。这里再次重复下我们定义和使用内容提供者的步骤吧。

定义内容提供者:

我们定义内容提供者的目的是什么,共享数据,对,定义内容提供者的目的就是让别的应用能够访问当前应用的一些数据,至于到底暴露给外界什么数据,我们可以 在定义内容提供者的时候详细控制!不管如何,我们明确了第一个问题,定义内容提供者的目的----数据共享!

我们平时对数据的操作都有哪些?增删改查!就四个字!这也是为什么我们再定义内容提供者的时候必须要实现相应的方法了。当然如果你要是不想提供相应的操作,你可以在内部进行方法空实现。

是不是所有的应用都可以访问我啊?不可能!我们可不是随便的人,对吧!所以我们要进行验证,验证不通过的直接让它去死就可以了。验证怎么验证啊?通过UriMatcher进行匹配!

现在我们已经提供了访问接口了,我们怎么让系统知道,别的应用可以用我的东西啊?去配置文件中注册!!

使用内容提供者:

如何找到该内容提供者啊?需要Uri和相应的访问权限。相当于地址

如何进行增删查改啊?通过ContentResolver对象的相应方法。