ContentProvider 是 Android 四大组件之一,方便我们跨应用(跨应用本身就是跨进程)、跨进程访问与操作数据,之前学过但是早就忘了,故温习一遍,而 SQlite 是 Android 提供的轻量级数据库,自定义 ContentProvider 时也自己提供了数据,所以捎带手回顾了一下 SQlite。
1我们通过 ContentResolver 访问数据,比如增删改查,而这些操作其实是 ContentResolver 底层去调用 ContentProvider 的对应的操作。
2.通过调用 getContentResolver() 去获取 ContentResolver,然后进行操作。
3.做好把对 ContentResolver 的操作再放后台线程,查询时其实还可以使用 CursorLoader 自动异步进行。
4.读写数据前注意权限问题。
5.要防止 SQL 注入。
6.通过继承 ContentProvider 来实现自己的 ContentProvider,并实现onCreate()、query()、update()、insert()、delete()、getType(),除了 onCreate() 运行在主线程,所以不要在 onCreate() 做耗时操作,其他五个方法都运行在都运行在 Binder 线程。
7.ContentProvider 的 query() 方法只有在出错是才返回 null,否则就算没有数据也会返回一个长度为 0 的 Cursor。
8.ContentProvider 的 insert() 方法一般在成功后返回带有 Id 的 URI,而插入失败返回 null。
9.update()、delete() 返回成功操作的条数。
10.自己在定义 ContentProvider 后,需要在清单文件中声明,声明是一定要声明 android:authorities,这时 ContentProvider 的唯一标识,也是别人访问你的 ContentProvider 使用的值。
11.如果想要声明 ContentProvider 的使用权限要在自己的清单文件中自定义好权限,然后提供给别人使用。对方使用的使用就直接声明好对应权限即可,而自定义权限中的 android:protectionLevel 表示权限级别,这里是普通权限。
别的 App 需要声明 <uses-permission android:name="com.google.jaaaule.gzw"/>
12.别的 App 中想要使用自己自定义 ContentProvider 还有一点需要注意要在 <provider> 下声明 android:exported="true",这样其他 App 才能正常访问这个ContentProvider。
13.侦听 ContentProvider 数据变化可以使用自定义的 ContentObserver 实现 onChange() 即可,然后在 ContentResolver 中注册侦听器即可,注册时第二参数需要传递一个 boolean 值,该值表示是否可以侦听到子级数据的变化,记得反注册哟。
14.ContentResolver 的 applyBatch() 可以同时向多个表中插入数据,而 insert() 以及 bulkInsert() 只是针对单一 URI 插入,只是 bulkInsert() 向一个表中同时插入多条。
15.SQLiteDatabase 是用于展示管理 SQLite 数据库中的方法,因为 SQLite 本身是用 C 写的,SQLiteDatabase 是 SQLite 在 Java 层分一层封装。
16.SQLiteOpenHelper 是我们对 SQLiteDatabase 进行管理操作的类,SQLiteOpenHelper 本身是抽象类,而我们需要实现自己的 SQLiteOpenHelper,在里面创建或者升级数据库,而我们通过getWritable/ReadaleDatabase() 获取对应的 SQLiteDatabase。
17.我们无需手动调用 SQLiteOpenHelper 的 onCreate(),getWritable/ReadaleDatabase() 会隐式帮我调用 onCreate()。
18.使用 SQLiteDatabase 插入数据时,我们可以使用insert() 或者 insertWithOnConflict(),后者多了一个冲突解决器,例如下面这行代码,冲突解决器为 CONFLICT_REPLACE,这个解决器的效果是如果数据不存在时插入,如果存在则更新数据,与 SQLiteDatabase 的 replace() 效果相同,因为源码中就是调用 insertWithOnConflict(xxx, xxx, xxx, SQLiteDatabase.CONFLICT_REPLACE)。
db.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
19.升级数据库时记得不仅要在 onUpgrade() 中写对应的 SQL 执行语句,还需要在 onCreate() 中写入新的字段,因为有些用户是从新版本开始安装的 App。
20.并发访问时,多个线程中同时使用同一个 SQLiteOpenHelper 实例其实是顺序访问,问并发访问需要创建多个 SQLiteOpenHelper 实例。
21.还有一点需要注意,当我们查询时候,应该小心调用 cursor.moveToFirst() 因为正常情况下,最好不要不同调用 cursor.moveToNext() 这样会导致光标连续移动,从而错过第一个位置的数据,所以 cursor.moveToFirst() 配合 do...while 更合适,单纯 cursor.moveToNext() 更适合 while。
这些只是个人学习与使用中一些知识点总结,如需 Demo 请留言。