Android 简明教程

Android - Content Providers

content

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

Content providers let you centralize content in one place and have many different applications access it as needed. A content provider behaves very much like a database where you can query it, edit its content, as well as add or delete content using insert(), update(), delete(), and query() methods. In most cases this data is stored in an SQlite database.

一个内容提供程序作为一个 ContentProvider 类的子类实现,并且必须实现一套标准的 API,以便其他应用程序执行事务。

A content provider is implemented as a subclass of ContentProvider class and must implement a standard set of APIs that enable other applications to perform transactions.

public class My Application extends  ContentProvider {
}

Content URIs

要查询内容提供程序,您需要以 URI 的形式指定查询字符串,其格式如下:

To query a content provider, you specify the query string in the form of a URI which has following format −

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

以下是 URI 各个部分的详细内容:

Here is the detail of various parts of the URI −

Sr.No

Part & Description

1

prefix This is always set to content://

2

authority This specifies the name of the content provider, for example contacts, browser etc. For third-party content providers, this could be the fully qualified name, such as com.tutorialspoint.statusprovider

3

data_type This indicates the type of data that this particular provider provides. For example, if you are getting all the contacts from the Contacts content provider, then the data path would be people and URI would look like thiscontent://contacts/people

4

id This specifies the specific record requested. For example, if you are looking for contact number 5 in the Contacts content provider then URI would look like this content://contacts/people/5.

Create Content Provider

创建自己的内容提供程序涉及许多简单的步骤。

This involves number of simple steps to create your own content provider.

  1. First of all you need to create a Content Provider class that extends the ContentProviderbaseclass.

  2. Second, you need to define your content provider URI address which will be used to access the content.

  3. Next you will need to create your own database to keep the content. Usually, Android uses SQLite database and framework needs to override onCreate() method which will use SQLite Open Helper method to create or open the provider’s database. When your application is launched, the onCreate() handler of each of its Content Providers is called on the main application thread.

  4. Next you will have to implement Content Provider queries to perform different database specific operations.

  5. Finally register your Content Provider in your activity file using <provider> tag.

以下是您需要在内容提供程序类中覆盖的方法列表,以让您的内容提供程序正常工作:

Here is the list of methods which you need to override in Content Provider class to have your Content Provider working −

content1
  1. onCreate() This method is called when the provider is started.

  2. query() This method receives a request from a client. The result is returned as a Cursor object.

  3. *insert()*This method inserts a new record into the content provider.

  4. delete() This method deletes an existing record from the content provider.

  5. update() This method updates an existing record from the content provider.

  6. getType() This method returns the MIME type of the data at the given URI.

Example

此示例会告诉你如何创建自己的 ContentProvider。那么让我们按照下面类似于创建 Hello 示例中所遵循的步骤来进行−

This example will explain you how to create your own ContentProvider. So let’s follow the following steps to similar to what we followed while creating Hello World Example−

Step

Description

1

You will use Android StudioIDE to create an Android application and name it as My Application under a package com.example.MyApplication, with blank Activity.

2

Modify main activity file MainActivity.java to add two new methods onClickAddName() and onClickRetrieveStudents().

3

Create a new java file called StudentsProvider.java under the package com.example.MyApplication to define your actual provider and associated methods.

4

Register your content provider in your AndroidManifest.xml file using <provider…​/> tag

5

Modify the default content of res/layout/activity_main.xml file to include a small GUI to add students records.

6

No need to change string.xml.Android studio take care of string.xml file.

7

Run the application to launch Android emulator and verify the result of the changes done in the application.

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

Following is the content of the modified main activity file src/com.example.MyApplication/MainActivity.java. This file can include each of the fundamental life cycle methods. We have added two new methods onClickAddName() and onClickRetrieveStudents() to handle user interaction with the application.

package com.example.MyApplication;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;

import android.content.ContentValues;
import android.content.CursorLoader;

import android.database.Cursor;

import android.view.Menu;
import android.view.View;

