Android コンテンツ プロバイダーとコンテンツ オブザーバー: データ共有とリアルタイム更新の完璧な組み合わせ

ミッション要件

  • 1 つは連絡先データを提供する ContentProvider として
  • もう 1 つは、連絡先データの変更を監視するオブザーバーとして機能します。

1. ContactProvider プロジェクトを作成します。

2. ContactProvider プロジェクトの Sqlite データベースを使用して、連絡先の読み取りおよび書き込み機能を実装します。

3. ContactProvider プロジェクトの ContentProvider を通じて連絡先データを提供します。

4. ContactObserver プロジェクトを作成します。

5. ContentObserver を ContactObserver プロジェクトに登録して、連絡先データベースの変更を監視します。


ContactProvider プロジェクトや ContactObserver プロジェクトなどの Android 連絡先アプリケーションを作成する場合は、add、delete、modify、query メソッドと 2 つのページのレイアウト ファイルを実装する必要があります。詳しい手順は次のとおりです。

ステップ 1: ContactProvider プロジェクトを作成する

  1. ContactProvider という名前の新しい Android プロジェクトを作成します。

  2. ContactProvider プロジェクトで、データベース テーブル構造とコンテンツ プロバイダーの URI を定義する ContactContract という名前の Java クラスを作成します。

package com.leo.contactprovider;

import android.net.Uri;
import android.provider.BaseColumns;

public class ContactContract {
    
    
    public static final String AUTHORITY = "com.leo.contactprovider";
    public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY);

    public static final String PATH_CONTACTS = "contacts";

    public static final class ContactEntry implements BaseColumns {
    
    
        public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(PATH_CONTACTS).build();

        public static final String TABLE_NAME = "contacts";
        public static final String COLUMN_NAME = "name";
        public static final String COLUMN_PHONE = "phone";
    }
}}
  1. 連絡先データベースを作成および管理するための ContactDbHelper という名前のデータベース ヘルパー クラスを作成します。
package com.example.contactprovider;
package com.leo.contactprovider;

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

public class ContactDbHelper extends SQLiteOpenHelper {
    
    
    private static final String DATABASE_NAME = "contacts.db";
    private static final int DATABASE_VERSION = 1;

    public ContactDbHelper(Context context) {
    
    
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    
    
        final String SQL_CREATE_CONTACTS_TABLE = "CREATE TABLE " +
                ContactContract.ContactEntry.TABLE_NAME + " (" +
                ContactContract.ContactEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                ContactContract.ContactEntry.COLUMN_NAME + " TEXT NOT NULL, " +
                ContactContract.ContactEntry.COLUMN_PHONE + " TEXT NOT NULL" +
                ");";

        db.execSQL(SQL_CREATE_CONTACTS_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
    
        db.execSQL("DROP TABLE IF EXISTS " + ContactContract.ContactEntry.TABLE_NAME);
        onCreate(db);
    }
}
  1. ContactProvider という名前のコンテンツ プロバイダー クラスを作成し、連絡先データの追加、削除、変更、確認の機能を実装します。

「新規」—> 「その他」—> 「コンテンツプロバイダー」
ここに画像の説明を挿入します
ここに画像の説明を挿入します

package com.leo.contactprovider;

import static com.leo.contactprovider.ContactContract.AUTHORITY;

import android.content.ContentProvider;
import android.content.ContentValues;
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;

public class ContactProvider extends ContentProvider {
    
    
    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    // Define integer constants for the URIs
    private static final int CONTACTS = 100;
    private static final int CONTACT_ID = 101;

    static {
    
    
        uriMatcher.addURI(AUTHORITY, "contacts", CONTACTS);
        uriMatcher.addURI(AUTHORITY, "contacts/#", CONTACT_ID);
    }
    private ContactDbHelper dbHelper;

    @Override
    public boolean onCreate() {
    
    
        dbHelper = new ContactDbHelper(getContext());
        return true;
    }

