当前位置:网站首页 > 数据科学与大数据 > 正文

《第一行代码》 第七章:跨程序共享数据-内容提供器

一,运行时权限的理解

Android现在将所有的权限归成了两类,一类是普通权限,一类是危险权限。普通权限指的是那些不会直接威胁到用户的安全和隐私的权限,对于这部分权限申请,系统会自动帮我们进行授权、而不需要用户再去手动操作了。

1,普通权限

系统自动授权的,无需申请。

这样一来,我们在安装程序的时候,就会告知用户,该程序会获取设备的啥权限。

2,危险权限

例如打开摄像头,麦克风等的权限,就需要在已经安装app后,用户使用时弹窗让用户授权。
对于危险权限,我们只要在AndroidManifest.xml文件中加两句权限声明:
在这里插入图片描述

3,在程序运行时申请权限

做个例子,申请拨打电话的。
第一步:新建项目runtimepermissiontest
第二步:修改布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/make_call" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="拨打电话"/> </LinearLayout> 

第三步:Menifest文件申请权限

public class MainActivity extends AppCompatActivity { 
    @Override protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button makeCall=(Button) findViewById(R.id.make_call); makeCall.setOnClickListener(new View.OnClickListener(){ 
    @Override public void onClick(View view) { 
    //判断是否授权 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(){ 
    try { 
    Intent intent =new Intent(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:10086")); startActivity(intent); }catch(SecurityException e){ 
    e.printStackTrace(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 
    switch(requestCode){ 
    case 1: if(grantResults.length>0 && grantResults[0]== PackageManager.PERMISSION_GRANTED){ 
    call(); }else{ 
    Toast.makeText(this, "你拒绝了授权", Toast.LENGTH_SHORT).show(); } break; default: } } } 

二,内容提供器

内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访数据的安全性。目前,使用内容提供器是 Android 实现跨程序共享数据的标准方式。
不同于文件存储和 SharedPreferences存储中的两种全局可读写操作模式,内容提供器可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。
内容提供器的用法一般有两种:

一种是使用现有的内容提供器来读取和操作相应程序中的数据 另一种是创建自己的内容提供器给我们程序的数据提供外部访问接口。 

1,ContentResolver的基本用法,使用现有的内容提供器

对于每一个应用程序来说,如果想要访问内容提供器中共享的数据,就一定要借助 Content.Resolver 类,可以通过 Context 中的 getContentResolver()方法获取到该类的实例。Content-Resolver 中提供了一系列的方法用于对数据进行CRUD操作,

insert()方法用于添加数据 update()方法用于更新数据, delete()方法用于删除数据, query()方法用于查询数据。 
content://com.example.app.provider/tablel content://com.example.app.provider/table2 

在得到了内容 URI字符串之后,我们还需要将它解析成 Uri 对象才可以作为参数传人。解析的方法也相当简单,代码如下所示:

Uri uri = Uri.parse("content://com.example.app.provider/tablel") 

只需要调用Uriparse()方法,就可以将内容 URI字符串解析成Uri对象了现在我们就可以使用这个 Uri 对象来查询 table1 表中的数据了,代码如下所示:

Cursor sursor=getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder); 

在这里插入图片描述
查询完成后返回的仍然是一个 Cursor 对象,这时我们就可以将数据从 Cursor 对象中逐个读取出来了。读取的思路仍然是通过移动游标的位置来遍历 Cursor 的所有行,然后再取出每行中相应列的数据,代码如下所示:

if (cursor != null) { 
    while (cursor.moveToNext()) { 
    String column]= cursor.getString(cursor.getColumnIndex("column1")); int column2 = cursor.getInt(cursor.getColumnIndex("column2")); } cursor.close(); } 

我们先来看看如何向 table1 表中添加一条数据,代码如下所示:

ContentValues values = new ContentValues(); values.put("column]"“text"); values.put("column2"1); getContentResolver().insert(uri, values); 

现在如果我们想要更新这条新添加的数据,把column1的值清空,可以借助ContentResolver的 update()方法实现,代码如下所示:

ContentValues values = new ContentValues(); values.put("column]"""); getContentResolver().update(uri, values,"column1 = ? and column2 = ?",newString[] { 
   "text""1"}); 
getContentResolver(),delete(uri,"column2 = ?"new Stringl][""); 

2,创建自己的内容提供器

上文说了如何在自己的程序中访问其他应用程序的数据。那提供给其他程序使用的访问接口的数据又如何实现呢?
需要我们创建自己的内容提供器。
如果想要实现跨程序共享数据的功能,官方推荐的方式就是使用内容提供器,可以通过新建一个类去继承 ContentProvider 的方式来创建一个自己的内容提供器。ContentProvider 类中有6个抽象方法,我们在使用子类继承它的时候,需要将这 6个方法全部重写。新建 MyProvider 继承自 ContentProvider,代码如下所示:

public class MyProvider extends ContentProvider { 
    @Override public boolean onCreate() { 
    return false; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) { 
    return null; } @Nullable @Override public String getType(@NonNull Uri uri) { 
    return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) { 
    return null; } @Override public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) { 
    return 0; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) { 
    return 0; } } 

1.onCreate()

初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作,返回 true表示内容提供器初始化成功,返回 false 则表示失败。注意,只有当存在 ContentResolver 尝试访问我们程序中的数据时,内容提供器才会被初始化。 

2.query()

从内容提供器中查询数据。使用uri参数来确定查询哪张表,proiection 参数用于确定查询哪些列,selection 和selectionArgs 参数用于约束查询哪些行,sortOrder 参数用于对结果进行排序,查询的结果存放在 Cursor 对象中返回。 

3.insert()

向内容提供器中添加一条数据。使用 uri 参数来确定要添加到的表,待添加的数据保存在values 参数中。添加完成后,返回一个用于表示这条新记录的URI。 

4.update()

更新内容提供器中已有的数据。使用 uri 参数来确定更新哪一张表中的数据新数据保存在values 参数中,selection 和 selectionArgs 参数用于约束更新哪些行,受影响的行数将作为返回值返回。 

5.delete()

从内容提供器中删除数据。使用 uri 参数来确定删除哪一张表中的数据,selection 和selectionArgs 参数用于约束删除哪些行,被删除的行数将作为返回值返回 

6.getType()

根据传人的内容URI来返回相应的MIME类型 
以路径结尾就表示期访问该表中所有的数据:content://com.example.app.provider/tablel id 结尾就表示期望访问该表中拥有相应 id 的数据。content://com.example.app.provider/tablel/1 

我们可以通过通配符的方式来分别匹配这两种格式的内容uri,规则如下:

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

所以,一个能够匹配任意表的内容 URI格式就可以写成:

content://com.example.app.provider/* 

而一个能够匹配 table1表中任意一行数据的内容URI格式就可以写成

content://com.example.app.provider/tablel/# 
public class MyProvider extends ContentProvider { 
    public static final int TABLE1_RIR=0; public static final int TABLE1_TIME=1; public static final int TABLE2_DIR=2; public static final int TABLE2_TIME=3; private static UriMatcher uriMatcher; static { 
    //静态代码块, uriMatcher=new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI("com.example.app.provider","table1",TABLE1_RIR); uriMatcher.addURI("com.example.app.provider","table1/#",TABLE1_TIME); uriMatcher.addURI("com.example.app.provider","table2",TABLE2_DIR); uriMatcher.addURI("com.example.app.provider","table1",TABLE2_TIME); } @Override public boolean onCreate() { 
    return false; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) { 
    switch(uriMatcher.match(uri)){ 
    case TABLE1_RIR: //查询table1表中的所有数据 break; case TABLE1_TIME: //查询table1表中的单条数据 break; case TABLE2_DIR: //查询table2表中的所有数据 break; case TABLE2_TIME: //查询table2表中的单条数据 break; default: break; } return null; } @Nullable @Override public String getType(@NonNull Uri uri) { 
    return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) { 
    return null; } @Override public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) { 
    return 0; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) { 
    return 0; } } 

除此之外,还有一个方法你会比较陌生,即 getType()方法。它是所有的内容提供器都必须提供的一个方法,用于获取Uri 对象所对应的 MIME类型。一个内容 URI所对应的 MIME 字符串主要由3部分组成,Android 对这3个部分做了如下格式规定。

必须以 vnd 开头。 如果内容 URI以路径结尾,则后接 android.cursor.dir/, 如果内容 URI以id结尾,则后接android.cursor.item/。 最后接上 vnd,<authority>,<path>

所以,对于 content://com.exampleapp.provider/tablel 这个内容 URI,它所对应的 MIME类型就可以写成:

vnd.android.cursor.dir/vnd.com.example.app.provider.tablel 

对于 content://comexampleappprovider/tablel/1 这个内容 URI,它所对应的MIME类型就可以写成:

vnd.android.cursor.item/vnd.com.example.app.provider.tablel 

现在我们可以继续完善MyProvider 中的内容了,这次来实现getType()方法中的逻辑,代码如下所示:

 @Nullable @Override public String getType(@NonNull Uri uri) { 
    switch(uriMatcher.match(uri)){ 
    case TABLE1_RIR: return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1"; case TABLE1_TIME: 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_TIME: return "vnd.android.cursor.item/vnd.com.example.app.provider.table2"; default: break; } return null; } 

到这里,一个完整的内容提供器就创建完成了,现在任何一个应用程序都可以使用ContentResolver 来访问我们程序中的数据。

到此这篇《第一行代码》 第七章:跨程序共享数据-内容提供器的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • jdbc使用java连接数据库学习笔记2024-12-01 08:54:08
  • 表格当前行-对象数据传递给弹框 & 父组件传对象数据给子组件 & 接口写法-增删改查-post-get-delete2024-12-01 08:54:08
  • js中,封装一个判断所有数据类型的方法getType,入参为任意变量,返回值为该参数类型的字符串形式2024-12-01 08:54:08
  • js之对象分类、数据类型分类和存储位置、函数 function介绍、变量的作用域2024-12-01 08:54:08
  • Node学习(五)022-处理静态资源——自定义中间件处理post提交的数据 & 将接收到的数据,赋值给req.body & 使用express和body-parser中间件处理post提交数据2024-12-01 08:54:08
  • 《第一行代码》 第六章:数据库与存储方案2024-12-01 08:54:08
  • 响应式数据2024-12-01 08:54:08
  • vuex实例方法replaceState解决vuex页面刷新数据丢失问题2024-12-01 08:54:08
  • js基础-(一)-基本数据类型2024-12-01 08:54:08
  • JavaScript技术总结9:轮询数据2024-12-01 08:54:08
  • 全屏图片