import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
   }
   public void onClickAddName(View view) {
      // Add a new student record
      ContentValues values = new ContentValues();
      values.put(StudentsProvider.NAME,
         ((EditText)findViewById(R.id.editText2)).getText().toString());

      values.put(StudentsProvider.GRADE,
         ((EditText)findViewById(R.id.editText3)).getText().toString());

      Uri uri = getContentResolver().insert(
         StudentsProvider.CONTENT_URI, values);

      Toast.makeText(getBaseContext(),
         uri.toString(), Toast.LENGTH_LONG).show();
   }
   public void onClickRetrieveStudents(View view) {
      // Retrieve student records
      String URL = "content://com.example.MyApplication.StudentsProvider";

      Uri students = Uri.parse(URL);
      Cursor c = managedQuery(students, null, null, null, "name");

      if (c.moveToFirst()) {
         do{
            Toast.makeText(this,
               c.getString(c.getColumnIndex(StudentsProvider._ID)) +
                  ", " +  c.getString(c.getColumnIndex( StudentsProvider.NAME)) +
                     ", " + c.getString(c.getColumnIndex( StudentsProvider.GRADE)),
            Toast.LENGTH_SHORT).show();
         } while (c.moveToNext());
      }
   }
}

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

Create new file StudentsProvider.java under com.example.MyApplication package and following is the content of src/com.example.MyApplication/StudentsProvider.java

package com.example.MyApplication;

import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;

import android.database.Cursor;
import android.database.SQLException;

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;

import android.net.Uri;
import android.text.TextUtils;

public class StudentsProvider extends ContentProvider {
   static final String PROVIDER_NAME = "com.example.MyApplication.StudentsProvider";
   static final String URL = "content://" + PROVIDER_NAME + "/students";
   static final Uri CONTENT_URI = Uri.parse(URL);

   static final String _ID = "_id";
   static final String NAME = "name";
   static final String GRADE = "grade";

   private static HashMap<String, String> STUDENTS_PROJECTION_MAP;

   static final int STUDENTS = 1;
   static final int STUDENT_ID = 2;

   static final UriMatcher uriMatcher;
   static{
      uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
      uriMatcher.addURI(PROVIDER_NAME, "students", STUDENTS);
      uriMatcher.addURI(PROVIDER_NAME, "students/#", STUDENT_ID);
   }

   /**
      * Database specific constant declarations
   */

   private SQLiteDatabase db;
   static final String DATABASE_NAME = "College";
   static final String STUDENTS_TABLE_NAME = "students";
   static final int DATABASE_VERSION = 1;
   static final String CREATE_DB_TABLE =
      " CREATE TABLE " + STUDENTS_TABLE_NAME +
         " (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
         " name TEXT NOT NULL, " +
         " grade TEXT NOT NULL);";

   /**
      * Helper class that actually creates and manages
      * the provider's underlying data repository.
   */

   private static class DatabaseHelper extends SQLiteOpenHelper {
      DatabaseHelper(Context context){
         super(context, DATABASE_NAME, null, DATABASE_VERSION);
      }

      @Override
      public void onCreate(SQLiteDatabase db) {
         db.execSQL(CREATE_DB_TABLE);
      }

