Android ContentProvider の使用


序文

ContentProvider を理解するために必要な最善の方法は、Android の公式ドキュメントである
ContentProvider Basicsを確認することです。

通常、ContentProvider には 2 つの使用シナリオがあります

  1. 1 つは、他のアプリケーションの既存のコンテンツ プロバイダーにアクセスするコードを実装することです。
  2. もう 1 つは、アプリケーション内に新しいコンテンツ プロバイダーを作成して、他のアプリケーションとデータを共有することです。

1. ContentProvider とは?

コンテンツ プロバイダは、リレーショナル データベースのテーブルと同様に、1 つ以上のテーブルの形式で外部アプリケーションにデータを提示します。

行は、プロバイダーによって収集された何らかのタイプのデータのインスタンスを表し、行の各列は、インスタンスに対して収集された単一のデータを表します。

コンテンツ プロバイダーは、アプリが自身や他のアプリによって保存されたデータへのアクセスを管理し、他のアプリとデータを共有する方法を提供するのに役立ちます。

コンテンツ プロバイダーは、あるプロセスのデータを別のプロセスで実行されているコードに接続する標準インターフェイスです。コンテンツ プロバイダーを構成することで、他のアプリケーションがアプリケーション データに安全にアクセスして変更できるようになります。

内容提供程序如何管理存储空间访问的概览图
コンテンツ プロバイダーがストレージ アクセスを管理する方法の概要図

2.ステップを使用する

1. アクセス方法

コンテンツ プロバイダーのデータにアクセスするために、クライアントはアプリケーションの Context でContentResolverオブジェクトを使用してプロバイダーと通信できます。

ContentResolver オブジェクトは、プロバイダー オブジェクト (つまり、ContentProvider を実装するクラスのインスタンス) と通信します。

ContentResolver メソッドは、永続ストレージの基本的な「CRUD」(作成、取得、更新、および削除) 機能を提供します。

2.ContentProvider を作成する

2.1. ContentProvider をビルドする手順

1、为数据设计原始存储
コンテンツ プロバイダーは、次の 2 つの方法でデータを提供します。

  • ファイル データ: 通常、写真、オーディオ、ビデオなどのファイルに保存されるデータ。アプリのプライベート スペースにファイルを保存します。プロバイダーは、他のアプリケーションからのファイル要求に応じてファイル ハンドルを提供できます。
  • 「構造化された」データ: 通常、データベース、配列、または同様の構造に格納されるデータ。

2、定义 ContentProvider 类及其所需方法的具体实现。
3、定义提供程序的授权字符串、该字符串的内容 URI 以及列名称。

2.2. Uri讲解

1. コンテンツ URI

コンテンツ URI は、プロバイダー内のデータを識別するために使用されます。コンテンツ URI は、プロバイダ全体の記号名 (その機関) とポインティング テーブルの名前 (パス) で構成されます。

ContentResolver オブジェクトは、URI の権限を解決し、それを既知のプロバイダーのシステム テーブルと比較することによって、プロバイダーを "解決" します。

ContentProvider は、コンテンツ URI のパス部分を使用して、アクセスするテーブルを選択します。通常、プロバイダーは公開する各テーブルのパスを公開します。

content://user_dictionary/words

user_dictionary文字列はプロバイダーの承認でありwords文字列はテーブルへのパスです。

文字列 content:// (スキーマ) は常に存在し、コンテンツ URI として識別されます。

2. URI を設計する

1. 設計承認
プロバイダーは通常、Android 内部名として機能する単一の承認を持っています。他のプロバイダーとの競合を避けるために、インターネット ドメインの所有権 (リバース) をプロバイダーの承認の基礎として使用する必要があります

例: Android パッケージ名が com.example.appname の場合、プロバイダーにはcom.example.appname.provider承認を与える必要があります。

2. パス構造の設計
通常、開発者は個々のテーブルを指すパスを追加して、権限に基づいてコンテンツ URI を作成します。

