Android(安卓)内容提供器

内容提供器组件根据请求将数据从一个应用程序提供给其他应用程序。此类请求由 ContentResolver 类的方法处理。内容提供器可以使用不同的方式存储其数据,数据可以存储在数据库、文件甚至网络中。

有时需要跨应用程序共享数据。这就是内容提供器变得非常有用的地方。

内容提供器让您可以将内容集中在一个地方,并让许多不同的应用程序根据需要访问它。内容提供程序的行为非常类似于数据库,您可以在其中使用 insert()update()delete()query() 方法查询内容、编辑内容以及添加或删除内容。在大多数情况下,这些数据存储在 SQlite 数据库中。

内容提供器是作为 ContentProvider 类的子类实现的,必须实现一组标准的 API,使其他应用程序能够执行事务。

  1. public class My Application extends ContentProvider {
  2. }

内容 URI

要查询内容提供器,请以 URI 的形式指定查询字符串,其格式如下:

  1. <prefix>://<authority>/<data_type>/<id>

以下是 URI 各个部分的详细信息:

编号部分 & 描述
1

prefix

这始终设置为内容://

2

authority

这将指定内容提供器的名称,例如联系人、浏览器等对于第三方内容提供器,这可以是完全限定的名称,例如 com.cankaoshouce.statusprovider

3

data_type

这表示此特定提供器的数据类型例如,比如您要从 "联系人" 内容提供器获取所有联系人,那么数据路径将是 peopleURI 将类似于 iscontent://contacts/people

4

id

这将指定请求的特定记录例如,如果您正在 "联系人" 内容提供器中查找联系人编号 5,则 URI 将如下所示 content://contacts/people/5..


创建内容提供器

创建内容提供商的一些简单步骤:

  • 首先,您需要创建一个扩展 ContentProviderbase 类的 ContentProvider 类。
  • 其次,您需要定义将用于访问内容的内容提供器 URI 地址。
  • 接下来,您需要创建自己的数据库来保存内容。通常,Android 使用 SQLite 数据库,框架需要覆盖 onCreate() 方法,该方法将使用 SQLite Open Helper 方法创建或打开提供器的数据库。启动应用程序时,将在主应用程序线程上调用其每个内容提供器的 onCreate() 处理程序。
  • 接下来,您必须实现内容提供器查询以执行不同的数据库特定操作。
  • 最后,使用 <Provider> 标记在 activity 文件中注册内容提供器。

以下是您需要在 ContentProvider 类中重写的方法列表,以使内容提供器正常工作:

  • onCreate() - 此方法在启动提供器时调用。
  • query() - 此方法接收来自客户端的请求。结果作为 Cursor 对象返回。
  • insert() - 此方法将新记录插入内容提供器。
  • delete() - 此方法从内容提供器中删除现有记录。
  • update() - 此方法更新内容提供器中的现有记录。
  • getType() - 此方法返回给定 URI 处数据的 MIME 类型。

实例

这个例子将解释如何创建自己的内容提供器。因此,让我们按照以下步骤来创建 Hello World 实例:

步骤描述
1您将使用 Android Studio IDE 创建一个 Android 应用程序,并在 com.example.helloworld 包下将其命名为 HelloWrold,Activity 为空。
2修改主 activity 文件 MainActivity.java 以添加两个新方法 onClickAddName()onClickRetrieveStudents()
3com.example.helloworld 包下创建一个名为 StudentsProvider.java 的新 Java 文件, 以定义您的实际提供器和相关方法。。
4使用 <provider … /> 标记在 AndroidManifest.xml 文件中注册内容提供者
5修改 res/layout/activity_main.xml 文件的默认内容,以包括一个小的 GUI 图形界面以添加学生记录。
6无需更改 string.xml。Android Studio 负责字符串 xml 文件。
7运行应用程序以启动 Android 模拟器,并验证应用程序中所做更改的结果。

