Android - Content Provider

Content-Provider-Komponenten stellen Daten aus einer Anwendung anderen Anwendungen über Anfragen bereit. Diese Anfragen werden von den Methoden der Klasse ContentResolver verarbeitet. Content-Provider können verschiedene Möglichkeiten zum Speichern von Daten nutzen. Daten können in einer Datenbank, in Dateien oder sogar im Netzwerk gespeichert werden.

图片

Manchmal besteht die Notwendigkeit, Daten zwischen Anwendungen gemeinsam zu nutzen. Hier werden Content-Provider sehr nützlich.

Content-Provider können Inhalte zentralisieren, und bei Bedarf können mehrere verschiedene Anwendungen darauf zugreifen. Content-Provider verhalten sich sehr ähnlich wie Datenbanken. Sie können ihren Inhalt abfragen und bearbeiten, insert(), update(), delete() und query() verwenden, um Inhalte hinzuzufügen oder zu löschen. In den meisten Fällen werden Daten in einer SQLite-Datenbank gespeichert.

Content-Provider werden als Unterklassen der Klasse ContentProvider implementiert. Sie müssen eine Reihe standardmäßiger APIs implementieren, damit andere Anwendungen Transaktionen durchführen können.

public class MyApplication extends  ContentProvider {

}

Content-URI

Um einen Content-Provider abzufragen, müssen Sie die Abfragezeichenfolge in Form einer URI im folgenden Format angeben:

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

Das Folgende ist die konkrete Erklärung der einzelnen Teile der URI:

Teil Beschreibung
Präfix Präfix: immer auf content:// gesetzt
Autorität Autorität: gibt den Namen des Content-Providers an, z. B. contacts, browser usw. Content-Provider von Drittanbietern können vollständige Namen verwenden, wie z. B.: cn.programmer.statusprovider
data_type Datentyp: Dies gibt den Typ der Daten in diesem bestimmten Content-Provider an. Wenn Sie beispielsweise alle Kontakte über den Content-Provider Contacts abrufen möchten, ist der Datenpfad people, dann sieht die URI so aus: content://contacts/people
id Dies gibt einen bestimmten angeforderten Datensatz an. Wenn Sie beispielsweise einen Kontakt mit der ID 5 im Content-Provider Contacts nachschlagen, sieht die URI so aus: content://contacts/people/5

Erstellen eines Content-Providers

Hier werden die einfachen Schritte zum Erstellen Ihres eigenen Content-Providers beschrieben.

  • Zuerst müssen Sie die Basisklasse ContentProvider unterklassieren, um eine Content-Provider-Klasse zu erstellen.
  • Zweitens müssen Sie die URI-Adresse Ihres Content-Providers definieren, die für den Zugriff auf den Inhalt verwendet wird.
  • Als Nächstes müssen Sie eine Datenbank erstellen, um den Inhalt zu speichern. Normalerweise verwendet Android eine SQLite-Datenbank, und Sie überschreiben die Methode onCreate() im Framework, um die Methoden von SQLiteOpenHelper zu verwenden, um die Datenbank des Providers zu erstellen oder zu öffnen. Wenn Ihre Anwendung gestartet wird, wird die Methode onCreate() jedes ihrer Content-Provider im Hauptthread der Anwendung aufgerufen.
  • Registrieren Sie schließlich den Content-Provider in AndroidManifest.xml mit dem Tag <provider.../>.

Hier sind einige Methoden, die Sie in der Klasse ContentProvider überschreiben müssen, damit Ihr Content-Provider ordnungsgemäß funktioniert:

图片

  • onCreate(): wird aufgerufen, wenn der Provider gestartet wird.
  • query(): Diese Methode nimmt Anfragen von Clients entgegen. Als Ergebnis wird ein Cursor-Objekt zurückgegeben.
  • insert(): Diese Methode fügt neue Datensätze in den Content-Provider ein.
  • delete(): Diese Methode löscht vorhandene Datensätze aus dem Content-Provider.
  • update(): Diese Methode aktualisiert vorhandene Datensätze im Content-Provider.
  • getType(): Diese Methode gibt den Metadatentyp für die angegebene URI zurück.

Beispiel

Dieses Beispiel erklärt, wie Sie Ihren eigenen Content-Provider erstellen. Befolgen Sie die folgenden Schritte:

Schritt Beschreibung
1 Verwenden Sie Android Studio, um eine Android-Anwendung zu erstellen, nennen Sie sie Content Provider, unter dem Paket com.example.contentprovider, und erstellen Sie eine leere Activity.
2 Ändern Sie die Hauptaktivitätsdatei MainActivity.java, um zwei neue Methoden hinzuzufügen, onClickAddName() und onClickRetrieveStudents().
3 Erstellen Sie eine neue Java-Datei StudentsProvider.java im Paket com.example.contentprovider, um den eigentlichen Provider und zugehörige Methoden zu definieren.
4 Verwenden Sie das <provider.../>-Tag, um den Content-Provider in AndroidManifest.xml zu registrieren.
5 Ändern Sie den Standardinhalt der Datei res/layout/activity_main.xml, um eine einfache Oberfläche zum Hinzufügen von Studentendatensätzen einzufügen.
6 Es ist nicht nötig, strings.xml zu ändern; Android Studio kümmert sich um die Datei strings.xml.
7 Starten Sie den Android-Emulator, um die Anwendung auszuführen und die Ergebnisse der an der Anwendung vorgenommenen Änderungen zu überprüfen.