たとえば、table1 と table2 という 2 つのテーブルがある場合、前の例の承認を組み合わせて、コンテンツ URI
com.example.appname.provider/table1 と com.example.appname.provider/table2を生成できます。

3. コンテンツ URI ID の処理
合意により、プロバイダーは末尾に行 ID 値を持つコンテンツ URI を受け入れ、テーブル内の 1 つの行へのアクセスを提供します。
また、慣例により、プロバイダーはこの ID 値をテーブルの _ID 列と照合し、一致する行に対して要求されたアクセスを実行します。

4. コンテンツ URI パターン
着信コンテンツ URI の処理方法を選択しやすくするために、プロバイダー API は、コンテンツ URI の「パターン」を整数値にマップする便利なクラスUriMatcherを追加します。これらの整数値を switch ステートメントで使用して、特定のパターンに一致する 1 つ以上のコンテンツ URI に対して目的のアクションを選択できます。

4.1. コンテンツ URI モードでは、次のワイルドカードを使用してコンテンツ URI に一致させます

  • * :任意の長さの有効な文字で構成される文字列に一致します
  • # :任意の長さの数字からなる文字列に一致

4.2. コンテンツ URI 処理の設計とエンコードを例にとると、com.example.app.provider 権限を持つプロバイダーは、テーブルを指す次のコンテンツ URI を認識できると仮定します。

  • content://com.example.app.provider/table1: table1 という名前のテーブル
  • content://com.example.app.provider/table2/dataset1: dataset1 という名前のテーブル
  • content://com.example.app.provider/table2/dataset2: dataset2 という名前のテーブル
  • content://com.example.app.provider/table3: table3 という名前のテーブル

プロバイダーは、行 ID が追加されたコンテンツ URI も認識します。
content://com.example.app.provider/table3/1: table3 の 1 で識別される行のコンテンツ URI。

4.3. 次のコンテンツ URI パターンを使用できます

  • content://com.example.app.provider/ *:プロバイダー内の任意のコンテンツURI に一致します。
  • content://com.example.app.provider/table2/ *: 4.2 の内容によると、テーブル dataset1 およびテーブル dataset2 のコンテンツ URI には一致しますが、 table1 または table3 のコンテンツ URI には一致しません
  • content://com.example.app.provider/table3/#: table3 の単一行のコンテンツ URI に一致します。たとえば、content://com.example.app.provider/table3/6: コンテンツ URI に対応します。 6 で識別される行の

次のコード スニペットは、UriMatcher のメソッドがどのように機能するかを示しています。

このコードは、テーブル全体の URI を個々の行の URI とは異なる方法で为整张表使用的内容 URI 模式是 content://<authority>/<path>処理します单个行使用的内容 URI 模式则是 content://<authority>/<path>/<id>

addURI() メソッドは、認証とパスを整数値にマップしますmatch() メソッドは URI の整数値を返しますswitch ステートメントは、match メソッドによって返された整数値に応じて、テーブル全体またはクエリ テーブル内の 1 つのレコードのクエリを選択します。

例: 後続のコードとは関係ありません

// 1. Creates a UriMatcher object.
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

// 2. 添加URI到UriMatcher
static {
    
    
   // 访问table3整个表的内容,将1跟跟整个表的URI进行映射
   uriMatcher.addURI("com.example.app.provider", "table3", 1);
   // 访问table3中单个记录,#匹配由任意长度的数字字符组成的字符串
   // 将数字2跟单个记录访问的URI进行映射
   uriMatcher.addURI("com.example.app.provider", "table3/#", 2);
}

// 3. 对比Uri,然后进行各自正确的操作
switch (uriMatcher.match(uri)) {
    
    
    case 1:
        if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
      break;
    case 2:
         selection = selection + "_ID = " + uri.getLastPathSegment();
       break;
    default:
          // 如果条件不满足可以再次抛出异常提醒调用者
}

