Android学习笔记(十四)——内容提供器

【第一部分】历史文章:
Android学习笔记(一)——创建第一个Android项目
Android学习笔记(二)android studio基本控件及布局(实现图片查看器)
Android学习笔记(三)android studio中CheckBox自定义样式(更换复选框左侧的勾选图像)
Android学习笔记(四)Android 中Activity页面的跳转及传值
Android学习笔记(五)——Toast提示、Dialog对话框、Menu菜单
Android学习笔记(六)——自定义ListView布局+AsyncTask异步任务
Android学习笔记(七)——数据存储(共享参数SharedPreferences)
Android学习笔记(八)——数据存储(SD卡文件操作)
Android学习笔记(九)——网络技术
Android学习笔记(十)——实现新闻列表案例
Android学习笔记(十一)——一些高级控件的使用
Android学习笔记(十二)——数据存储(SQLite数据库)
Android学习笔记(十三)——数据存储(LitePal操作数据库)

【第二部分】主要问题解决:
Android Studio(存)读取不了SD卡上的文件——【已解决】


第一行代码

内容提供器介绍

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

在正式学习内容提供器之前,我们先了解一下Android运行时的权限:

  • 在Android6.0版本之前,权限都是一条龙服务的,只要用户安装完,AndroidManifest清单上申请的权限都会被系统默认授权,并且授权后也撤销不了。
  • 在Android 6.0版本之后,权限分为普通权限危险权限

普通权限:这个权限类型并不直接威胁到用户的隐私,可以直接在AndroidManifest清单里注册,系统会帮我们默认授权。
危险权限:这个权限可能会触及到用户隐私或者对设备安全性造成影响。
注:危险权限不仅需要在AndroidManifest清单里注册,同时在运行的时候,需要向系统请求授权。危险权限共9组24个权限(见下表),一旦用户授权某个危险权限,那么该权限所对应权限组中所有其它权限也会同时被授权。
在这里插入图片描述
注:

  • 除了上表中的全部危险权限外,剩余的都是普通权限。
  • 当我们使用一个权限时,如果是上表中的危险权限,就需要进行运行时权限处理;如果不是上表中的权限,只需要在AndroidManifest.xml文件中添加以下权限声明就可以了。

在程序运行时申请权限

下面是一个拨打电话功能的实现:
关键代码
在这里插入图片描述
然后修改AndroidManifest.xml文件,声明权限:

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

具体分析
1、首先我们用ContextCompat.checkSelfPermission()来判断用户是否已经给我们授权。
checkSelfPermission()方法:接收两个参数,第一个参数是Context;第二个参数是:具体的权限名;这里我们用的是打电话的权限名: Manifest.permission.CALL_PHONE
2、我们使用方法的返回值与PackageManager.PERMISSION_GRANTED做比较,相同就说明用户已经授权了,不同则表示用户没有授权。
3、

  • 如果授权了,使用Intent完成你想要的操作就可以了。
  • 如果没有授权,那么我们调用ActivityCompat.requestPermissions方法来向用户申请权限。
    requestPermissions方法:接受三个参数,第一个参数是Activity的实例;第二个参数是String数组,把要申请的权限给他;第三个参数是请求码。
    当系统弹出对话框询问完用户是否申请授权后,会回调onRequestPermissionResult()方法,授权的结果封装在了grantResults参数中。最后判断最后的授权结果,用户同意则会调用call()方法拨打电话,用户拒绝,则会弹出提示信息。
    点击CALL按钮,没有同意授权则会弹出失一个操作失败的提示(下图2);如果同意授权结果如(下图3)。
    在这里插入图片描述

    访问其他程序中的数据


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

当一个应用程序通过内容提供器对其数据提供了外部访问接口,那么任何其他的应用程序就 都可以对这部分数据进行访问。Android 系统中自带的电话簿、短信、媒体库等程序都提供 了类似的访问接口,这就使得第三方应用程序可以充分地利用这部分数据来实现更好的功能。

1、ContentResolver类的用法
对于每个程序来说,如果想要访问内容提供器中的数据,一定要借助ContentResolver类,可以通过Context中的getContentResolver方法得到该类的实例。ContentResolver中提供了一系列的方法用于对数据进行CRUD操作。

  • insert()方法用于添加数据。
  • update()方法用于更新数据。
  • delete()方法用于删除数据。
  • query()方法用于查询数据。

ContentResolver中的增删改查方法接受的是Uri参数,被称为内容URI。由authoritypath两部分组成的。

  • authority:一般包名.provider。
  • path:表名。

在得到了内容URI字符串之后,我们需要把它解析成Uri对象才可以作为参数传入。

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

只需要调用Uri.parse()方法,就可以将内容URI字符串解析成Uri对象了。
下面是具体的四种CRUD操作介绍:
1.1、查询操作

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

参数说明:

query()方法参数 对应的SQL部分 描述
uri from table_name 指定查询某个应用程序下的某一张表
projection select column1,column2 指定查询的列名
selection where column=value 指定where的约束条件
selectionArgs - 为where中的占位符提供具体的值
sortOrder order by column1,column2 指定查询结果的排序方式

注:查询后返回的是Cursor对象,读取的方式是通过游标的位置来遍历Cursor的所有行,然后取出相应列的值。
示例:

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

1.2、添加操作
将添加的数据组装到ContentValues中,调用ContentResolver的insert()方法,将Uri和ContentValues作为参数传入。

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

1.3、更新操作

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

1.4、删除操作

getContentResolver().delete(uri,"column2=",newString[]{"1"});  

下面是一个读取手机联系人的例子:
首先我们先添加一个手机联系人。填入姓名及手机号,点击右上角的SAVE。
在这里插入图片描述

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" 
    android:id="@+id/activity_main"
    android:layout_width="match_parent" 
    android:layout_height="match_parent"
    tools:context="cn.edu.hznu.contactstest.MainActivity">
    <ListView
        android:id="@+id/contacts_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

</RelativeLayout>

MainActivity.java

package cn.edu.hznu.contactstest;
import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
    ArrayAdapter<String> adapter;
    List<String> contactsList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ListView contactsView = (ListView) findViewById(R.id.contacts_view);
        adapter = new ArrayAdapter<String>(this, android.R.layout. simple_list_item_1, contactsList);
        contactsView.setAdapter(adapter);
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_CONTACTS }, 1);
        } else {
            readContacts();
        }
    }
    private void readContacts() {
        Cursor cursor = null;
        try {
            // 查询联系人数据
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    // 获取联系人姓名
                    String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                    // 获取联系人手机号
                    String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                    contactsList.add(displayName + "\n" + number);
                }
                adapter.notifyDataSetChanged();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    readContacts();
                } else {
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }
}

在AndroidManifest.xml声明权限

在这里插入图片描述
注意:在readContacts()方法中,使用了ContentResolver的query()方法来查询系统的联系人。却没有调用Uri.parse()方法解析内容URI字符串。因为ContactsContract.CommonDataKinds.Phone.CONTENT_URI类已经帮我们做了封装,提供了CONTENT_URI常量,这个常量是使用Uri.parse()方法解析出来的结果。
在这里插入图片描述
运行程序:
在这里插入图片描述


若文章中有错误的地方欢迎大家反馈或者留言,十分感谢!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章