Android第一行代码 Day07笔记

7. 跨程序共享数据-内容提供器

7.1 运行时权限

tips:访问 权限列表可以查看Android系统中完整的权限列表

在程序运行时申请权限的流程:

  1. 借助ContextCompat.checkSelfPermission()方法判断是否已经授权,接收两个参数
    第一个参数为context
    第二个参数为具体的权限名 前缀为 Manifest.permission.具体的权限名
    然后使用返回值和PackageManager.PERMISSION_GRANTED作比较,相等即为已授权
  2. 如果没有授权需要调用 ActivityCompat.requestPermissions()方法申请,接收三个参数
    第一个参数为Activity的实例
    第二个参数为String数组:把要申请的权限名放入,Manifest.permission.具体的权限名
    第三个参数为请求码,传入唯一值即可
  3. 调用完requestPermissions()方法后,不管什么结果都会回调到 onRequestPermissionsResult()方法,授权的结果则会封装在grantResults参数中
  4. 重写onRequestPermissionsResult()方法,在方法中再判断一下最后的授权结果
    如果 grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED 为true,则说明有授权
    否则未授权

动态申请权限基本固定写法(代码示例):

     //1.首先在Manifest文件中,申明需要的权限
     //2.在OnCreate()方法中或者按钮的点击事件中:
       if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.权限名)!= PackageManager.PERMISSION_GR
            //没有权限,进行动态申请  1为请求码,唯一值即可
            ActivityCompat.requestPermissions(MainActivity.this,new String[]{ Manifest.permission.权限名},1);
        }else{
           //有权限,执行操作
        }
     //3.重写onRequestPermissionsResult():
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode){
            case 1:
                if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                     //有权限,执行操作
                }else{
                   	//没有权限
                }
                break;
            default:
                break;
        }
    }

7.2 ContentResolver的基本用法

  • 通过Context中的getContentResolver()方法获取到实例

  • 调用实例方法:都需要传入使用Uri.parse()方法解析的URI字符串,URI字符串标准写法:content://com.example.包名.provider/table名

    • query(uri,projection,selection,selectionArgs,sortOrder):返回的是一个Cursor对象

      • uri:指定查询某个应用程序的某一张表
      • projection:指定查询的列名
      • selection:指定where的约束条件
      • selectionArgs:为占位符提供具体的值
      • sortOrder:指定查询结果的排列方式
    • update(uri,values,“condition”,new String[]{}):

      • uri:指定查询某个应用程序的某一张表
      • ContentValues:要更新的数据组装到ContentValues
      • selection:条件
      • selectionArgs:对应的条件值
    • delete(uri,“condition”,new String[]{})

      • uri:指定查询某个应用程序的某一张表
      • selection:条件
      • selectionArgs:对应的条件值
    • insert(uri,values):使用ContentValues

      • uri:指定查询某个应用程序的某一张表
      • values:将待添加的数据组装到ContentValues中