2.3. ContentProvider クラスを実装する

ContentProvider 实例会处理其他应用发送的请求,从而管理对结构化数据集的访问。

すべての形式のアクセスは、最終的に ContentResolver を呼び出します。これは、 ContentProvider で具体的なメソッドを呼び出すことによってアクセスを取得します

抽象クラスContentProvider は、具象サブクラスの一部として実装する必要がある 6 つの抽象メソッドを定義します。

query() は
プロバイダーからデータを取得します。パラメーターを使用して、クエリを実行するテーブル、返す行と列、および結果の並べ替え順序を選択します。データを Cursor オブジェクトとして返します。

query() パラメータ SELECT キーワード/パラメータ 述べる
ウリ FROM テーブル名 Uri は、プロバイダー内の table_name という名前のテーブルにマップされます。
投影 丘、丘、丘… プロジェクションは、取得された各行に含まれる列の配列です。
選択 WHERE 列 = 値 selection は、行を選択する基準を指定します。
選択引数 (正確に同等のものはありません。selection パラメーターは、selection 句の ? プレースホルダーを置き換えます。)
ソート順 ORDER BY col,col,… sortOrder は、返された Cursor 内の行の表示順序を指定します。

insert()
はプロバイダに新しい行を挿入します。パラメーターを使用してターゲット テーブルを選択し、使用する列の値を取得します。新しく挿入された行のコンテンツ URI を返します。

update() は、
プロバイダー内の既存の行を更新します。パラメーターを使用して、更新するテーブルと行を選択し、更新された列の値を取得します。更新された行数を返します。

delete() は
プロバイダーから行を削除します。パラメーターを使用して、削除するテーブルと行を選択します。削除された行数を返します。

getType() は、
コンテンツ URI に対応する MIME タイプを返します。

onCreate() は
プロバイダーを初期化します。Android システムは、プロバイダーが作成されるとすぐにこのメソッドを呼び出します。システムは、プロバイダーにアクセスしようとするときにのみ ContentResolver オブジェクトを作成することに注意してください。

予防:

  • これらのメソッド (onCreate() を除く) はすべて複数のスレッドから同時に呼び出すことができるため、スレッドセーフなメソッドである必要があります。
  • onCreate() での時間のかかる操作は避けてください。実際に必要になるまで、初期化タスクを延期します。
  • ContentProvider を継承するには、上記のすべてのメソッドを実装する必要がありますが、コードは必要な操作のみを実装する必要があり、他の操作を実行する必要はありません。たとえば、他のアプリケーションが特定のテーブルにデータを挿入できないようにすることができます。これを実現するには、insert() 呼び出しを無視して 0 を返します

3. onCreate() メソッドを実装する

Android システムは、プロバイダーの開始時にonCreate()を呼び出します。

この方法では、高速に実行される初期化タスクのみを実行しプロバイダーが実際にデータのリクエストを受け取るまで、データベースの作成とデータの読み込みを延期する必要があります。

onCreate() で長いタスクを実行すると、プロバイダーの起動が遅くなります。これにより、他のアプリケーションに対するプロバイダの応答性が低下します。

コードは次のとおりです (例)。
ここに画像の説明を挿入

4. クエリ

ContentProvider.query() メソッドは Cursor オブジェクトを返す必要があり、失敗した場合、システムは例外をスローします。

SQLite データベースをデータ ストアとして使用している場合は、SQLiteDatabase クラスの query() メソッドのいずれかによって返される Cursor を返すだけで済みます。

クエリがどの行とも一致しない場合は、Cursor インスタンス (getCount() メソッドが 0 を返す) を返す必要があります。クエリ中に内部エラーが発生した場合にのみ null を返す必要があります。

Android システムは、プロセスの境界を越えて例外を伝達できる必要があります。

  • IllegalArgumentException (プロバイダーが無効なコンテンツ URI を受け取ったときに、この例外をスローすることを選択できます)
  • NullPointerException

