【Android】内容提供器

一、什么是内容提供器

  • 定义 :Android 四大组件之一,用于管理应用数据的跨应用共享

  • 核心功能

    • 提供统一的接口访问数据(SQLite、文件、网络等)

    • 控制数据的安全访问权限

    • 支持跨进程数据交互

主要用于在不同的应用程序之间实现数据共享的功能,允许程序访问另一个程序的数据并保证被访问数据的安全性。

内容提供器可选择对哪一部分数据进行共享。

二、运行时权限

危险权限需要在运行时处理,普通权限需要在AndroidManifest.xml文件中添加权限声明

2.1在程序运行时申请权限

以CALL_PHONE权限为例

1.修改AndroidManifest.xml文件,声明权限

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.CALL_PHONE"/>

2.修改主活动,先判断用户是否已经授权,借用ContextCompat.checkSelfPermission方法,接收两个参数,Context和具体权限名,使用返回值和PackageManager.PERMISSION_GRANTED比较,相等表示已授权,直接执行逻辑。

没有授权,调用 ActivityCompat.requestPermissions向用户申请授权,接收三个参数,Activity实例,含有要申请权限名的String数组和请求码,之后系统自动回调onRequestPermissionResult方法,授权结果封装在grantResults中,只需要判断授权结果

java 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=(Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, 1);
                } else {
                    call();
                }
            }
        });
    }
    private void call(){
        Intent intent=new Intent(Intent.ACTION_CALL);
        intent.setData(Uri.parse("tel:10086"));
        startActivity(intent);
    }
    public void onRequestPermissionResult(int requestCode,String[] permissions,int[] grantResults){
        switch (requestCode){
            case 1:
                if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
                    call();
                }else{
                    Toast.makeText(this,"permission",Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }

用户之后在手机设置中中可以对程序危险权限进行关闭

三、访问其他程序中的数据

3.1ContentResolver的基本用法

想要访问内容提供器中共享的数据,要借助ContentResolver类,可以通过Context中的getContentResolver()方法获取到该类的实例。

ContentResolver中的CRUD操作接收Uri参数,这个参数被称为内容URI,内容URI为内容提供器中的数据建立了唯一标识符,主要由authority和path两部分组成。

  • authority:对不同应用程序进行区分,采用程序包名进行命名

  • path:对同一应用程序中的不同表区分,一般添加在authority后

得到内容URI后通过Uri.parse得到Uri对象后才能作为参数传入。

java 复制代码
Uri uri=Uri.parse("content://com.example.app.provider/tabel");

查询数据

java 复制代码
Cursor cursor=getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
  • uri:指定查询某个应用程序下的某一张表
  • projection:指定查询的列名
  • selection:指定where的约束条件
  • selectionArgs:为where中的占位符提供具体的值
  • sortOrder:指定查询结果的排序方式

查询完成后返回Cursor对象,可以将数据从Cursor中逐个读取出来

java 复制代码
 if(cursor!=null){
            while (cursor.moveToNext()){
                String column1=cursor.getString(cursor.getColumnIndex("column1"));
                int column2=cursor.getInt(cursor.getColumnIndex("column2"));
            }
            cursor.close();
        }

添加数据

java 复制代码
ContentValues values=new ContentValues();
        values.put("column1","text");
        values.put("column2",1);
        getContentResolver().insert(uri,values);

将待添加的数据组装到ContentValues中,然后调用ContentResolver的insert方法,传入参数uri和values

更新数据

java 复制代码
ContentValues values=new ContentValues();
        values.put("column1","");
        getContentResolver().update(uri,values,"column1 = ? and column2 = ?",new String[]{"text","1"});

上述代码使用了selection和selectionArgs参数对想要更新的数据进行约束

删除数据

java 复制代码
 getContentResolver().delete(uri,"column2 = ?",new String[]{"1"});

调用ContentResolver的delete方法删除数据

四、创建内容提供器

1.新建一个类继承ContentProvider,需要全部重写6个抽象方法

java 复制代码
public class MyProvider extends ContentProvider {
    public boolean onCreate(){
        return false;
    }
    public Cursor query(Uri uri, String[] projection,String selection,String[] selectionArgs,String sortOrder){
        return null;
    }
    public Uri insert(Uri uri, ContentValues values){
        return null;
    }
    public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs){
        return 0;
    }
    public int delete(Uri uri,String selection,String[] selectionArgs){
        return 0;
    }
    public String getType(Uri uri){
        return null;
    }
}

onCreate:初始化内容提供器时调用,完成对数据库的创建和升级

query:从内容提供器中查询数据,查询结果存放在Cursor对象中返回

insert:向内容提供器中添加一条数据,返回一个用于表示这条新记录的URI

update:更新已有数据,返回受影响的行数

delete:删除数据,返回被删除的行数

getType:返回相应的MIME类型

URI以路径结尾表示访问该表中的所有数据,以id结尾表示访问该表中有相应id的数据

可以使用通配符的方式分别匹配这两种格式的内容URI

*:表示匹配任意长度的任意字符
#:表示匹配任意长度的数字

2.借助UriMatcher类可以实现匹配内容的URI功能

UriMatcher中提供了addURI方法,接收三个参数,authority,path和自定义代码

调用UriMatcher的match方法时,传入一个Uri对象,返回的是自定义代码,通过代码判断调用方相要调用的数据

java 复制代码
public class MyProvider extends ContentProvider {
    public static final int TABLE1_DIR=0;
    public static final int TABLE1_ITEM=1;
    public static final int TABLE2_DIR=2;
    public static final int TABLE2_ITEM=3;
    private static UriMatcher uriMatcher;
    static {
        uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI("com.example.app.provider","table1",TABLE1_DIR);
        uriMatcher.addURI("com.example.app.provider","table1/#",TABLE1_ITEM);
        uriMatcher.addURI("com.example.app.provider","table2",TABLE2_DIR);
        uriMatcher.addURI("com.example.app.provider","table2/#",TABLE2_ITEM);
    }
    public boolean onCreate(){
        return false;
    }
    public Cursor query(Uri uri, String[] projection,String selection,String[] selectionArgs,String sortOrder){
        switch (uriMatcher.match(uri)){
            case TABLE1_DIR:
                break;
            case TABLE1_ITEM:
                break;
            case TABLE2_DIR:
                break;
            case TABLE2_ITEM:
                break;
            default:
                break;
        }
        return null;
    }
    public Uri insert(Uri uri, ContentValues values){
        return null;
    }
    public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs){
        return 0;
    }
    public int delete(Uri uri,String selection,String[] selectionArgs){
        return 0;
    }
    public String getType(Uri uri){
        return null;
    }
}

getType是所有内容提供器必须提供的一个方法,用于获取Uri对象的MIME类型,由三部分组成:

  • 必须以vnd开头

  • 如果以路径结尾,则后接android.cursor.dir/,如果以id结尾,后接android.cursor.item/

  • 最后接vnd.<authority>.<path>

java 复制代码
public String getType(Uri uri){
        switch (uriMatcher.match(uri)){
            case TABLE1_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
            case TABLE1_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
            case TABLE2_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
            case TABLE2_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
            default:
                break;
        }
        return null;
    }

内容提供器要在AndroidManifest.xml中注册才能使用

XML 复制代码
 <provider
            android:name=".DatabaseProvider"
            android:authorities="com.example.databasetest.provider"
            android:enabled="true"
            android:exported="true"></provider>
相关推荐
阿巴斯甜11 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker11 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952712 小时前
Andorid Google 登录接入文档
android
黄林晴13 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android