Content URI重要部分解析:(示例:content://com.example.provider/path/001)

  • content:// : 标准前缀,用于标识该数据由ContentProvider管理,不需修改
  • com.example.provider:URI的权限部分,用于对不同的应用程序做区分,一般采用完整的类名(小写形式)来保证其唯一性。
  • path:Content Provider的路径(path)部分,用于指定要操作的数据,可以是数据表、文件、XML等。访问数据表person中所有记录,使用“/person”,要访问person中的ID为001记录的name字段,使用“/person/001/name”
  • 001:被请求的特定记录ID。这是被请求记录的_ID值。如果请求不仅限于单条记录,该部分及其前面的斜杠应该删除。

7.3 创建自己的内容提供器

7.3.1 简要流程

内容提供器:
提供方:

  1. 创建数据库,自定义类继承SQLiteOpenHelper,如已有数据库则忽略
  2. 手动自定义内容提供类,继承自ContentProvider,并重写6个方法
  3. Manifest文件中注册provider,注意包名,类名的正确性
        <provider
            android:name=".DatabaseProvider"
            android:authorities="com.example.databasetest.provider"
            android:enabled="true"
            android:exported="true">
        </provider>

索要方:

  1. 在Manifest文件中定义:
   <!-- 不定义则运行会报错 java.lang.IllegalArgumentException: Unknown URL content://com.example.databasetest.provider/book--> 
	<!--要访问的程序的全包名 -->
    <queries>
        <package android:name="com.example.databasetest" />
    </queries>

  1. 确定要操作数据的Uri
  2. 调用getContentResolver()下的方法访问数据库

7.3.2 具体流程

创建自己的内容提供器:

  • 在同一个包下 右键 New->Other->ContentProvider自动创建,不用配置< provider >标签
  • 在同一个包下 手动自定义一个类继承于ContentProvider并重写6个抽象方法
方法说明
onCreate()用于初始化provider,完成数据库的创建和升级操作
query()返回数据给调用者,调用db.query()方法查询返回一个cursor对象
insert()插入新数据到ContentProvider,返回一个Uri
update()更新ContentProvider中已经存在的数据,返回一个int即受影响的行数
delete()从Content Provider 中删除数据,返回一个int即被删除的行数
getType()返回Content provider数据的MIME类型

MIME字符串格式:

  1. 必须以vnd开头
  2. 如果内容URI以路径结尾,则后接android.cursor.dir/ , 如果内容URI以id结尾,则后接android.cursor.item/
  3. 最后接上vnd .< authority > .< path >
  • 在Manifest.xml文件中配置< provider >标签
			<!-- enabled 是否启用这个内容提供器  exported是否允许外部程序访问-->
            <provider
                  android:name="com.example.<package名>.类名"
                  android:authorities="com.example.<package名>.provider"
                  android:enabled="true"   
                  android:exported="true">             
            </provider>
手动创建代码示例(提供方创建Provider):
public class DatabaseProvider extends ContentProvider {
	//定义常量表示访问哪个表中的所有数据 以及哪个表中的单条数据
    public static final int BOOK_DIR = 0;
    public static final int BOOK_ITEM = 1;
    public static final int CATEGORY_DIR = 2;
    public static final int CATEGORY_ITEM = 3;

    public static final String AUTHORITY = "com.example.databasetest.provider";
    //匹配内容Uri
    private static UriMatcher uriMatcher;

    private MyDatabaseHelper dbHelper;
    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
        uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
        uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
        uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }

    public DatabaseProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int deleteRows = 0;
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                deleteRows = db.delete("Book",selection,selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deleteRows = db.delete("Book","id=?",new String[]{bookId});
                break;
            case CATEGORY_DIR:
                deleteRows = db.delete("Category",selection,selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deleteRows = db.delete("Category","id=?",new String[]{categoryId});
                break;
            default:
                break;
        }

        return deleteRows;
    }

    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category";
            default:
                break;
        }
        return null;
    }


    //添加数据
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Uri uriReturn = null;

        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("Book",null,values);
                uriReturn = uri.parse("content://"+AUTHORITY+"/book/"+newBookId);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long newCategoryId = db.insert("Category",null,values);
                uriReturn = uri.parse("content://"+AUTHORITY+"/category/"+newCategoryId);
                break;
            default:
                break;
        }
        return uriReturn;
    }

    @Override
    public boolean onCreate() {

        dbHelper = new MyDatabaseHelper(getContext(),"BookStore.db",null,2);
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {

        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = null;

        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                cursor = db.query("Book",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query("Book",projection,"id=?",new String[]{bookId},null,null,sortOrder);
                break;
            case CATEGORY_DIR:
                cursor = db.query("Category",projection,selection,selectionArgs,null,null,sortOrder);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query("Category",projection,"id=?",new String[]{categoryId},null,null,sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }


    //更新数据
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int updateRows = 0;

        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                updateRows = db.update("Book",values,selection,selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updateRows = db.update("Book",values,"id=?",new String[]{bookId});
                break;
            case CATEGORY_DIR:
                updateRows = db.update("Category",values,selection,selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updateRows = db.update("Category",values,"id=?",new String[]{categoryId});
                break;
            default:
                break;
        }
        return updateRows;
    }
}

以上类中使用到的方法解析:

URI的格式:

  • 以路径结尾就表示期望访问该表中所有的数据
  • 以id结尾就表示期望访问该表中拥有相应id的数据

以通配符的方式来匹配:

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

借助UriMatcher类来匹配内容Uri:uriMatcher.addURI()接收三个参数,返回值为能够匹配Uri对象所对应的自定义代码:

  • authority: com.example.<package名>.provider
  • path: table名 or table名/id
  • 自定义代码: int类型

Uri对象的getPathSegments()方法:会将内容URI权限之后的部分以"/"进行分隔,得到一个字符串列表
第0个位置存放的是路径,第1个位置存放的是id

索要方代码示例:
//在方法中或按钮点击事件中

 //确定好要访问的uri
 uri = Uri.parse("content://com.example.databasetest.provider/book");
//TODO: 组装数据  ContenValues

//调用一系列方法进行CRUD
getContentResolver().insert()
getContentResolver().update()
getContentResolver().delete()
getContentResolver().query()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值