コードは次のとおりです (例)。
ここに画像の説明を挿入

5.挿入する

insert() メソッドは、ContentValues パラメータの値を使用して、対応するテーブルに新しい行を追加します列名が ContentValues パラメーターに含まれていない場合は、プロバイダー コードまたはデータベース スキーマでその既定値を指定することができます。

このメソッドは、新しい行のコンテンツ URI を返す必要があります。このメソッドを作成するには、withAppendedId()を使用して、新しい行の _ID (または他の主キー) 値をテーブルのコンテンツ URI に追加します。

コードは次のとおりです (例)。
ここに画像の説明を挿入

6.修正

update() メソッドは、insert() と同じ ContentValues パラメーターを受け取り、このメソッドは、delete() および ContentProvider.query() と同じ selection および selectionArgs パラメーターを受け取ります。

コードは次のとおりです (例)。
ここに画像の説明を挿入

7. 削除

delete() メソッドは、実際にデータ ストアから行を削除する必要はありません。 如果您将同步适配器与提供程序一起使用,则应考虑为已删除的行添加“删除”标志,而不是完全移除行。同步适配器可以检查是否存在已删除的行,并将这些行从服务器中移除,然后再将其从提供程序中删除。

コードは次のとおりです (例)。
ここに画像の説明を挿入

8. コンテンツ プロバイダーの MIME タイプを実装する (getType())

ContentProvider クラスには、MIME タイプを返す 2 つのメソッドがあります。

  • getType():任何ContentProvider程序都必须实现。
  • getStreamTypes():当提供程序提供文件时,系统要求您实现的方法。

8.1 テーブルの MIME タイプ

テーブル データの 1 つ以上の行を指すコンテンツ URIの場合、getType() は MIME タイプを Android ベンダー固有の MIME 形式で返す必要があります。

  • 类型部分:vnd
  • 子类型部分:
    • 単一の行に URI パターンを使用する場合: android.cursor.item/
    • URI パターンが複数の行に使用されている場合: android.cursor.dir/
  • 提供程序特有部分:vnd.<name>.<type>
    <name> および を提供します<type><name>値はグローバルに一意である必要があり、<type> 値は対応する URI パターン内で一意である必要があります。会社名やアプリの Android パッケージ名の一部を選択するのに適しています <name>URI 関連付けテーブルの選択に適した識別文字列<type>

プロバイダーの承認がcom.example.app.provider ( <type>) で、<name>table1( ) という名前のテーブルを公開している場合。

Type.Subtype セクション / コンテンツ プロバイダー固有のセクション

  • table1 の行の MIME タイプは次のとおりです。
// 类型.子类型部分/内容提供者特有部分
vnd.android.cursor.dir/vnd.com.example.provider.table1
  • MIME タイプの table1 の単一行:
vnd.android.cursor.item/vnd.com.example.provider.table1

コードは次のとおりです(例)

@Override
    public String getType(@NonNull Uri uri) {
    
    
        int match = sUriMatcher.match(uri);
        switch (match) {
    
    
            // 1. 必须以vnd开头
            // 2. 如果内容URI以路径结尾,则后接android.cursor.dir/,
            // 如果内容URI以id结尾,则后接android.cursor.item/
            // 3. 最后接上vnd.<authority>.<path>
            case GESTURE_DIR:
                return "vnd.android.cursor.dir/vnd." + AUTHORITY + ".gesture";
            case GESTURE_ITEM:
                return "vnd.android.cursor.item/vnd." + AUTHORITY + ".gesture";
            default:
                throw new IllegalArgumentException(String.format("Unknown URI: %s", uri));
        }
    }

