ContentProvider程序之间数据的相互调用

1权限的获取和调用

权限分为普通权限和危险权限,除了日历信息,电话,通话记录,相机,通讯录,定位,麦克风,电话,传感器,界面识别(Activity-Recognition),SMS和存储权限11组是危险权限之外,其他都是普通权限。
普通权限只需要在xml文件中声明即可使用相应的权限,但是对于危险权限的获取,需要在代码中进行动态的由用户确认才行。
以打电话为例进行危险权限的调用和同意需求:在一个应用中点击按钮就可以直接到电话应用中进行电话的拨打
前置工作准备好按钮,和连接好模拟器等简单工作,然后进行申请权限操作

class PhoneCallActivity:AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.content_provider_phonecall)
        phone_call.setOnClickListener(){
    
    
            //一点击这个按钮,就要让他拨打电话,拨打电话属于危险权限,所以需要进行权限的申请,先判断当前权限是否已经被允许,如果没有被允许则需要去请求权限,三个参数,第二个参数是一个数组,存放所有需要的权限,第三个参数是一个唯一的标识,用来在下面方法中针对当前请求,进行再次处理的逻辑
            if(ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){
    
    
                //如果当前权限还没有授权,就去请求权限
                ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CALL_PHONE),1)
            }else{
    
    
                call()
            }
        }
       
    //父类的请求权限允许的解过,只要用户进行结果确认之后,不管结果如何都会调用这个方法
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
    
    
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        
        when(requestCode){
    
    
            1->{
    
    
                if(grantResults.isNotEmpty()&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
    
    
                    call()
                }else{
    
    
                    Toast.makeText(this,"您未授权允许进行拨号",Toast.LENGTH_SHORT).show()
                }
            }
            
        }
    }
    private fun call(){
    
    
        try{
    
    
            val intent=Intent(Intent.ACTION_CALL)
            intent.data= Uri.parse("tel:10086")
            startActivity(intent)
        }catch (e:SecurityException){
    
    
            e.printStackTrace()
        }
    }
    
}

别忘了在xml文件中进行拨号权限的声明

<uses-permission android:name="android.permission.CALL_PHONE"/>

2使用ContentResolver来获取其他应用中的数据

场景:在当前应用程序中获取通讯录
前置准备,一个按钮,一个即将展示通讯录内容的recyclerview界面,在模拟器上新建几个用户,

确定其用户名和电话,然后开始进行数据的调用的权限访问工作
1因为涉及到点击按钮时进行界面的跳转,所以需要声明contact通讯录的实体类,而且还要其进行实现序列化接口,因为要在intent中传输
1实体类:

import java.io.Serializable
class ContactFriend(val name:String,val number:String):Serializable

2