    // 实现数据的增删改查方法
    @Override
    public Uri insert(Uri uri, ContentValues values) {
    
    
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        long id = db.insert(ContactContract.ContactEntry.TABLE_NAME, null, values);
        if (id > 0) {
    
    
            getContext().getContentResolver().notifyChange(uri, null);
            return ContactContract.ContactEntry.CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
        }
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
    
    
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int rowsDeleted;

        switch (uriMatcher.match(uri)) {
    
    
            case CONTACTS:
                rowsDeleted = db.delete(ContactContract.ContactEntry.TABLE_NAME, selection, selectionArgs);
                break;
            case CONTACT_ID:
                String contactId = uri.getLastPathSegment();
                rowsDeleted = db.delete(ContactContract.ContactEntry.TABLE_NAME,
                        ContactContract.ContactEntry._ID + "=?", new String[]{
    
    contactId});
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }

        if (rowsDeleted > 0) {
    
    
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return rowsDeleted;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    
    
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int rowsUpdated;

        switch (uriMatcher.match(uri)) {
    
    
            case CONTACTS:
                rowsUpdated = db.update(ContactContract.ContactEntry.TABLE_NAME, values, selection, selectionArgs);
                break;
            case CONTACT_ID:
                String contactId = uri.getLastPathSegment();
                rowsUpdated = db.update(ContactContract.ContactEntry.TABLE_NAME, values,
                        ContactContract.ContactEntry._ID + "=?", new String[]{
    
    contactId});
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }

        if (rowsUpdated > 0) {
    
    
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return rowsUpdated;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    
    
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor;

        switch (uriMatcher.match(uri)) {
    
    
            case CONTACTS:
                cursor = db.query(ContactContract.ContactEntry.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case CONTACT_ID:
                String contactId = uri.getLastPathSegment();
                cursor = db.query(ContactContract.ContactEntry.TABLE_NAME, projection,
                        ContactContract.ContactEntry._ID + "=?", new String[]{
    
    contactId}, null, null, sortOrder);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }

        // Set notification URI on the cursor
        cursor.setNotificationUri(getContext().getContentResolver(), uri); // 添加这行代码

        return cursor;
    }


    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
    
    
        return null;
    }

    // ...
}
  1. UriMatcher: UriMatcher は、受信 URI を照合して実行する操作 (クエリ、挿入、削除、更新など) を決定するために使用されます。 。コードは、静的初期化ブロックを使用して uriMatcher を初期化し、2 つの異なるタイプの URI (「contacts」と「contacts/#」) に定数の整数を割り当てます。

  2. onCreate メソッド: これはコンテンツ プロバイダーの初期化メソッドです。ここでは、データベース ヘルパー クラス (ContactDbHelper) を初期化し、それをコンテンツ プロバイダーに関連付けます。コンテンツプロバイダーの初期化は、アプリケーションの起動時に行われます。

  3. insert メソッド: これはデータの挿入に使用されるメソッドです。アプリケーションがコンテンツ プロバイダーを通じて新しいデータを挿入すると、 insert メソッドはデータベースを開いて挿入を実行し、 notifyChange を使用してデータに関心のあるコンテンツ オブザーバーに通知します。

  4. delete メソッド: このメソッドはデータを削除するために使用されます。 URI をチェックし、URI のタイプに基づいて削除操作を実行します。一部の行が正常に削除されると、notifyChange を使用してコンテンツ オブザーバーに通知します。

  5. update メソッド: データの更新に使用されます。 delete メソッドと同様に、URI をチェックし、適切な更新操作を実行し、notifyChange を使用してコンテンツ オブザーバーに通知します。

  6. クエリ メソッド: データのクエリに使用されます。これは、コンテンツ プロバイダーがデータを取得するために使用する最も一般的な方法です。受信 URI に基づいてクエリを実行し、setNotificationUri を使用して関連するコンテンツ オブザーバーに通知します。

  7. setNotificationUri: このメソッドはクエリ結果を特定の URI に関連付け、データが変更されたときにオブザーバーに通知します。

このコードは、挿入、削除、更新、クエリ操作の処理や、これらの操作の完了時の関連コンテンツ オブザーバーへの通知など、コンテンツ プロバイダーのコア機能をカバーしています。コンテンツ プロバイダーを使用すると、アプリケーションがデータを共有し、コンテンツ オブザーバー パターンを通じてリアルタイムのデータ更新が可能になります。

  1. レイアウトファイルの作成 -contact_provider
    ここに画像の説明を挿入します
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btnAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add Contact"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true" />

    <Button
        android:id="@+id/btnDelete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Delete Contact"
        android:layout_below="@+id/btnAdd"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true" />

    <Button
        android:id="@+id/btnUpdate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Update Contact"
        android:layout_below="@+id/btnDelete"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true" />

    <Button
        android:id="@+id/btnQuery"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Query Contacts"
        android:layout_below="@+id/btnUpdate"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true" />

    <TextView
        android:id="@+id/textViewResult"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btnQuery"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="16dp"
        android:textSize="20sp" 
 />
</RelativeLayout>

効果を達成する
ここに画像の説明を挿入します

ContactProvider プロジェクトの各ボタンに適切な機能を追加します。

package com.leo.contactprovider;

import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    
    
    private TextView textViewResult;

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

        textViewResult = findViewById(R.id.textViewResult);

        Button btnAdd = findViewById(R.id.btnAdd);
        Button btnDelete = findViewById(R.id.btnDelete);
        Button btnUpdate = findViewById(R.id.btnUpdate);
        Button btnQuery = findViewById(R.id.btnQuery);

        btnAdd.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                // 添加联系人示例
                ContentValues values = new ContentValues();
                values.put(ContactContract.ContactEntry.COLUMN_NAME, "John Doe");
                values.put(ContactContract.ContactEntry.COLUMN_PHONE, "123-456-7890");
                Uri insertUri = getContentResolver().insert(ContactContract.ContactEntry.CONTENT_URI, values);
                textViewResult.setText("Contact added with URI: " + insertUri.toString());
            }
        });