8.1 ファイルの MIME タイプ

  • ContentProvider プログラムが写真画像を.jpg、.png、および .gif形式のファイルとして提供する場合。アプリケーションがContentResolver.getStreamTypes()を呼び出すときにフィルター文字列を使用する場合image/*(任何“图像”内容)、 ContentProvider.getStreamTypes() メソッドは配列を返す必要があります。
{
    
     "image/jpeg", "image/png", "image/gif"}
  • ContentProvider が.jpgファイルのみに関心がある場合は、ContentResolver.getStreamTypes()を呼び出すときにフィルター文字列を使用でき*\/jpeg、ContentProvider.getStreamTypes() は次を返す必要があります。
{
    
    "image/jpeg"}

9. ContentProvider にパーミッションを追加する

Android の公式 Web サイトでは、ContentProvider: Implementing content provider permissionsを提供しています。

一般的に使用される 2 つのものについて簡単に説明します (詳細については、上記の公式 Web サイトのリンクを参照してください)。

  • 统一的读写提供程序级权限
    provider 全体への読み取りアクセスと書き込みアクセスの両方を制御するパーミッション (<provider> 要素 。

例えば:

com.example.app.provider.permission.READ_PROVIDER
  • 单独的读写提供程序级权限
    プロバイダー全体の読み取りおよび書き込み権限これらのアクセス許可は、要素の属性と属性<provider>を使用して指定します。これらのパーミッションは、 android:permission で必要なパーミッションよりも優先されますandroid:readPermissionandroid:writePermission

  • パス レベルのアクセス許可一時的なアクセス許可もあります。次を参照してください:コンテンツ プロバイダーのアクセス許可の実装

9. マニフェスト ファイルで ContentProvider を構成する

Authorization (android:authorities) は、
システム内の ContentProvider 全体を識別するために使用される記号名です。

プロバイダー クラス名 ( android:name )
によって実装される ContentProvider へのフル パス。
例えば:com.example.app.provider.MyContentProvider

アクセス許可: 他のアプリケーションがプロバイダー データにアクセスするために必要なアクセス許可を指定します。

  • android:grantUriPermssions: 一時的な許可フラグ。
  • android:permission: 統一されたプロバイダ全体の読み取り/書き込み権限。
  • android:readPermission: プロバイダー全体の読み取り許可。
  • android:writePermission: プロバイダー全体の書き込み権限。

起動と制御のプロパティ

  • android:enabled: システムがプロバイダーを開始できるようにするフラグ。
  • android:exported: 他のアプリがこのプロバイダーを使用できるようにするフラグ。
  • android:initOrder: 同じプロセス内の他のプロバイダーに対するこのプロバイダーの起動順序。
  • android:multiProcess: システムが呼び出し元のクライアントと同じプロセスでプロバイダーを開始できるようにするフラグ。
  • android:process: プロバイダーが実行するプロセスの名前。
  • android:syncable: プロバイダーのデータがサーバー上のデータと同期されることを示すフラグ。

情報のプロパティ: プロバイダーのオプションのアイコンとラベル:

  • android:icon: プロバイダーのアイコンを含むドローアブル リソース。アイコンは、アプリケーション リスト ([設定] > [アプリ] > [すべて]) のプロバイダーのラベルの横に表示されます。
  • android:label: プロバイダーやそのデータを説明する情報ラベル。タブがアプリケーション リストに表示されます ([設定] > [アプリ] > [すべて])。

たとえば、AndroidManifest.xml で構成します。

<provider
     android:name="com.example.app.MyContentProvider"
     android:authorities="com.example.app.provider"
     android:enabled="true"
     android:exported="true"
android:permission="com.example.app.provider.permission.READ_PROVIDER" />

3. 完全な例:

1. SQLite を使用してデータベースを作成するだけです

package com.google.mediapipe.examples.hands.service;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import androidx.annotation.Nullable;

public class GestureSqliteHelper extends SQLiteOpenHelper {
    
    
    private static final String CREATE_GESTURE = "create table if not exists Gesture ("
            + "id integer primary key autoincrement,"
            + "gesture_switch integer,"
            + "gesture_result integer)";

    public GestureSqliteHelper(@Nullable Context context, @Nullable String name,
                               @Nullable SQLiteDatabase.CursorFactory factory, int version) {
    
    
        super(context, name, factory, version);
    }

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

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
    

    }
}

2. ContentProvider の完全な実装

package com.google.mediapipe.examples.hands.service;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.List;

public class GestureContentProvider extends ContentProvider {
    
    
    private static final String TAG = "GestureContentProvider";
    private static final String DB_NAME = "Hands.db";
    private static final String TABLE_GESTURE = "Gesture";
    private static final int GESTURE_DIR = 0;
    private static final int GESTURE_ITEM = 1;
    private static final String AUTHORITY = "com.google.mediapipe.examples.hands.provider";
    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
    
    
        sUriMatcher.addURI(AUTHORITY, "gesture", GESTURE_DIR);
        sUriMatcher.addURI(AUTHORITY, "gesture/#", GESTURE_ITEM);
    }

    private GestureSqliteHelper mDbHelper;

    @Override
    public boolean onCreate() {
    
    
        Context context = getContext();
        if (context == null) {
    
    
            return false;
        }
        mDbHelper = new GestureSqliteHelper(context, DB_NAME, null, 1);
        return true;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
    
    
        int match = sUriMatcher.match(uri);
        switch (match) {
    
    
            // 1. 必须以vnd开头
            // 2. 如果内容URI以路径结尾,则后接android.cursor.dir/,
            // 如果内容URI以id结尾,则后接android.cursor.item/
            // 3. 最后接上vnd.<authority>.<path>
            case GESTURE_DIR:
                return "vnd.android.cursor.dir/vnd." + AUTHORITY + ".gesture";
            case GESTURE_ITEM:
                return "vnd.android.cursor.item/vnd." + AUTHORITY + ".gesture";
            default:
                throw new IllegalArgumentException(String.format("Unknown URI: %s", uri));
        }
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
    
    
        // 查询数据
        SQLiteDatabase database = mDbHelper.getReadableDatabase();
        Cursor cursor = null;
        switch (sUriMatcher.match(uri)) {
    
    
            case GESTURE_DIR:
                cursor = database.query(TABLE_GESTURE, projection, selection, selectionArgs,
                        null, null, sortOrder);
                cursor.setNotificationUri(getContext().getContentResolver(), uri);
                break;
            case GESTURE_ITEM:
                List<String> segments = uri.getPathSegments();
                if (segments.size() < 2) {
    
    
                    return null;
                }
                String gestureId = segments.get(1);
                cursor = database.query(TABLE_GESTURE, projection, "id=?", new String[]{
    
    gestureId},
                        null, null, sortOrder);
                cursor.setNotificationUri(getContext().getContentResolver(), uri);
                break;
        }
        return cursor;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
    
    
        SQLiteDatabase database = mDbHelper.getWritableDatabase();
        Uri uriReturn = null;
        switch (sUriMatcher.match(uri)) {
    
    
            case GESTURE_DIR:
            case GESTURE_ITEM:
                long gestureId = database.insert(TABLE_GESTURE, null, values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/gesture/" + gestureId);
                getContext().getContentResolver().notifyChange(uriReturn, null);
                break;
        }
        return uriReturn;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
    
    
        SQLiteDatabase database = mDbHelper.getWritableDatabase();
        int deleteRows = 0;
        switch (sUriMatcher.match(uri)) {
    
    
            case GESTURE_DIR:
                deleteRows = database.delete(TABLE_GESTURE, selection, selectionArgs);
                getContext().getContentResolver().notifyChange(uri, null);
                break;
            case GESTURE_ITEM:
                List<String> segments = uri.getPathSegments();
                if (segments.size() < 2) {
    
    
                    return deleteRows;
                }
                String gestureId = segments.get(1);
                deleteRows = database.delete(TABLE_GESTURE, "id=?", new String[]{
    
    gestureId});
                getContext().getContentResolver().notifyChange(uri, null);
                break;
        }
        return deleteRows;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
    
    
        SQLiteDatabase database = mDbHelper.getWritableDatabase();
        int updateRows = 0;
        switch (sUriMatcher.match(uri)) {
    
    
            case GESTURE_DIR:
                updateRows = database.update(TABLE_GESTURE, values, selection, selectionArgs);
                getContext().getContentResolver().notifyChange(uri, null);
                break;
            case GESTURE_ITEM:
                List<String> segments = uri.getPathSegments();
                if (segments.size() < 2) {
    
    
                    return updateRows;
                }
                String gestureId = segments.get(1);
                updateRows = database.update(TABLE_GESTURE, values, "id=?", new String[]{
    
    gestureId});
                getContext().getContentResolver().notifyChange(uri, null);
                break;
        }
        return updateRows;
    }
}

3. ContentProvider のクライアント使用

定数 URI とフィールド名

private static final String TAB_NAME = "gesture";
private static final String AUTHORITY = "com.google.mediapipe.examples.hands.provider";
public static final Uri URI_GESTURE_SWITCH_DIR = Uri.parse("content://" + AUTHORITY + "/" + TAB_NAME);
public static final Uri URI_GESTURE_SWITCH_ITEM = Uri.parse("content://" + AUTHORITY + "/" + TAB_NAME + "/1");

// 数据库字段名
public static final String COLUMN_ID = "id";
public static final String COLUMN_GESTURE_SWITCH = "gesture_switch";

ContentProvider を介してデータを挿入する

// 通过ContentProvider插入数据
private void providerInsert() {
    
    
        runDatabase(() -> {
    
    
            ContentValues values = new ContentValues();
            // 数据库字段名+当前字段值
            values.put(COLUMN_GESTURE_SWITCH, 0);
            Uri insertUri = getContentResolver().insert(URI_GESTURE_SWITCH_DIR, values);
            Log.d(TAG, "providerInsert:: " + insertUri);
        });
    }

ContentProvider を介してデータを更新する

private void providerUpdate() {
    
    
        runDatabase(() -> {
    
    
            ContentValues values = new ContentValues();
            values.put(COLUMN_GESTURE_SWITCH, 0);
            int updateId = getContentResolver().update(URI_GESTURE_SWITCH_ITEM, values, null,
                    null);
            Log.d(TAG, "providerUpdate:: " + updateId);
        });
    }

ContentProvider を介してデータをクエリする

private void providerQuery() {
    
    
        runDatabase(() -> {
    
    
            Cursor cursor = getContentResolver().query(URI_GESTURE_SWITCH_DIR, null, null,
                    null, null);
            if (cursor != null) {
    
    
                while (cursor.moveToNext()) {
    
    
                    long id = cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_ID));
                    int switchStatus = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_GESTURE_SWITCH));
                    Log.d(TAG, "providerQuery id:: " + id + " ,switchStatus:: " + switchStatus);
                }
                cursor.close();
            }
        });
    }

ContentProvider を介してデータを削除する

private void providerDelete() {
    
    
        runDatabase(() -> {
    
    
            int deleteId = getContentResolver().delete(URI_GESTURE_SWITCH_ITEM, null, null);
            Log.d(TAG, "providerDelete:: " + deleteId);
        });
    }

要約する

以上がこの記事の内容です. この記事は ContentProvider の概念と使い方を簡単に紹介しただけです. ContentProvider の使い方についてもっと知りたい場合は, Android の公式ドキュメントを確認してください.
作成済みの ContentProvider は使用済みです

参考:
ContentProvider で
Android について説明 - コンテンツ プロバイダー (Content Provider)

おすすめ

転載: blog.csdn.net/u013855006/article/details/124363014