以下是修改后的主 activity 文件 src/com.example.MyApplication/MainActivity.java 的内容。该文件可以包括每个基本生命周期方法。我们添加了两个新方法 onClickAddName()onClickRetrieveStudents() 来处理用户与应用程序的交互。

  1. package com.example.MyApplication;
  2. import android.net.Uri;
  3. import android.os.Bundle;
  4. import android.app.Activity;
  5. import android.content.ContentValues;
  6. import android.content.CursorLoader;
  7. import android.database.Cursor;
  8. import android.view.Menu;
  9. import android.view.View;
  10. import android.widget.EditText;
  11. import android.widget.Toast;
  12. public class MainActivity extends Activity {
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.activity_main);
  17. }
  18. public void onClickAddName(View view) {
  19. // Add a new student record
  20. ContentValues values = new ContentValues();
  21. values.put(StudentsProvider.NAME,
  22. ((EditText)findViewById(R.id.editText2)).getText().toString());
  23. values.put(StudentsProvider.GRADE,
  24. ((EditText)findViewById(R.id.editText3)).getText().toString());
  25. Uri uri = getContentResolver().insert(
  26. StudentsProvider.CONTENT_URI, values);
  27. Toast.makeText(getBaseContext(),
  28. uri.toString(), Toast.LENGTH_LONG).show();
  29. }
  30. public void onClickRetrieveStudents(View view) {
  31. // Retrieve student records
  32. String URL = "content://com.example.MyApplication.StudentsProvider";
  33. Uri students = Uri.parse(URL);
  34. Cursor c = managedQuery(students, null, null, null, "name");
  35. if (c.moveToFirst()) {
  36. do{
  37. Toast.makeText(this,
  38. c.getString(c.getColumnIndex(StudentsProvider._ID)) +
  39. ", " + c.getString(c.getColumnIndex( StudentsProvider.NAME)) +
  40. ", " + c.getString(c.getColumnIndex( StudentsProvider.GRADE)),
  41. Toast.LENGTH_SHORT).show();
  42. } while (c.moveToNext());
  43. }
  44. }
  45. }

