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数据,必须做如下事情:
- 在配置文件中设置ContentProvider 的authorities属性
- 在配置文件中设置ContentProvider 的exported属性为true
- 在配置文件中设置ContentProvider 的读写权限属性
- 在配置文件中注册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的启动速度
- Android ContentProvider 知识点总结
- 6.ContentProvider知识点总结
- Android知识点之ContentProvider
- Android之ContentProvider总结
- Android之ContentProvider总结
- Android之ContentProvider总结
- Android之ContentProvider总结
- Android之ContentProvider总结
- Android之ContentProvider总结
- Android之ContentProvider总结
- Android ContentProvider总结
- android之ContentProvider总结
- Android ContentProvider 总结
- Android之ContentProvider总结
- Android之ContentProvider总结
- Android之ContentProvider总结
- Android之ContentProvider总结
- Android ContentProvider 总结
- PHP之冒泡排序的优化
- Druid数据库连接池的正确姿势
- Lua-cjson -> require(“cjson”) successful, then errors when calling cjson.encode
- Zookeeper原理详解
- Java
- Android ContentProvider 知识点总结
- 287. Find the Duplicate Number
- android 2048游戏、kotlin应用、跑马灯、动画源码
- 【数据结构】红黑树——自平衡二叉搜索树
- 设计模式之工厂模式(factory pattern)
- linux 单用户模式
- Spring Boot整合MyBatis
- Android studio突然不提示了。
- 6-3 在一个数组中实现两个堆栈(20 分)-pta_same_无聊的水体