Android ContentProvider 知识点总结

来源:互联网 发布:软件项目管理课程设计 编辑:程序博客网 时间:2024/05/21 13:24

概述

ContentProvider:内容提供者,是Android四大组件之一,为其他app提供数据。其他app可以通过ContentResolver:内容解析器来增,删,查,改相关的数据。ContentProvider主要用于跨进程共享数据,它是一套标准的接口访问,其内部实现可以是SQLiteDatabase,文件,图片索引,媒体索引等。如果你不需要在多个应用间共享数据,可以使用SQLiteDatabase来实现。

ContentProvider简单使用

ContentProvider主要实现以下五个方法

  • onCreate:初始化provider时调用
  • insert:插入数据时调用
  • delete:删除数据时调用
  • query:用于查询数据
  • update:用于修改数据

ContentProvider内容提供者 简单示例实现如下:

/** * Created by xujinping on 2017/11/3. * ContentProvider 简单使用示例 */public class MyContentProvider extends ContentProvider {    private static final String TAG = "MyContentProvider";    private final static String DB_NAME = "my_demo.db";    private final String TABLE_NAME = "Demo";    private final int DB_VERSION = 2;    private SQLiteDatabase mDataBase;    private Context mContext;    private static UriMatcher matcher = new UriMatcher(NO_MATCH);    private final static String TABS[] = {            Contract.DEMO_PATH    };    static {        matcher.addURI(Contract.AUTHORITY, Contract.DEMO_PATH, 0);    }    @Override    public boolean onCreate() {        Log.i(TAG, "onCreate: ContentProvider");        mContext = getContext();        SQLDBHelper sqldbHelper = new SQLDBHelper(mContext);        mDataBase = sqldbHelper.getWritableDatabase();        return false;    }    @Nullable    @Override    public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {        String tabName = getTabName(uri);        if (TextUtils.isEmpty(tabName)) {            return null;        }        Cursor cursor = mDataBase.query(tabName, projection, selection, selectionArgs, null, null, sortOrder);        return cursor;    }    @Nullable    @Override    public String getType(@NonNull Uri uri) {        return null;    }    @Nullable    @Override    public Uri insert(@NonNull Uri uri, ContentValues values) {        String tabName = getTabName(uri);        if (TextUtils.isEmpty(tabName)) {            return null;        }        long id = mDataBase.insert(tabName, null, values);        Uri resultUri = null;        if (id != -1) {            resultUri = ContentUris.withAppendedId(uri, id);            ContentResolver resolver = mContext.getContentResolver();            resolver.notifyChange(resultUri, null);        }        return resultUri;    }    @Override    public int delete(Uri uri, String selection, String[] selectionArgs) {        String tabName = getTabName(uri);        if (TextUtils.isEmpty(tabName)) {            return -1;        }        int count = mDataBase.delete(tabName, selection, selectionArgs);        return count;    }    @Override    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {        String tabName = getTabName(uri);        if (TextUtils.isEmpty(tabName)) {            return -1;        }        int count = mDataBase.update(tabName, values, selection, selectionArgs);        return count;    }    private String getTabName(Uri uri) {        int code = matcher.match(uri);        if (code == NO_MATCH) {            return null;        }        return TABS[code];    }    private class SQLDBHelper extends SQLiteOpenHelper {        public SQLDBHelper(Context context) {            super(context, DB_NAME, null, DB_VERSION);        }        @Override        public void onCreate(SQLiteDatabase db) {            //第一次安装使用该应用时调用            createDemoTable1(db);            final int firstVersion = 1;//该值是第一个版本的版本号,一直不变即可            onUpgrade(db, firstVersion, DB_VERSION);        }        @Override        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {            //只有数据库版本大于之前数据库版本,才会执行该方法,否者 都执行onCreate方法            for (int i = oldVersion; i < newVersion; i++) {                switch (i) {                    case 1://升级为版本2                        alertDemoTable1(db);                        break;                    case 2://升级为版本3                        break;                }            }        }    }    private void createDemoTable1(SQLiteDatabase db) {        try {            String sql = "create table " + TABLE_NAME + " ("                    + "id integer primary key autoincrement, "                    + "name text, "                    + "age integer)";            db.execSQL(sql);            Log.i(TAG, "createDemoTable1: 创建demo表成功");        } catch (Exception e) {            Log.i(TAG, "createDemoTable1: 创建demo表失败");        }    }    /**     * 升级数据库     * @param db     */    private void alertDemoTable1(SQLiteDatabase db) {        try {            db.execSQL("alter table " + TABLE_NAME + " add " + "address" + " text ");            Log.i(TAG, "alertDemoTable1: 升级数据库成功");        } catch (Exception e) {            Log.i(TAG, "alertDemoTable1: 升级数据库失败");        }    }}

ContentProvider 客户端实现如下

public class ContentProviderDemo extends Activity {    private static final String TAG = "ContentProviderDemo";    private ContentResolver provider;    private TextView mContent;    private int mIndex;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_content_provider);        provider = getContentResolver();        mContent = (TextView) findViewById(R.id.content);    }    public void insert(View view) {        mIndex++;        ContentValues values = new ContentValues();        values.put("name", "王二" + mIndex);        values.put("age", 21 + mIndex);        values.put("address", "珠海" + mIndex);        Uri uri = provider.insert(Contract.DEMO_URI, values);        Log.i(TAG, "insert: uri===" + uri.getPath());    }    public void query(View view) {        StringBuilder sb = new StringBuilder();        Cursor cursor = provider.query(Contract.DEMO_URI, null, null, null, null);        if (cursor != null && cursor.getCount() > 0) {            while (cursor.moveToNext()) {                int nameIndex = cursor.getColumnIndex("name");                int ageIndex = cursor.getColumnIndex("age");                int addressIndex = cursor.getColumnIndex("address");                String name = cursor.getString(nameIndex);                sb.append("name:").append(name);                sb.append("  ");                int age = cursor.getInt(ageIndex);                sb.append("age:").append(age);                sb.append("  ");                String address = cursor.getString(addressIndex);                sb.append("address:").append(address);                sb.append("\n");            }            cursor.close();            mContent.setText(sb.toString());        }    }}

ContentProvider 共享数据

AndroidManifest.xml配置文件

    <--声明其他进程访问该provider所需要的权限!-->    <permission        android:name="com.example.xjp.demo.contentprovider.READ_PROVIDER"        android:protectionLevel="normal" />    <permission        android:name="com.example.xjp.demo.contentprovider.WRITE_PROVIDER"        android:protectionLevel="normal" />  <provider        android:name=".contentprovider.MyContentProvider"        android:authorities="com.example.xjp.demo.contentprovider"        android:exported="true"        android:multiprocess="true"//是否允许多个进程多个实例?默认为false,即只有一个ContentObserver实例        android:readPermission="com.example.xjp.demo.contentprovider.READ_PROVIDER"        android:writePermission="com.example.xjp.demo.contentprovider.WRITE_PROVIDER"/>

如果你想要让其他app能访问你的ContentProvider数据,必须做如下事情:

  1. 在配置文件中设置ContentProvider 的authorities属性
  2. 在配置文件中设置ContentProvider 的exported属性为true
  3. 在配置文件中设置ContentProvider 的读写权限属性
  4. 在配置文件中注册ContentProvider 的读写权限

为了让第三方应用使用你的ContentProvider,第三方应用应该在AndroidManifest.xml中声明ContentProvider读写权限:

<uses-permission android:name="com.example.xjp.demo.contentprovider.READ_PROVIDER"/><uses-permission android:name="com.example.xjp.demo.contentprovider.WRITE_PROVIDER"/>

ContentProvider多线程问题

ContentProvider为多个应用提供数据,那么ContentProvider肯定有多线程访问的问题,那么ContentProvider是不是线程安全的呢?答案是:ContentProvider是线程不安全的。所以如果你的应用中涉及到ContentProvider多线程问题,ContentProvider中insert,delete,update方法必须同步,否则将会出现数据错乱问题。解决方案如下:给这三个方法都加上类锁即可。

    @Nullable    @Override    public Uri insert(@NonNull Uri uri, ContentValues values) {        //加类锁        synchronized (MyContentProvider.class) {            String tabName = getTabName(uri);            if (TextUtils.isEmpty(tabName)) {                return null;            }            long id = mDataBase.insert(tabName, null, values);            Uri resultUri = null;            if (id != -1) {                resultUri = ContentUris.withAppendedId(uri, id);                ContentResolver resolver = mContext.getContentResolver();                resolver.notifyChange(resultUri, null);            }            return resultUri;        }    }    @Override    public int delete(Uri uri, String selection, String[] selectionArgs) {        //加类锁        synchronized (MyContentProvider.class) {            String tabName = getTabName(uri);            if (TextUtils.isEmpty(tabName)) {                return -1;            }            int count = mDataBase.delete(tabName, selection, selectionArgs);            return count;        }    }    @Override    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {        //加类锁        synchronized (MyContentProvider.class) {            String tabName = getTabName(uri);            if (TextUtils.isEmpty(tabName)) {                return -1;            }            int count = mDataBase.update(tabName, values, selection, selectionArgs);            return count;        }    }

ContentObserver

ContentObserver用于监听ContentProvider数据变化,我们上面的例子在插入数据时,会通过ContentResolver#notifyChange()方法来发送一个数据更新通知,让外部监听器知道该数据发生变化了。ContentObserver使用示例如下:

 provider = getContentResolver(); provider.registerContentObserver(Contract.DEMO_URI, true, new MyContentObserver(new Handler()));private class MyContentObserver extends ContentObserver {        public MyContentObserver(Handler handler) {            super(handler);        }        @Override        public void onChange(boolean selfChange, Uri uri) {            Log.i(TAG, "onChange: uri====" + uri.toString());        }    }

ContentProvider 初始化时机

ContentProvider初始化时机有点神秘,在app起来了就已经初始化了,但是也不知道具体什么时机?有谁来初始化它?我们可以根据堆栈来看看

这里写图片描述

右上图可以看出,ContentProvider 是在创建应用的Application时由ActivityThread创建的。所以app一起动时,该app的所有ContentProvider都初始化了。

注意:在ContentProvider 的onCreate初始化方法中不要做耗时操作,否则会影响整个app的启动速度

原创粉丝点击