      @Override
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
         db.execSQL("DROP TABLE IF EXISTS " +  STUDENTS_TABLE_NAME);
         onCreate(db);
      }
   }

   @Override
   public boolean onCreate() {
      Context context = getContext();
      DatabaseHelper dbHelper = new DatabaseHelper(context);

      /**
         * Create a write able database which will trigger its
         * creation if it doesn't already exist.
      */

      db = dbHelper.getWritableDatabase();
      return (db == null)? false:true;
   }

   @Override
   public Uri insert(Uri uri, ContentValues values) {
      /**
         * Add a new student record
      */
      long rowID = db.insert(	STUDENTS_TABLE_NAME, "", values);

      /**
         * If record is added successfully
      */
      if (rowID > 0) {
         Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
         getContext().getContentResolver().notifyChange(_uri, null);
         return _uri;
      }

      throw new SQLException("Failed to add a record into " + uri);
   }

   @Override
   public Cursor query(Uri uri, String[] projection,
      String selection,String[] selectionArgs, String sortOrder) {
      SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
      qb.setTables(STUDENTS_TABLE_NAME);

      switch (uriMatcher.match(uri)) {
         case STUDENTS:
            qb.setProjectionMap(STUDENTS_PROJECTION_MAP);
         break;

         case STUDENT_ID:
            qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1));
         break;

         default:
      }

      if (sortOrder == null || sortOrder == ""){
         /**
            * By default sort on student names
         */
         sortOrder = NAME;
      }

      Cursor c = qb.query(db,	projection,	selection,
         selectionArgs,null, null, sortOrder);
      /**
         * register to watch a content URI for changes
      */
      c.setNotificationUri(getContext().getContentResolver(), uri);
      return c;
   }

   @Override
   public int delete(Uri uri, String selection, String[] selectionArgs) {
      int count = 0;
      switch (uriMatcher.match(uri)){
         case STUDENTS:
            count = db.delete(STUDENTS_TABLE_NAME, selection, selectionArgs);
         break;

         case STUDENT_ID:
            String id = uri.getPathSegments().get(1);
            count = db.delete( STUDENTS_TABLE_NAME, _ID +  " = " + id +
               (!TextUtils.isEmpty(selection) ? "
               AND (" + selection + ')' : ""), selectionArgs);
            break;
         default:
            throw new IllegalArgumentException("Unknown URI " + uri);
      }

      getContext().getContentResolver().notifyChange(uri, null);
      return count;
   }

   @Override
   public int update(Uri uri, ContentValues values,
      String selection, String[] selectionArgs) {
      int count = 0;
      switch (uriMatcher.match(uri)) {
         case STUDENTS:
            count = db.update(STUDENTS_TABLE_NAME, values, selection, selectionArgs);
         break;

         case STUDENT_ID:
            count = db.update(STUDENTS_TABLE_NAME, values,
               _ID + " = " + uri.getPathSegments().get(1) +
               (!TextUtils.isEmpty(selection) ? "
               AND (" +selection + ')' : ""), selectionArgs);
            break;
         default:
            throw new IllegalArgumentException("Unknown URI " + uri );
      }

      getContext().getContentResolver().notifyChange(uri, null);
      return count;
   }

   @Override
   public String getType(Uri uri) {
      switch (uriMatcher.match(uri)){
         /**
            * Get all student records
         */
         case STUDENTS:
            return "vnd.android.cursor.dir/vnd.example.students";
         /**
            * Get a particular student
         */
         case STUDENT_ID:
            return "vnd.android.cursor.item/vnd.example.students";
         default:
            throw new IllegalArgumentException("Unsupported URI: " + uri);
      }
   }
}

以下是已修改的 AndroidManifest.xml 文件的内容。我们在其中添加了 <provider…​/> 标记以包含我们的内容提供程序:

Following will the modified content of AndroidManifest.xml file. Here we have added <provider…​/> tag to include our content provider:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.MyApplication">

   <application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:supportsRtl="true"
      android:theme="@style/AppTheme">
         <activity android:name=".MainActivity">
            <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
         </activity>

      <provider android:name="StudentsProvider"
         android:authorities="com.example.MyApplication.StudentsProvider"/>
   </application>
</manifest>

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

Following will be the content of res/layout/activity_main.xml file−

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context="com.example.MyApplication.MainActivity">

   <TextView
      android:id="@+id/textView1"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Content provider"
      android:layout_alignParentTop="true"
      android:layout_centerHorizontal="true"
      android:textSize="30dp" />

   <TextView
      android:id="@+id/textView2"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Tutorials point "
      android:textColor="#ff87ff09"
      android:textSize="30dp"
      android:layout_below="@+id/textView1"
      android:layout_centerHorizontal="true" />

   <ImageButton
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/imageButton"
      android:src="@drawable/abc"
      android:layout_below="@+id/textView2"
      android:layout_centerHorizontal="true" />

   <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/button2"
      android:text="Add Name"
      android:layout_below="@+id/editText3"
      android:layout_alignRight="@+id/textView2"
      android:layout_alignEnd="@+id/textView2"
      android:layout_alignLeft="@+id/textView2"
      android:layout_alignStart="@+id/textView2"
      android:onClick="onClickAddName"/>

   <EditText
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/editText"
      android:layout_below="@+id/imageButton"
      android:layout_alignRight="@+id/imageButton"
      android:layout_alignEnd="@+id/imageButton" />

   <EditText
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/editText2"
      android:layout_alignTop="@+id/editText"
      android:layout_alignLeft="@+id/textView1"
      android:layout_alignStart="@+id/textView1"
      android:layout_alignRight="@+id/textView1"
      android:layout_alignEnd="@+id/textView1"
      android:hint="Name"
      android:textColorHint="@android:color/holo_blue_light" />

   <EditText
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/editText3"
      android:layout_below="@+id/editText"
      android:layout_alignLeft="@+id/editText2"
      android:layout_alignStart="@+id/editText2"
      android:layout_alignRight="@+id/editText2"
      android:layout_alignEnd="@+id/editText2"
      android:hint="Grade"
      android:textColorHint="@android:color/holo_blue_bright" />

   <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Retrive student"
      android:id="@+id/button"
      android:layout_below="@+id/button2"
      android:layout_alignRight="@+id/editText3"
      android:layout_alignEnd="@+id/editText3"
      android:layout_alignLeft="@+id/button2"
      android:layout_alignStart="@+id/button2"
      android:onClick="onClickRetrieveStudents"/>
</RelativeLayout>

请确保你拥有 res/values/strings.xml 文件的以下内容:

Make sure you have following content of res/values/strings.xml file:

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

让我们尝试运行我们刚刚创建的 My Application 已修改的应用程序。我假设你已在执行环境设置时创建了 AVD 。若要通过 Android Studio IDE 运行该应用程序,请打开项目中的一个活动文件并从工具栏中单击运行图标。Android Studio 会将此应用程序安装到你的 AVD 上,然后启动它,如果你的设置和应用程序一切正常,它将显示以下模拟器窗口,请耐心等待,因为这可能取决于你的计算机速度而需要一些时间−

Let’s try to run our modified My Application application we just created. I assume you had created your AVD while doing environment set-up. To run the app from Android Studio IDE, open one of your project’s activity files and click Run icon from the tool bar. Android Studio installs the app on your AVD and starts it and if everything is fine with your set-up and application, it will display following Emulator window, be patience because it may take sometime based on your computer speed −

content2

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

Now let’s enter student Name and Grade and finally click on Add Name button, this will add student record in the database and will flash a message at the bottom showing ContentProvider URI along with record number added in the database. This operation makes use of our insert() method. Let’s repeat this process to add few more students in the database of our content provider.

content3

一旦你完成在数据库中添加记录,现在是时候让 ContentProvider 向我们返回这些记录了,因此让我们单击 Retrieve Students 按钮,这会按照我们对 query() 方法的实现一一抓取并显示所有记录。

Once you are done with adding records in the database, now its time to ask ContentProvider to give us those records back, so let’s click Retrieve Students button which will fetch and display all the records one by one which is as per our the implementation of our query() method.

你可以通过在 MainActivity.java 文件中提供回调函数针对更新和删除操作编写活动,然后修改用户界面以获取与我们为添加和读取操作所做的相同方式的更新和删除操作的按钮。

You can write activities against update and delete operations by providing callback functions in MainActivity.java file and then modify user interface to have buttons for update and deleted operations in the same way as we have done for add and read operations.

这样,你可以使用现有的 Content Provider(如通讯录),否则可以将 Content Provider 概念用于开发基于数据库的精良应用程序,在其中,你可以执行上面示例中所解释的读、写、更新和删除等各种数据库操作。

This way you can use existing Content Provider like Address Book or you can use Content Provider concept in developing nice database oriented applications where you can perform all sort of database operations like read, write, update and delete as explained above in the example.