今天我们要讲的是Android四大组件的最后一个ContentProvider
什么是ContentProvider:
它的出身就是为解决不同应用间数据共享而设计的。在正式将它之前我们还需要了解跟它相关的2个东西
Android中常用的保存数据的方法:数据库(Sqlite3)以及URI。因为这2个东西都是ContentProvider中必须用到的。
首先来看数据库(SQLite3):sqlite3是Android默认就提供支持的一种数据库,下面我们直接来看看怎么用:
按照Android的基本思路肯定也是要继承一个类的,这个类就是SQLiteOpenHelper,我们定义一个MySQLiteHelper来继承它,SQLiteOpenHelper是一个抽象类,它的构造方法由好几个,这里我们就简单的重写一个最常用的
public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
@Nullable CursorFactory factory, int version) {
this(context, name, factory, version, null);
}
其中的factory可以传空,name和version表示我们要创建的数据库名称和版本。因为随着apk不断的完善、增加功能,我们的数据库也是会经常升级的,所以这里会需要传入一个版本,当有新版本是系统会回调
public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);方法通知我们。我们需要做的就是根据新、久版本来判断应该怎么升级数据库。
下面看实际代码:
class MySQLiteHelper private constructor(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
companion object {
@Volatile
private var instance: MySQLiteHelper? = null
fun getInstance(context: Context): MySQLiteHelper {
if (instance == null) {
synchronized(MySQLiteHelper::class.java) {
if (instance == null) {
instance = MySQLiteHelper(context)
}
}
}
return instance!!
}
}
override fun onCreate(db: SQLiteDatabase?) {
db?.execSQL(DB_CREATE_USER_TABLE)
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
}
fun getUser(tmpName: String): UserBean? {
val cursor = readableDatabase.query(DB_TABLE_NAME_USER, null, "$DB_TABLE_USER_COL_NAME = ?", arrayOf(tmpName), null, null, null)
if (cursor.count == 0) {
cursor.close()
return null
}
cursor.moveToFirst()
val name = cursor.getString(cursor.getColumnIndex(DB_TABLE_USER_COL_NAME))
val age = cursor.getInt(cursor.getColumnIndex(DB_TABLE_USER_COL_AGE))
cursor.close()
return UserBean(name, age)
}
fun insertUser(userBean: UserBean) {
val value = ContentValues()
value.put(DB_TABLE_USER_COL_NAME, userBean.name)
value.put(DB_TABLE_USER_COL_AGE, userBean.age)
writableDatabase.insert(DB_TABLE_NAME_USER, null, value)
}
object DBConstant {
const val DB_NAME = "contentProviderDB.db"
const val DB_VERSION = 1
const val DB_TABLE_NAME_USER = "user"
const val DB_TABLE_USER_COL_ID = "id"
const val DB_TABLE_USER_COL_NAME = "name"
const val DB_TABLE_USER_COL_AGE = "age"
/**
* 这里是创建表的sql语句
*/
const val DB_CREATE_USER_TABLE = """
CREATE TABLE IF NOT EXISTS $DB_TABLE_NAME_USER ( $DB_TABLE_USER_COL_ID integer PRIMARY KEY, $DB_TABLE_USER_COL_NAME TEXT, $DB_TABLE_USER_COL_AGE integer)
"""
}
}
下面来讲一下每个方法
onCreate:在我们实例化这个类,系统在创建好数据库文件之后会调用这个onCreate方法,参数SQLiteDataBase是我们实际能操作数据库的类,一般我们在这里进行表的一些创建工作。这里我们先调用它的execSQL方法,它接收一个sql语句的字符串,没有返回值,直接执行我们的sql语句。我们这里传入的创建一个user表的语句。
OnUpgrade:在构造方法中我们传入的数据库的名称是contentProviderDB.db,数据库的版本是1,如果我们的版本需要升级,传入大于1的数,那么这个方法将被回调。
getUser:我们自己写的方法,用来获取一个用户,参数是用户名。我们这里假设用户名是唯一字段,这里我们调用了readableDatabase的query方法,SQLiteOpenHelper提供了一个read和一个write的操作类,我们这里查询就只需要read就可以了,然后调用它的query方法,我们这里查询用户信息的方法为:
readableDatabase.query(DB_TABLE_NAME_USER, null, "$DB_TABLE_USER_COL_NAME = ?", arrayOf(tmpName), null, null, null)
翻译成sql语句为:select * from user where name=”tmpName”
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having,String orderBy)
table:要查询的表名,即sql语句中from后的表名
columns:要查询的列,可以传入空,及所有列,即sql中 select后面的 *
select:查询条件,如果有条件则值暂时用?代替。即sql中 where后的字段,取值为?
selecttionArgs:对应查询条件的值,是数组。即sql中where中的值。
groupBy,having、orderBy都是sql语句中相同的意义。
insertUser:自定义方法,为插入一个用户信息
插入数据我们需要借助ContentValues类,它里面使用了map来保存我们的数据
使用put方法将数据都设置好之后,我们调用writableDatabase方法的insert即可
下面我们来运行看看效果

