活动地址:CSDN21天学习挑战赛
跨程序共享数据
1、内容提供器简介
内容提供其(Content Provider)主要 用于在不同引用程序之间实现数据共享功能,他提供了一套 完整的机制,允许一个程序访问另外一个程序中的数据,并且还能保证访问的数据安全性
不同文件存储和SharedPreferences存储中的两种全局读写操作模式
内容提供其可以选择只对哪一部分进行操作
2、运行时权限
权限分成两种权限 危险权限和普通权限
危险权限一共9组24个 其他的全是普通权限
http://developer.android.com/reference.android/Mainfest.permission,html 中可以查看完整的权限列表
3、程序运行时申请权限
首先创建一个RuntimePermissionTest项目
首先申请一个拨打电话的权限 CALL_PHONE
之前学过的ACTION_DIAL打开电话键盘是不需要需要授权的
通过点击按钮进行设置
首先创建一个显示的Intent 并将其action设置成ACTION_CALL则是一个系统内置的打电话的动作,并且该动作需要获取相关权限
public void Apply_callphone(View view) {
Intent intent=new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
然后外面前往AndroidManifest.xml文件中设置权限
添加权限即可
添加该权限后 还需要用户进行手动确认权限,因此我们就先要确认用户是否授权
我们只需要向调用ContextCompat.checkSelfPermission()这个方法传入一个 Context 和拨打电话的权限名字 Manifest.permission.CALL_PHONE 他就会返回一个是否授权的只我们就用它和PackageManager.PERMISSION_GRANTED作比较,如果 相等就代表已经授权可以直接进行打电话的动作,
不相等就要进行授权否则程序崩溃,我们就通过 ActivityCompat.requestPermissions()该方法调出一个对话框 让用户进行确认
这个方法接收三个参数
1、Context
2、一个String数组 数组中我们放入打电话的权限名即可
3、requestCode 一个请求码 只要是唯一值即可
调用完这个方法后就会返回一个权限申请的对话框,用户可以对其进行确定和取消
但是不管是那种结果最终都会回到onRequestPermissionsResult()这个方法
因此我们需要重写一下这个方法,利用我们刚刚所传入的请求与码 进行操作
注意 方法在ActivityCompat.requestPermissions()申请权限之后调用 因此申请的结果都会封装到grantResults数组中 我们访问数组中的第一个值如果结果等于PackageManager.PERMISSION_GRANTED就代表授权成功否则就提示授权失败
演示代码如下
// 该方法为点击事件的方法
public void Apply_callphone(View view) {
if (ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){
// 授权结果不等于 PackManager.PERMISSION.GRANTED 则进行调用
// 弹出授权对话框
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},1);
}else{
// 否则就代表授权成功了
try {
Intent intent=new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:15313396550"));
startActivity(intent);
}catch (Exception e){
Toast.makeText(this, "授权失败了,亲", Toast.LENGTH_SHORT).show();
}
}
}
}
// 重写的onRequestPermissionsResult
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 利用请求码进行操作
switch (requestCode){
case 1:
if (grantResults[0]==PackageManager.PERMISSION_GRANTED){
try {
Intent intent=new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:15313396550"));
startActivity(intent);
}catch (Exception e){
Toast.makeText(this, "异常错误,亲", Toast.LENGTH_SHORT).show();
}
}else {
// 取消授权或者授权失败了进行调用
Toast.makeText(this, "授权失败了,亲", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
4、Content Resolver 的i基本用法
对于一个程序来说,想要访问内容提供其中的共享数据就一定要借助Content Resolver类,Content中的getContentResolver()方法就可以得到该类的实例,Content中也是提供了CRUD方法对数据进行增删改查的,
insert() 查询数据
update() 更新数据
delete() 删除数据
query() 查询数据
ContentResolver中的增删改查不同于Sqllite他无需查询表明 而是使用了URI参数进行代替,内容URI给内容提供其中提供数据建立的唯一标识符,它由两个部分组成
1、authority 该参数用于区分不同的应用程序,一般采用程序包名的方式命名
2、path 用于对统一程序中不同的表做区分的通常加到authority后面
然后我们还需要在组成的字符串前面加上协议声明
例如一个程序包名为 com.example.app.这个程序包下有两个表table1和table2
那URI标准组成格式如下
content://com.example.app.provider/table2
content://com.example.app.provide/table1
这样可以非常清楚的表述出来外面想要访问的数据了
得到URI字符串后外面还需要将其解析成Uri对象
Uri uri=Uri.Parse(“content://com.example.app.provider/table2”)
然后就可以利用改产线查询数据了
// 查询数据
Cousor cousor=getContentResolver().query{
uri,
projection,
selection,
selectionArgs,
sortOrder
}
query()方法参数 | 对应SQL部分 | 描述 |
---|---|---|
uri | from table name | 指定查询的表 |
projection | select column1,column2 | 指定查询的列名 |
selection | where column=value | 指定where的约束条件 |
selectionArgs | 为where中的占位符提供具体值 | |
orderBy | order by column1,column2 | 指定查询结果排序方式 |
最后返回的对象依旧是一个 Cousor对象
查询数据:
我们通过移动游标的位置来遍历Cursor的所有行,然后再取出每一行中相对应的数据
if(cursor!=null){
where(cursor.moveTotext()){
String column1=cursor.getString(cursor.getColumnIndex("colunm1"));
int colunm2=cursor.getInt(cursor.getColumnText("column2"))
}
}
插入数据:
依旧利用ContentValues来传递与组装数据
ContentValues Values=new ContentValues();
Values.put("column1","text");
Values.put("column2",1)
getCotentResolver().insert(uri,values)
更新数据:
如果我们想要将column1的值清空,可以借助update()方法实现
我们利用selection 和 selectionArgs参数便可以实现
ContentValues values=new ContentValues();
values.put("column1","");
getContenResolver().update(uri,values,"conlumn1=? and column2=?",new String[]{"text","1"})
删除数据
我们也可以调用 delete()房啊删除这条数据
getContentResolver(),delete(uri,"column2=?",new String[]{"1"});
5、读取系统联系人
本次实践将会联系前几章节的类容
首先我们创建Contacts Text项目
我们本次使用ListView将获取到的信息展现出来
因此我们需要准备一个 Furit类用于存储获取到信息类容
然后再准备一个ListViewAdapter的一个适配器 和准备一个ListView子元素的布局
Furit类代码如下
public class Furit {
String Name;
String Phone;
public Furit(String name ,String phone){
this.Name=name;
this.Phone=phone;
}
public String getName() {
return Name;
}
public String getPhone() {
return Phone;
}
}
ListView适配器代码如下
public class ListViweAdapter extends ArrayAdapter<Furit> {
int itemID=0;
public ListViweAdapter(@NonNull Context context, int resource, @NonNull List<Furit> objects) {
super(context, resource, objects);
itemID=resource;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
TextView Name=null;
TextView Phone=null;
View view;
Furit furit=getItem(position);
if (convertView==null){
view=LayoutInflater.from(getContext()).inflate(itemID,parent,false);
Name=view.findViewById(R.id.main_listview_item_name);
Phone=view.findViewById(R.id.main_listview_item_phone);
}else {
view=convertView;
}
Name.setText(furit.getName());
Phone.setText(furit.getPhone());
return view;
}
}
ListView子元素布局如下
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp"
android:orientation="vertical"
>
<TextView
android:id="@+id/main_listview_item_name"
android:layout_marginLeft="30dp"
android:textSize="30dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name"/>
<TextView
android:id="@+id/main_listview_item_phone"
android:textSize="30dp"
android:layout_marginLeft="50dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Phone"></TextView>
</LinearLayout>
然后我们在MainActivity的布局项目下添加布局
代码如下
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/main_activity_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
然后再将准备的适配器传递给ListView
代码如下
public List<Furit> list=new ArrayList<>();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView=findViewById(R.id.main_activity_listview);
adapter=new ListViweAdapter(this,R.layout.listview_item,list);
listView.setAdapter(adapter);
}
然后我们正式需要的功能是 启动获取电话本中的信息、点击拨打对应人电话
因此我们需要两个权限一个是电话本的获取权限、一个是拨打电话的权限
前往AndroidManifest.xml文件进行权限声明首先,不然程序会报错
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
然后前往MainActivity中的初始方法中对电话本的获取权限进行一个检测
代码如下
// 查询权限是否获取
if(ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CONTACTS)!=PackageManager.PERMISSION_GRANTED){
// 检测到权限未被授予 请求用户授予权限
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
}else {
// 用于查询将查询到的信息 添加给适配器
readContacts();
}
然后重写onRequestPermissionsResult这个方法 授权后不管最结果都会执行这个方法
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){
readContacts();
}else {
Toast.makeText(this,"授权失败,请授权后再查询!",Toast.LENGTH_SHORT).show();
}
break;
}
}
完成后 就可以撰写readContacts方 用于获取联系人信息
private void readContacts(){
// 构建一个cursor对象
Cursor cursor=null;
// 电话本已经提供了一个用于获取信息的uri 对象
Uri uri= ContactsContract.CommonDataKinds.Contactables.CONTENT_URI;
try {
// 该对象用于执行 Content Resolver中的查询操作 传入五个值
cursor=getContentResolver().query(uri,null,null,null,null);
if (cursor!=null){
// 移动游标 没查询完一行向下移动一次
while(cursor.moveToNext()){
@SuppressLint("Range") String UserName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));// 获取名字
@SuppressLint("Range") String UserPhone=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));// 获取信息
// 传入list中
Furit furit=new Furit(UserName,UserPhone);
list.add(furit);
}
// 刷新ListView
adapter.notifyDataSetChanged();
}
}catch (Exception e){
Toast.makeText(this,"信息获取异常",Toast.LENGTH_SHORT).show();
}finally {
if (cursor!=null){
// 关闭 Cursor
cursor.close();
}
}
}
完成之后我们再写一给 点击拨号的功能、
回到 MainActivity中的初始方法onCreate中
// 创建ListView 子元素点击事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Phone 为全局变量
phone=list.get(position).getPhone();
Toast.makeText(MainActivity.this, phone, Toast.LENGTH_SHORT).show();
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){
// 检测到权限未被授予 请求用户授予权限
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CALL_PHONE},2);
}else {
Intent intent=new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:"+phone));
startActivity(intent);
}
}
});
然后回到 onRequestPermissionsResult方法中的 Switch 添加一个请求码即可
case 2:
if (grantResults.length>0&&grantResults[1]==PackageManager.PERMISSION_GRANTED){
try {
Toast.makeText(this,grantResults[0]+"",Toast.LENGTH_SHORT).show();
Log.d(TAG,grantResults[1]+"" );
Log.d(TAG,grantResults[0]+"" );
Intent intent=new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:"+phone));
startActivity(intent);
}catch (Exception e){
Toast.makeText(this,"回调执行失败!",Toast.LENGTH_SHORT).show();
}
}else {
Toast.makeText(this,"授权失败,请授权后再查询!",Toast.LENGTH_SHORT).show();
}
break;