class PhoneCallActivity:AppCompatActivity() {
    
    
    //存放所有读取的通讯录名单
    private lateinit var  list:ArrayList<ContactFriend>
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.content_provider_phonecall)
        phone_call.setOnClickListener(){
    
    
            //一点击这个按钮,就要让他拨打电话,拨打电话属于危险权限
            if(ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){
    
    
                //如果当前权限还没有授权,就去请求权限
                ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CALL_PHONE),1)
            }else{
    
    
                call()
            }
        }
        //一点击之后需要跳转到另一个界面上用来展示当前的所有数据,所有需要跳转到一个新的界面
        show_contact.setOnClickListener(){
    
    
            //当前权限是否获取,如果没获取就去申请,如果获取过了,就直接进行跳转显示即可
            if(ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS)
            !=PackageManager.PERMISSION_GRANTED){
    
    
                ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CONTACTS),2)
            }else{
    
    
                readContacts()
                val intent=Intent(this,DisplayPhoneContact::class.java)
                intent.putExtra("contacts",list)
                //Log.d("tong","第一个好友姓名${list[0].name}第一个好友电话${list[0].number}")
                startActivity(intent)
            }
        }

    }
    //父类的请求权限允许的解过
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
    
    
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when(requestCode){
    
    
            1->{
    
    
                if(grantResults.isNotEmpty()&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
    
    
                    call()
                }else{
    
    
                    Toast.makeText(this,"您未授权允许进行拨号",Toast.LENGTH_SHORT).show()
                }
            }
            2->{
    
    
                if(grantResults.isNotEmpty()&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
    
    
                    //readContacts()
                    //Toast.makeText(this,"感谢您的信任",Toast.LENGTH_SHORT).show()
                    readContacts()
                    val intent=Intent(this,DisplayPhoneContact::class.java)
                    intent.putExtra("contacts",list)
                    startActivity(intent)
                }else{
    
    
                    Toast.makeText(this,"您未授权允许查看通讯录",Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
    private fun call(){
    
    
        try{
    
    
            val intent=Intent(Intent.ACTION_CALL)
            intent.data= Uri.parse("tel:10086")
            startActivity(intent)
        }catch (e:SecurityException){
    
    
            e.printStackTrace()
        }
    }
    @SuppressLint("Range")
    private fun readContacts(){
    
    
        list= ArrayList<ContactFriend>()
        contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
        null,null,null,null)?.apply{
    
    
            while(moveToNext()){
    
    
                val name=getString(getColumnIndex(
                    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
                val number=getString(getColumnIndex(
                    ContactsContract.CommonDataKinds.Phone.NUMBER))
                list.add(ContactFriend(name,number))
            }
            close()
        }
    }
}

3获取到数据之后,在另一个界面进行数据的展示所以需要layout中由recyclerview展示数据,所有又牵扯到Adapter的编写,如下
每个item

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="90dp">
    <TextView
        android:id="@+id/contact_name"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
    <TextView
        android:id="@+id/contact_number"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

</LinearLayout>

展示通讯录数据界面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_phone_contact"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

adapter的编写

package com.njupt.kotlinlearn.contendProvider

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.njupt.kotlinlearn.R
import com.njupt.kotlinlearn.entity.ContactFriend

class ContactsAdapter (var contactList:List<ContactFriend>):RecyclerView.Adapter<ContactsAdapter.ViewHolder>(){
    
    
    inner class ViewHolder(view:View):RecyclerView.ViewHolder(view){
    
    
        val name:TextView=view.findViewById(R.id.contact_name)
        val number:TextView=view.findViewById(R.id.contact_number)
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactsAdapter.ViewHolder {
    
    
        val view=LayoutInflater.from(parent.context).inflate(R.layout.content_provider_display_contact_item,parent,false)
        var viewHolder=ViewHolder(view)
        return viewHolder

    }

    override fun onBindViewHolder(holder: ContactsAdapter.ViewHolder, position: Int) {
    
    
        val contact=contactList[position]
        holder.name.text=contact.name
        holder.number.text=contact.number

    }

    override fun getItemCount(): Int {
    
    
        return contactList.size
    }

}

数据展示

class DisplayPhoneContact:AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.content_provider_display_contact)
        //跳转过来之后,获取到intent中的数据,然后进行绑定Adapter数据展示
        val contacts=intent.getSerializableExtra("contacts") as ArrayList<ContactFriend>

        //准备好数据之后可以进行数据在当前界面的展示,绑定Adapter
        val recyclerView=findViewById<RecyclerView>(R.id.recycler_phone_contact)
        val layoutManager=LinearLayoutManager(this)
        recyclerView.layoutManager=layoutManager
        recyclerView.adapter=ContactsAdapter(contacts)
    }
}

最后即可将数据进行展示在界面上,如下

在这里插入图片描述

扫描二维码关注公众号,回复: 15522339 查看本文章

自定义的ContentProvider结合Room数据库的使用

1首先在一个项目中用room数据库进行创造一个表,简单的存放user的信息,具体步骤见Room数据库学习。为了简单演示如下:
实体类


@Entity
class LoginUser(var name:String,var password:String) {
    
    
    @PrimaryKey(autoGenerate = true)
    var id:Long=0
    override fun toString(): String {
    
    
        return "id为:${
      
      id}用户名为:$name:密码为$password"
    }
}

userDao的编写,为了往表中添加数据

@Dao
interface LoginUserDao {
    
    
    @Insert
    fun insertUser(user:LoginUser):Long
    //按照id查询
    @Query("select id,name,password from LoginUser where id=:id")
    fun queryUserById(id:Long) :LoginUser
    //按照姓名查询
    @Query("select id,name,password from LoginUser where name=:name")
    fun queryUserByName(name:String):LoginUser
    @Update
    fun updateUser(user: LoginUser):Int
    //查询出所有的用户
    @Query("select * from LoginUser")
    fun queryAllUser():List<LoginUser>
}

数据库实例的创建

@Database(version = 1,entities = [LoginUser::class],exportSchema = false)
abstract class AppDatabase:RoomDatabase() {
    
    
    abstract fun userDao():LoginUserDao
    companion object{
    
    
        private var instance:AppDatabase?=null
        //为了安全起见,需要写一个线程安全的单例模式创建的room数据库
        fun getDatabaseSingleton(context:Context):AppDatabase=
            instance?: synchronized(AppDatabase::class.java){
    
    
                instance?:Room.databaseBuilder(context.applicationContext,
                AppDatabase::class.java,"app_lang_database")
                    .allowMainThreadQueries().build().also {
    
    
                        instance=it
                    }
            }
    }
}

创建完之后,点击按钮进行往表中添加数据

class PhoneCallActivity:AppCompatActivity() {
    
    
 
    private lateinit var userDao:LoginUserDao
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.content_provider_phonecall)
        userDao=AppDatabase.getDatabaseSingleton(this).userDao()
        //在这个界面中点击按钮就进行用户的添加操作,并且通过查表知道确实已经插入了
        add_user.setOnClickListener(){
    
    
            //插入数据
            userDao.insertUser(LoginUser("小明","123455"))
            userDao.insertUser(LoginUser("小花","987764"))
            val user:LoginUser=userDao.queryUserById(1)
            Toast.makeText(this,"${
      
      user}",Toast.LENGTH_SHORT).show()

        }

    }