界面很简单就不贴代码了,这里我们先点击插入数
acMainBtInsert.setOnClickListener {
MySQLiteHelper.getInstance(this).insertUser(UserBean("Test", 10))
}
向数据库中插入一条记录
然后点击读取,并且在界面显示读取的结果
acMainBtRead.setOnClickListener {
acMainTvShowMsg.text = MySQLiteHelper.getInstance(this).getUser("Test").toString()
}

好了,数据库就先简单的介绍到这里。
URI:统一资源标识符,这里的作用是用来标识指定唯一的ContentProvider,Android中的固定写法:
content::// authorities/path
content://为固定不变部分
authorities:在声明ContentProvider的时候指定的唯一标识,通常用包名。
path:路径,指的我们的数据库中表名
此外,再介绍一个系统提供的工具类:UriMatcher,它有2个方法addURI和match,一个是帮助我们构建URI,一个帮我们解析URI。
好了,有了这些东西下面我们开始创建ContentProvider
class MyContentProvider : ContentProvider() {
private lateinit var mySQLiteHelper: MySQLiteHelper
private lateinit var matcher: UriMatcher
override fun onCreate(): Boolean {
mySQLiteHelper = MySQLiteHelper.getInstance(context!!)
matcher = UriMatcher(UriMatcher.NO_MATCH)
matcher.addURI(URI_AUTHORITY, URI_PATH, URI_CODE_USER)
return true
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
if (matcher.match(uri) == URI_CODE_USER) {
values?.let {
val name = it["name"] as String
val age = it["age"] as Int
val userBean = UserBean(name, age)
mySQLiteHelper.insertUser(userBean)
}
}
return null
}
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? {
Log.d("ZLog MyContentProvider", "query uri:$uri")
if (matcher.match(uri) == URI_CODE_USER) {
return mySQLiteHelper.readableDatabase.query(
MySQLiteHelper.DBConstant.DB_TABLE_NAME_USER,
null,
selection,
selectionArgs,
null,
null,
sortOrder
)
}
return null
}
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int {
TODO("Not yet implemented")
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
TODO("Not yet implemented")
}
override fun getType(uri: Uri): String? {
TODO("Not yet implemented")
}
object ProviderUnit {
const val URI_AUTHORITY = "contentProviderTest"
const val URI_PATH = "user"
const val URI_CODE_USER = 1
}
}
同样我们需要继承ContentProvider并且重写相关方法insert、query、update、delete、getType。
我这里演示的代码是插入和查询。
在onCreate方法中我们初始化了数据库,并且使用UriMatcher添加了一个URI,第一个参数是authority,就是上文提到的URI中中间的部分,第二个参数是path,即数据库中的表名,第三个参数为一个int,用来标识当前URI。
在插入和查询方法中我们先用matcher.match方法判断当前URI是什么,它的返回值就是添加的时候的第三个参数。
然后再使用数据库进行插入的查找。
记得ContentProvider也是需要在Manifest中配置的
<provider
android:name=".MyContentProvider"
android:authorities="contentProviderTest"
android:exported="true" />
name:类路径
authorities:标识,和在ContentProvider中matcher添加的相同
exported:是否工其他应用使用
下面来看看怎么使用:
我们新建一个项目,在界面上创建一个textView和button,点击button来查询然后textView显示。布局文件就贴代码了,直接看怎么操作的:
class MainActivity : AppCompatActivity() {
private val uriUser = Uri.parse("content://contentProviderTest/user")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
acMainBtQuest.setOnClickListener {
val cursor = contentResolver.query(uriUser, null, "name=?", arrayOf("Test"), null)
Log.d("ZLog MainActivity", "onCreate: cursor:$cursor")
cursor?.let {
Log.d("ZLog MainActivity", "query: ${it.count}")
it.moveToFirst()
val name = it.getString(it.getColumnIndex("name"))
val age = it.getInt(cursor.getColumnIndex("age"))
acMainTvMsg.text = "name:$name age:$age"
}
}
}
}
先看上面定义的URI:Uri.parse("content://contentProviderTest/user") 这里除了content://之后的2部分必须跟之前的保持一致
然后我们调用contentResolver的query方法来查询,它的第一个参数就是uri,其他几个参数就跟上面 将的数据库查询差不多。查到之后就可以拿到数据了。

ContentProvider权限:
如果想要给我们的ContentProvider添加访问权限要怎么弄呢?只需要在ContentProvider的manifest中添加上相关权限就可以了

然后我们在使用的地方的manifest中添加权限即可:

好了,到此ContentProvider也讲完了。Android的四大组件也讲完了。
后面我们将开启Android学习的新篇章。
|