        btnDelete.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                // 删除联系人示例
                String selection = ContactContract.ContactEntry.COLUMN_NAME + " = ?";
                String[] selectionArgs = {
    
    "John Doe"};
                int deletedRows = getContentResolver().delete(ContactContract.ContactEntry.CONTENT_URI, selection, selectionArgs);
                textViewResult.setText("Deleted " + deletedRows + " contacts.");
            }
        });

        btnUpdate.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                // 更新联系人示例
                ContentValues values = new ContentValues();
                values.put(ContactContract.ContactEntry.COLUMN_PHONE, "987-654-3210");
                String selection = ContactContract.ContactEntry.COLUMN_NAME + " = ?";
                String[] selectionArgs = {
    
    "John Doe"};
                int updatedRows = getContentResolver().update(ContactContract.ContactEntry.CONTENT_URI, values, selection, selectionArgs);
                textViewResult.setText("Updated " + updatedRows + " contacts.");
            }
        });

        btnQuery.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View view) {
    
    
                // 查询联系人示例
                String[] projection = {
    
    
                        ContactContract.ContactEntry._ID,
                        ContactContract.ContactEntry.COLUMN_NAME,
                        ContactContract.ContactEntry.COLUMN_PHONE
                };
                Cursor cursor = getContentResolver().query(ContactContract.ContactEntry.CONTENT_URI, projection, null, null, null);

                StringBuilder result = new StringBuilder();
                while (cursor.moveToNext()) {
    
    
                    String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactContract.ContactEntry.COLUMN_NAME));
                    String phone = cursor.getString(cursor.getColumnIndexOrThrow(ContactContract.ContactEntry.COLUMN_PHONE));
                    result.append("Name: ").append(name).append(", Phone: ").append(phone).append("\n");
                }

                cursor.close();
                textViewResult.setText(result.toString());
            }
        });
    }
}