最终要的就是为其他程序提供修改数据的接口,这里只提供了一个对外查询的接口

class DatabaseProvider : ContentProvider() {
    
    
    private val authority="com.njupt.databasetset.provider"
    private val userDir=0
    private val userItem=1
    //应该是要获得数据库的实例,然后调用数据库
    private lateinit var userDao: LoginUserDao

    private val uriMatcher by lazy{
    
    
        val matcher=UriMatcher(UriMatcher.NO_MATCH)
        matcher.addURI(authority,"LoginUser",userDir)
        matcher.addURI(authority,"LoginUser/#",userDir)
        matcher
    }

    override fun onCreate() = context?.let {
    
    
        true
    }?:false

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
    
    
        TODO("Implement this to handle requests to delete one or more rows")
    }

    override fun getType(uri: Uri): String? {
    
    
        TODO(
            "Implement this to handle requests for the MIME type of the data" +
                    "at the given URI"
        )
    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {
    
    
        TODO("Implement this to handle requests to insert a new row.")
    }



    override fun query(
        uri: Uri, projection: Array<String>?, selection: String?,
        selectionArgs: Array<String>?, sortOrder: String?
    ): Cursor? {
    
    

        val tablename=when(uriMatcher.match(uri)){
    
    
            userDir,userItem->"LoginUser"
            else->null
        }

        //这里需要有一个dbhelper对象
        val dbHelper= context?.let {
    
     AppDatabase.getDatabaseSingleton(it) }?.openHelper?.readableDatabase
        return dbHelper?.query(SupportSQLiteQueryBuilder.builder(tablename)
            .selection(selection,selectionArgs)
            .columns(projection)
            .orderBy(sortOrder)
            .create()
        )

    }

    override fun update(
        uri: Uri, values: ContentValues?, selection: String?,
        selectionArgs: Array<String>?
    ): Int {
    
    
        TODO("Implement this to handle requests to update one or more rows.")
    }
}

然后另起一个项目,也就是模拟另一个app,在这个app中需要进行跨程序的数据 的访问。

class MainActivity : AppCompatActivity() {
    
    
    @SuppressLint("Range")
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val btn1=findViewById<Button>(R.id.getData)
        btn1.setOnClickListener(){
    
    
            val uri= Uri.parse("content://com.njupt.databasetset.provider/LoginUser")
            contentResolver.query(uri,null,null,null,null)?.apply {
    
    
                while(moveToNext()){
    
    
                    val name=getString(getColumnIndex("name"))
                    val password=getString(getColumnIndex("password"))
                    Log.d("aa","username is ${
      
      name},password is ${
      
      password}")
                }
            }
        }
    }
}

此时可能会报错 Failed to find provider info for***
解决办法是在要访问其他数据的xml文件中加入访问数据的包名,重新允许之后即可访问数据

  <queries>
        <package android:name="com.njupt.kotlinlearn"/>
    </queries>

在这里插入图片描述
点击访问数据按钮之后,就会显示已经可以读取到第一个项目App中的数据了
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_56184347/article/details/129571731