com.example.MyApplication 包下创建新文件 StudentsProvider.java,以下是 src/com.example.MyApplication/StudentsProvider.java 的内容

  1. package com.example.MyApplication;
  2. import java.util.HashMap;
  3. import android.content.ContentProvider;
  4. import android.content.ContentUris;
  5. import android.content.ContentValues;
  6. import android.content.Context;
  7. import android.content.UriMatcher;
  8. import android.database.Cursor;
  9. import android.database.SQLException;
  10. import android.database.sqlite.SQLiteDatabase;
  11. import android.database.sqlite.SQLiteOpenHelper;
  12. import android.database.sqlite.SQLiteQueryBuilder;
  13. import android.net.Uri;
  14. import android.text.TextUtils;
  15. public class StudentsProvider extends ContentProvider {
  16. static final String PROVIDER_NAME = "com.example.MyApplication.StudentsProvider";
  17. static final String URL = "content://" + PROVIDER_NAME + "/students";
  18. static final Uri CONTENT_URI = Uri.parse(URL);
  19. static final String _ID = "_id";
  20. static final String NAME = "name";
  21. static final String GRADE = "grade";
  22. private static HashMap<String, String> STUDENTS_PROJECTION_MAP;
  23. static final int STUDENTS = 1;
  24. static final int STUDENT_ID = 2;
  25. static final UriMatcher uriMatcher;
  26. static{
  27. uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  28. uriMatcher.addURI(PROVIDER_NAME, "students", STUDENTS);
  29. uriMatcher.addURI(PROVIDER_NAME, "students/#", STUDENT_ID);
  30. }
  31. /**
  32. * Database specific constant declarations
  33. */
  34. private SQLiteDatabase db;
  35. static final String DATABASE_NAME = "College";
  36. static final String STUDENTS_TABLE_NAME = "students";
  37. static final int DATABASE_VERSION = 1;
  38. static final String CREATE_DB_TABLE =
  39. " CREATE TABLE " + STUDENTS_TABLE_NAME +
  40. " (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
  41. " name TEXT NOT NULL, " +
  42. " grade TEXT NOT NULL);";
  43. /**
  44. * Helper class that actually creates and manages
  45. * the provider's underlying data repository.
  46. */
  47. private static class DatabaseHelper extends SQLiteOpenHelper {
  48. DatabaseHelper(Context context){
  49. super(context, DATABASE_NAME, null, DATABASE_VERSION);
  50. }
  51. @Override
  52. public void onCreate(SQLiteDatabase db) {
  53. db.execSQL(CREATE_DB_TABLE);
  54. }
  55. @Override
  56. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  57. db.execSQL("DROP TABLE IF EXISTS " + STUDENTS_TABLE_NAME);
  58. onCreate(db);
  59. }
  60. }
  61. @Override
  62. public boolean onCreate() {
  63. Context context = getContext();
  64. DatabaseHelper dbHelper = new DatabaseHelper(context);
  65. /**
  66. * Create a write able database which will trigger its
  67. * creation if it doesn't already exist.
  68. */
  69. db = dbHelper.getWritableDatabase();
  70. return (db == null)? false:true;
  71. }
  72. @Override
  73. public Uri insert(Uri uri, ContentValues values) {
  74. /**
  75. * Add a new student record
  76. */
  77. long rowID = db.insert(STUDENTS_TABLE_NAME, "", values);
  78. /**
  79. * If record is added successfully
  80. */
  81. if (rowID > 0) {
  82. Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
  83. getContext().getContentResolver().notifyChange(_uri, null);
  84. return _uri;
  85. }
  86. throw new SQLException("Failed to add a record into " + uri);
  87. }
  88. @Override
  89. public Cursor query(Uri uri, String[] projection,
  90. String selection,String[] selectionArgs, String sortOrder) {
  91. SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
  92. qb.setTables(STUDENTS_TABLE_NAME);
  93. switch (uriMatcher.match(uri)) {
  94. case STUDENTS:
  95. qb.setProjectionMap(STUDENTS_PROJECTION_MAP);
  96. break;
  97. case STUDENT_ID:
  98. qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1));
  99. break;
  100. default:
  101. }
  102. if (sortOrder == null || sortOrder == ""){
  103. /**
  104. * By default sort on student names
  105. */
  106. sortOrder = NAME;
  107. }
  108. Cursor c = qb.query(db,projection,selection,
  109. selectionArgs,null, null, sortOrder);
  110. /**
  111. * register to watch a content URI for changes
  112. */
  113. c.setNotificationUri(getContext().getContentResolver(), uri);
  114. return c;
  115. }
  116. @Override
  117. public int delete(Uri uri, String selection, String[] selectionArgs) {
  118. int count = 0;
  119. switch (uriMatcher.match(uri)){
  120. case STUDENTS:
  121. count = db.delete(STUDENTS_TABLE_NAME, selection, selectionArgs);
  122. break;
  123. case STUDENT_ID:
  124. String id = uri.getPathSegments().get(1);
  125. count = db.delete( STUDENTS_TABLE_NAME, _ID + " = " + id +
  126. (!TextUtils.isEmpty(selection) ? "
  127. AND (" + selection + ')' : ""), selectionArgs);
  128. break;
  129. default:
  130. throw new IllegalArgumentException("Unknown URI " + uri);
  131. }
  132. getContext().getContentResolver().notifyChange(uri, null);
  133. return count;
  134. }
  135. @Override
  136. public int update(Uri uri, ContentValues values,
  137. String selection, String[] selectionArgs) {
  138. int count = 0;
  139. switch (uriMatcher.match(uri)) {
  140. case STUDENTS:
  141. count = db.update(STUDENTS_TABLE_NAME, values, selection, selectionArgs);
  142. break;
  143. case STUDENT_ID:
  144. count = db.update(STUDENTS_TABLE_NAME, values,
  145. _ID + " = " + uri.getPathSegments().get(1) +
  146. (!TextUtils.isEmpty(selection) ? "
  147. AND (" +selection + ')' : ""), selectionArgs);
  148. break;
  149. default:
  150. throw new IllegalArgumentException("Unknown URI " + uri );
  151. }
  152. getContext().getContentResolver().notifyChange(uri, null);
  153. return count;
  154. }
  155. @Override
  156. public String getType(Uri uri) {
  157. switch (uriMatcher.match(uri)){
  158. /**
  159. * Get all student records
  160. */
  161. case STUDENTS:
  162. return "vnd.android.cursor.dir/vnd.example.students";
  163. /**
  164. * Get a particular student
  165. */
  166. case STUDENT_ID:
  167. return "vnd.android.cursor.item/vnd.example.students";
  168. default:
  169. throw new IllegalArgumentException("Unsupported URI: " + uri);
  170. }
  171. }
  172. }

以下将修改 AndroidManifest.xml 文件的内容 。在这里,我们添加了 <provider … /> 标签以包括我们的内容提供器:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.example.MyApplication">
  4. <application
  5. android:allowBackup="true"
  6. android:icon="@mipmap/ic_launcher"
  7. android:label="@string/app_name"
  8. android:supportsRtl="true"
  9. android:theme="@style/AppTheme">
  10. <activity android:name=".MainActivity">
  11. <intent-filter>
  12. <action android:name="android.intent.action.MAIN" />
  13. <category android:name="android.intent.category.LAUNCHER" />
  14. </intent-filter>
  15. </activity>
  16. <provider android:name="StudentsProvider"
  17. android:authorities="com.example.MyApplication.StudentsProvider"/>
  18. </application>
  19. </manifest>

以下是 res/layout/activity_main.xml 文件的内容;

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:paddingBottom="@dimen/activity_vertical_margin"
  7. android:paddingLeft="@dimen/activity_horizontal_margin"
  8. android:paddingRight="@dimen/activity_horizontal_margin"
  9. android:paddingTop="@dimen/activity_vertical_margin"
  10. tools:context="com.example.MyApplication.MainActivity">
  11. <TextView
  12. android:id="@+id/textView1"
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:text="Content provider"
  16. android:layout_alignParentTop="true"
  17. android:layout_centerHorizontal="true"
  18. android:textSize="30dp" />
  19. <TextView
  20. android:id="@+id/textView2"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"
  23. android:text="Cankaoshouce "
  24. android:textColor="#ff87ff09"
  25. android:textSize="30dp"
  26. android:layout_below="@+id/textView1"
  27. android:layout_centerHorizontal="true" />
  28. <ImageButton
  29. android:layout_width="wrap_content"
  30. android:layout_height="wrap_content"
  31. android:id="@+id/imageButton"
  32. android:src="@drawable/abc"
  33. android:layout_below="@+id/textView2"
  34. android:layout_centerHorizontal="true" />
  35. <Button
  36. android:layout_width="wrap_content"
  37. android:layout_height="wrap_content"
  38. android:id="@+id/button2"
  39. android:text="Add Name"
  40. android:layout_below="@+id/editText3"
  41. android:layout_alignRight="@+id/textView2"
  42. android:layout_alignEnd="@+id/textView2"
  43. android:layout_alignLeft="@+id/textView2"
  44. android:layout_alignStart="@+id/textView2"
  45. android:onClick="onClickAddName"/>
  46. <EditText
  47. android:layout_width="wrap_content"
  48. android:layout_height="wrap_content"
  49. android:id="@+id/editText"
  50. android:layout_below="@+id/imageButton"
  51. android:layout_alignRight="@+id/imageButton"
  52. android:layout_alignEnd="@+id/imageButton" />
  53. <EditText
  54. android:layout_width="wrap_content"
  55. android:layout_height="wrap_content"
  56. android:id="@+id/editText2"
  57. android:layout_alignTop="@+id/editText"
  58. android:layout_alignLeft="@+id/textView1"
  59. android:layout_alignStart="@+id/textView1"
  60. android:layout_alignRight="@+id/textView1"
  61. android:layout_alignEnd="@+id/textView1"
  62. android:hint="Name"
  63. android:textColorHint="@android:color/holo_blue_light" />
  64. <EditText
  65. android:layout_width="wrap_content"
  66. android:layout_height="wrap_content"
  67. android:id="@+id/editText3"
  68. android:layout_below="@+id/editText"
  69. android:layout_alignLeft="@+id/editText2"
  70. android:layout_alignStart="@+id/editText2"
  71. android:layout_alignRight="@+id/editText2"
  72. android:layout_alignEnd="@+id/editText2"
  73. android:hint="Grade"
  74. android:textColorHint="@android:color/holo_blue_bright" />
  75. <Button
  76. android:layout_width="wrap_content"
  77. android:layout_height="wrap_content"
  78. android:text="Retrive student"
  79. android:id="@+id/button"
  80. android:layout_below="@+id/button2"
  81. android:layout_alignRight="@+id/editText3"
  82. android:layout_alignEnd="@+id/editText3"
  83. android:layout_alignLeft="@+id/button2"
  84. android:layout_alignStart="@+id/button2"
  85. android:onClick="onClickRetrieveStudents"/>
  86. </RelativeLayout>

确保您的 res/values/strings.xml 文件内容如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <string name="app_name">My Application</string>
  4. </resources>;

让我们尝试运行我们刚刚创建的修改过的 My Application 应用程序。我假设您在进行环境设置时创建了 AVD。要从 Android Studio IDE 运行应用程序,请打开项目的 activity 文件之一,然后从工具栏中单击运行 Android Studio Eclipse Run Icon 图标。Android Studio 在您的 AVD 上安装应用程序并启动它,如果您的设置和应用程序一切正常,它将显示在模拟器窗口后,请耐心等待,因为根据您的计算机速度,可能需要一些时间。

现在,让我们输入学生的 NameGrade,最后单击 Add Name 按钮,这将在数据库中添加学生记录,并在底部闪烁一条消息,显示 ContentProvider URI 以及数据库中添加的记录编号。此操作使用我们的 insert() 方法。让我们重复这个过程,在内容提供器的数据库中添加更多的学生。

在数据库中添加完记录后,现在是时候让内容提供器将这些记录返回给我们了,所以让我们单击 Retrieve Students 按钮,它将根据 query() 方法的实现一个接一个地获取并显示所有记录。

通过在 MainActivity.java 文件中提供回调函数,可以针对更新和删除操作编写活动。然后修改用户界面,使其具有更新和删除操作的按钮,方法与添加和读取操作相同。

通过这种方式,您可以使用现有的内容提供者(如通讯簿),也可以使用内容提供器(Content Provider)概念开发漂亮的面向数据库的应用程序,在这些应用程序中,您可以执行所有类型的数据库操作,如读、写、更新和删除。