全体的な導入効果
画像の説明を追加してください

ステップ 2: ContactObserver プロジェクトを作成する

  1. 新しい Android プロジェクトを作成し、ContactObserver という名前を付けます。

  2. 監視対象のコンテンツを表示するためのレイアウト ファイルを 作成activity_main.xml します。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textViewObserver"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Content Observer Output" />
</RelativeLayout>

ここに画像の説明を挿入します

  1. 監視対象のコンテンツを表示するには、作成MainActivity.javaします。
package com.leo.contactobserver;

import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class ContactObserverActivity extends AppCompatActivity {
    
    
    private TextView textViewObserver;
    private ContentObserver contentObserver;

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

        textViewObserver = findViewById(R.id.textViewObserver);

        // 创建 ContentObserver 实例
        contentObserver = new ContentObserver(new Handler()) {
    
    
            @Override
            public void onChange(boolean selfChange, Uri uri) {
    
    
                super.onChange(selfChange, uri);
                // 处理内容变化时的逻辑
                Log.i("Content Changed: ", "URI: " + uri.toString());
                Log.i("Content Changed: ", "Self Change: " + selfChange);

                // 你可以在这里更新 textViewObserver 中的内容
                textViewObserver.setText("Content Changed: " + uri.toString());
            }

        };
    }

    @Override
    protected void onResume() {
    
    
        super.onResume();
        // 注册 ContentObserver 监听内容变化
        getContentResolver().registerContentObserver(
                Uri.parse("content://com.leo.contactprovider/contacts"),
                true, contentObserver);
    }

    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        // 在活动销毁时取消注册 ContentObserver
        getContentResolver().unregisterContentObserver(contentObserver);
    }

}


以下に重要な部分を説明し、強調表示します。

  1. ContentObserver:ContentObserver は、特定の URI でのデータ変更を監視するために使用されるクラスです。コードでは、ContentObserver のインスタンスを作成して、特定のコンテンツ プロバイダーのデータ変更を監視します。

  2. onChange メソッド: onChange メソッドは、監視対象の URI のデータが変更されたときの ContentObserver のコールバック メソッドです。 . と呼ばれます。このメソッド内で、データ変更のロジックを処理できます。コードでは、Log を通じて URI の変更と selfChange の値が記録されます。

  3. onResume メソッド: onResume メソッドでは、特定の URI でコンテンツの変更をリッスンするために ContentObserver が登録されます。 。こうすることで、 ContentObserver はアクティビティがフォアグラウンドにある場合にのみ有効になります。

  4. onDestroy メソッド: onDestroy メソッドでは、ContentObserver の登録がキャンセルされます。アクティビティが破棄されたときにコンテンツの変更をリッスンする必要がないためです。登録を解除すると、メモリ リークの可能性を回避できます。

工作原理

  • コンテンツ プロバイダのデータが変更されると、コンテンツ プロバイダは内部的に ContentResolvernotifyChange メソッドを呼び出し、登録されているすべてのリスナーに通知します /span>ContentObserver
  • ContentObserver は、onChange コールバック メソッドをトリガーします。これには、変更の URI と、変更が単独で開始されたかどうかを示すフラグ (selfChange) が含まれます。
  • UI の更新やログの記録など、onChange メソッドでデータ変更を処理できます。
  1. 設定ファイルを変更する

クエリリスニング権限を追加する

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

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.DayNight"
        tools:targetApi="31">

        <activity
            android:name=".ContactObserverActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.AppCompat.DayNight">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

    <queries>
        <provider android:authorities="com.leo.contactprovider"
            android:exported="true" />
    </queries>


</manifest>

ContactObserverService が ContentObserver を適切に登録および登録解除し、ブロードキャスト メッセージを送受信していることを確認してください。

効果を達成する

プロバイダーはデータを変更し、オブザーバーはデータの変更を監視します。

画像の説明を追加してください

おすすめ

転載: blog.csdn.net/qq_22841387/article/details/133708103