Mutual call of data between ContentProvider programs

1 Acquiring and invoking permissions

Permissions are divided into common permissions and dangerous permissions, except for calendar information, phone calls, call records, cameras, contacts, location, microphones, phones, sensors, interface recognition (Activity-Recognition), SMS and storage permissions 11 groups are dangerous permissions , and others are normal permissions.
Ordinary permissions only need to be declared in the xml file to use the corresponding permissions, but for the acquisition of dangerous permissions, it needs to be dynamically confirmed by the user in the code.
Take the phone call as an example to call and agree to dangerous permissions: click a button in an app to go directly to the phone app to make a phone call Pre-work ready
button, and connect the simulator and other simple tasks, and then apply Permission operation

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()
        }
    }
    
}

Don't forget to declare the dial-up permission in the xml file

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

2 Use ContentResolver to get data from other applications

Scenario: Obtain the address book in the current application
. Pre-preparation, a button, a recyclerview interface that will display the content of the address book, create several new users on the emulator,

Determine its user name and phone number, and then start the access work of calling data.
1 Because it involves jumping the interface when clicking the button, it is necessary to declare the entity class of the contact address book, and also implement the serialization interface, because To transfer
1 entity class in intent :

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 After the data is obtained, the data is displayed on another interface, so the data needs to be displayed by the recyclerview in the layout, all of which involve the writing of the Adapter, as follows for
each 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>

Display address book data interface

<?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 writing

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
    }

}

Data Display

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)
    }
}

Finally, the data can be displayed on the interface, as follows

insert image description here

Custom ContentProvider combined with the use of Room database

1. First, create a table in a project using the room database to simply store user information. For specific steps, see Room database learning . For a simple demonstration as follows:
Entity class


@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"
    }
}

The writing of userDao, in order to add data to the table

@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>
}

Creation of a database instance

@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
                    }
            }
    }
}

After creation, click the button to add data to the table

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()

        }

    }

The ultimate goal is to provide other programs with an interface to modify data. Here, only one interface for external query is provided.

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.")
    }
}

Then start another project, which is to simulate another app, in which cross-program data access is required.

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}")
                }
            }
        }
    }
}

At this time, an error may be reported Failed to find provider info for ***
The solution is to add the package name of the access data to the xml file that needs to access other data, and the data can be accessed after re-allowing

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

insert image description here
After clicking the access data button, it will be displayed that the data in the first project app can be read
insert image description here

Guess you like

Origin blog.csdn.net/m0_56184347/article/details/129571731
Recommended