AI智能
改变未来

Android之ContentProvider实例

今天我们要讲的是Android四大组件的最后一个ContentProvider

什么是ContentProvider:

它的出身就是为解决不同应用间数据共享而设计的。在正式将它之前我们还需要了解跟它相关的2个东西

Android中常用的保存数据的方法:数据库(Sqlite3)以及URI。因为这2个东西都是ContentProvider中必须用到的。

首先来看数据库(SQLite3):sqlite3是Android默认就提供支持的一种数据库,下面我们直接来看看怎么用:

按照Android的基本思路肯定也是要继承一个类的,这个类就是SQLiteOpenHelper,我们定义一个MySQLiteHelper来继承它,SQLiteOpenHelper是一个抽象类,它的构造方法由好几个,这里我们就简单的重写一个最常用的

[code]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);方法通知我们。我们需要做的就是根据新、久版本来判断应该怎么升级数据库。

下面看实际代码:

[code]class MySQLiteHelper private constructor(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {companion object {@Volatileprivate var instance: MySQLiteHelper? = nullfun 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 = 1const 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))

        }

 

向数据库中插入一条记录

然后点击读取,并且在界面显示读取的结果

[code]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

[code]class MyContentProvider : ContentProvider() {private lateinit var mySQLiteHelper: MySQLiteHelperprivate lateinit var matcher: UriMatcheroverride 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 Stringval age = it[\"age\"] as Intval 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中配置的

[code]<providerandroid:name=\".MyContentProvider\"android:authorities=\"contentProviderTest\"android:exported=\"true\" />

name:类路径

authorities:标识,和在ContentProvider中matcher添加的相同

exported:是否工其他应用使用

下面来看看怎么使用:

我们新建一个项目,在界面上创建一个textView和button,点击button来查询然后textView显示。布局文件就贴代码了,直接看怎么操作的:

[code]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学习的新篇章。

 

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Android之ContentProvider实例