Unten sehen Sie den Inhalt der geänderten Hauptaktivitätsdatei src/com.example.contentprovider/MainActivity.java. Diese Datei enthält jede der grundlegenden Lebenszyklusmethoden. Wir haben zwei neue Methoden hinzugefügt, onClickAddName() und onClickRetrieveStudents(), damit die Anwendung Benutzerinteraktionen verarbeiten kann.

package com.example.contentprovider;

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;
import com.example.contentprovider.R;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    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.provider.College/students";

        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());
        }
    }
}

Erstellen Sie eine neue Datei StudentsProvider.java im Paket com.example.contentprovider. Unten sehen Sie den Inhalt von src/com.example.contentprovider/StudentsProvider.java.

package com.example.contentprovider;

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.provider.College";
    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);
    }

    /**
     * 数据库特定常量声明
     */
    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);";

    /**
     * 创建和管理提供者内部数据源的帮助类.
     */
    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);

        /**
         * 如果不存在,则创建一个可写的数据库。
         */
        db = dbHelper.getWritableDatabase();
        return (db == null)? false:true;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        /**
         * 添加新学生记录
         */
        long rowID = db.insert( STUDENTS_TABLE_NAME, "", values);

        /**
         * 如果记录添加成功
         */

        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:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        if (sortOrder == null || sortOrder == ""){
            /**
             * 默认按照学生姓名排序
             */
            sortOrder = NAME;
        }
        Cursor c = qb.query(db, projection, selection, selectionArgs,null, null, sortOrder);

        /**
         * 注册内容URI变化的监听器
         */
        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)){
            /**
             * 获取所有学生记录
             */
            case STUDENTS:
                return "vnd.android.cursor.dir/vnd.example.students";

            /**
             * 获取一个特定的学生
             */
            case STUDENT_ID:
                return "vnd.android.cursor.item/vnd.example.students";

            default:
                throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
    }
}

Unten sehen Sie die geänderte Datei AndroidManifest.xml. Hier wurde das <provider.../>-Tag hinzugefügt, um unseren Content-Provider einzubinden:

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

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="22" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <activity
            android:name="com.example.contentprovider.MainActivity"
            android:label="@string/app_name" >

            <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.provider.College" >
        </provider>

    </application>

</manifest>

Unten sehen Sie den Inhalt der Datei res/layout/activity_main.xml:

<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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="内容提供者实例"
        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="www.example.com"
        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/ic_launcher.html"
        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="添加"
        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="姓名"
        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="年级"
        android:textColorHint="@android:color/holo_blue_bright" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="查询"
        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>

Stellen Sie sicher, dass die Datei res/values/strings.xml Folgendes enthält:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Content Provider</string>
    <string name="action_settings">Settings</string>

</resources>

Lassen Sie uns die Content-Provider-Anwendung ausführen, die wir gerade geändert haben. Ich gehe davon aus, dass Sie beim Einrichten der Umgebung bereits ein AVD erstellt haben. Öffnen Sie die Aktivitätsdatei in Ihrem Projekt und klicken Sie auf图片das Symbol, um die Anwendung in Android Studio auszuführen. Android Studio installiert die Anwendung auf dem AVD und startet sie. Wenn alles gut geht, wird Folgendes im Emulatorfenster angezeigt:

图片

Geben Sie einen Namen und eine Note ein und klicken Sie auf die Schaltfläche "Hinzufügen". Dadurch wird ein Studentendatensatz zu den Daten hinzugefügt und unten eine Nachricht angezeigt. Der Inhalt der Nachricht zeigt die Content-Provider-URI, die die Anzahl der zur Datenbank hinzugefügten Datensätze enthält. Dieser Vorgang verwendet die Methode insert(). Wiederholen Sie diesen Vorgang, um weitere Studenten zur Datenbank des Content-Providers hinzuzufügen.

图片

Sobald Sie mit dem Hinzufügen von Datenbankdatensätzen fertig sind, ist es Zeit, diese Datensätze vom Content-Provider zurückzufordern. Klicken Sie auf die Schaltfläche "Abfragen", die alle Datensätze über die implementierte Methode query() abruft und anzeigt.

Sie können Callback-Methoden in MainActivity.java bereitstellen, um Aktualisierungs- und Löschvorgänge zu schreiben, und die Benutzeroberfläche ändern, um Aktualisierungs- und Löschvorgänge hinzuzufügen.

Sie können vorhandene Content-Provider auf diese Weise verwenden, z. B. Contacts. Sie können diesen Ansatz auch nutzen, um eine hervorragende datenbankorientierte Anwendung zu entwickeln. Sie können alle Datenbankoperationen wie im oben eingeführten Beispiel ausführen, z. B. Lesen, Schreiben, Aktualisieren und Löschen.

Andere Erweiterungen