Android之ContentProvider实例

今天我们要讲的是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学习的新篇章。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值