ContentProvider学习

定义:四大组件之一

参考博客:https://blog.csdn.net/carson_ho/article/details/76101093

既可跨进程通信,也可进程内通信,底层原理使用binder;它其实类似于一种中间件如下图所示,只是对数据源进行了一层封装,解耦了访问者和底层数据,使得底层数据可以多样化而不影响上层的使用。

 

如上图所示,contentProvider使用上包括四块:

 1,统一资源标识符(URI):外界进程通过 URI 找到对应的ContentProvider & 其中的数据,再进行数据操作;

     URI分为 系统预置 & 自定义,分别对应系统内置的数据(如通讯录、日程表等等)和自定义数据库。

 

2,MIME数据类型

     作用:指定某个扩展名的文件用某种应用程序来打开, 由2部分组成 = 类型 + 子类型

     text / html     类型 = text、子类型 = html,表示.html文件用text应用程序打开。

3,ContentProvider类主要以 表格的形式 组织数据;

     进程间共享数据的本质是:添加、删除、获取 & 修改(更新)数据, ContentProvider核心方法也主要是上述4个作用。

4,ContentResolver类

      统一管理不同 ContentProvider间的操作,即通过 URI 即可操作不同的ContentProvider 中的数据,外部进程通过ContentResolver类从而与ContentProvider类进行交互。

5,ContentUris类:操作 URI

     核心方法有两个:withAppendedId()&parseId()

6,UriMatcher类

    在ContentProvider 中注册URI,根据 URI 匹配 ContentProvider 中对应的数据表。

7,ContentObserver类

     观察 Uri引起 ContentProvider 中的数据变化 & 通知外界(即访问该数据访问者)

     当ContentProvider 中的数据发生变化(增、删 & 改)时,就会触发该 ContentObserver类。

 进程一即继承ContentProvider的类:

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

    <permission android:name="com.example.PROVIDER"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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=".MyProvider"
            android:authorities="com.example.provider"
            android:permission="com.example.PROVIDER"
            android:exported="true"
            />
    </application>

</manifest>
package com.example.contentproviderserver;

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;


public class MyProvider extends ContentProvider {
    private Context mContext;
    private DbHelper mDbHelper;
    private SQLiteDatabase mDb;

    private static final String AUTHORITY = "com.example.provider";
    private static final int USER_CODE = 1;
    private static final int JOB_CODE = 2;
    private static final UriMatcher mUriMatcher;
    static {
        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mUriMatcher.addURI(AUTHORITY, "user", USER_CODE);
        mUriMatcher.addURI(AUTHORITY, "job", JOB_CODE);
    }

    @Override
    public boolean onCreate() {
        mContext = getContext();
        mDbHelper = new DbHelper(mContext);
        mDb = mDbHelper.getWritableDatabase();

        mDb.execSQL("delete from user");
        mDb.execSQL("insert into user values(1, 'wang')");
        mDb.execSQL("insert into user values(2, 'feng')");

        mDb.execSQL("delete from job");
        mDb.execSQL("insert into job values(1, 'android')");
        mDb.execSQL("insert into job values(2, 'ios')");

        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        String table = getTableName(uri);
        Cursor query = mDb.query(table, projection, selection, selectionArgs, null, null, sortOrder, null);

        return query;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        String table = getTableName(uri);
        mDb.insert(table, null, contentValues);
        mContext.getContentResolver().notifyChange(uri, null);

        return uri;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        String table = getTableName(uri);
        int delete = mDb.delete(table, selection, selectionArgs);

        return delete;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

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

    /**
     * 根据uri得到对应的表名
     * @param uri
     * @return
     */
    private String getTableName(Uri uri) {
        String tabName = null;

        switch (mUriMatcher.match(uri)) {
            case USER_CODE:
                tabName = DbHelper.TB_USER_NAME;
                break;
            case JOB_CODE:
                tabName = DbHelper.TB_JOB_NAME;
                break;
            default:
                break;
        }
        return tabName;
    }
}

进程二中数据访问者:

其中registerContentObserver() 方法:

  • 第一个参数:需要监听的 uri。
  • 第二个参数:为 false 表示精确匹配,即只匹配该 Uri。为 true 表示可以同时匹配其派生的 Uri,如:
    content://com.example.provider/student(精确匹配)
    content://com.example.provider/student/# (派生,false 才能匹配到)
    content://com.example.provider/student/schoolchild(派生,false 才能匹配到)
  • 如果该值为true,则所有地址包含此uri的数据项发生变化时都会触发通知。否则只有完全符合该uri地址的数据项发生变化时才会触发通知。以文件夹和其中的文件为例,若uri指向某文件夹,则需设置notifyForDescendents为true。即该文件夹下的任何文件发生变化,都需要通知监听者。
  • 第三个参数:ContentObserver 的实例。
public class ProviderActivity extends AppCompatActivity {
    private static final String TAG = "ProviderActivity";
    private static final int USER_OBSERVER = 1;
    private Handler mHandler;
    private ProviderObserver mObserver;

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

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getContentResolver().unregisterContentObserver(mObserver);
    }

    private void userOperation() {
        Uri userUri = Uri.parse("content://com.example.provider/user");
        ContentResolver resolver = getContentResolver();
        Cursor cursor = resolver.query(userUri, null, null, null, null);
        try {
            if (cursor != null) {
                cursor.moveToFirst();
                do {
                    int id = cursor.getInt(cursor.getColumnIndex("id"));
                    String name = cursor.getString(cursor.getColumnIndex("name"));
                    Log.d(TAG, "id: " + id + ", name: " + name);
                    cursor.moveToNext();
                } while (!cursor.isAfterLast());
            }
        } catch (Exception e) {
            Log.e(TAG, "error: " + e.getMessage());
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        mHandler = new Handler(getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (msg.what == USER_OBSERVER) {
                    Log.d(TAG, msg.obj.toString());
                }
            }
        };
        mObserver = new ProviderObserver(mHandler, getApplicationContext());
        getContentResolver().registerContentObserver(userUri, false, mObserver);
        ContentValues values = new ContentValues();
        values.put("id", 4);
        values.put("name", "observer");
        resolver.insert(userUri, values);
    }
}
public class ProviderObserver extends ContentObserver {
    private static final String TAG = "ProviderObserver";
    private static final int USER_OBSERVER = 1;
    private Handler mHandler;
    private Context mContext;

    public ProviderObserver(Handler handler, Context context) {
        super(handler);
        mHandler = handler;
        mContext = context;
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
        StringBuilder stringBuilder = new StringBuilder();
        Cursor cursor = mContext.getContentResolver().query(uri, null, null, null, null);
        try {
            if (cursor != null) {
                cursor.moveToFirst();
                do {
                    int id = cursor.getInt(cursor.getColumnIndex("id"));
                    String name = cursor.getString(cursor.getColumnIndex("name"));
                    stringBuilder
                            .append("change insert id: " + id + ", name: " + name)
                            .append("\n");
                    cursor.moveToNext();
                } while (!cursor.isAfterLast());
            }
        } catch (Exception e) {
            Log.e(TAG, "error: " + e.getMessage());
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        mHandler.obtainMessage(USER_OBSERVER, stringBuilder.toString()).sendToTarget();
    }
}

总结:

ContentProvider主要用于跨进程通信,也可以用在同一个进程中;它为应用间的数据交互提供了一个安全的环境:允许把自己的应用数据根据需求开放给 其他应用 进行 增、删、改、查,而不用担心因为直接开放数据库权限而带来的安全问题。

ContentProvider解耦了底层数据的存储方式,使得无论底层数据存储采用何种方式,外界对数据的访问方式都是统一的,这使得访问简单 、高效。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值