1.0 在我的上一篇,相当于运行时权限的初体验,这一篇主要是怎么访问其他程序的数据,比如接下来的这个例子,将手机联系人应用中的联系人数据,读取出来,在APP中展示。
2.0 一个应用程序通过内容提供器可以对其数据提供外部访问接口,其他任何应用程序都可以对这部分数据进行访问。
Android系统自带的电话簿、短信、媒体库等程序都提供了类似的访问接口,这使得第三方应用程序可以充分地利用这部分数据来实现更好的功能。
3.0对于每一个应用程序来说,如果想访问内容提供器中共享的数据,就一定要借助ContentResolver类,可以通过Context中的getContentResolve方法获得该类的实例。
ContentResolver中提供了一系列的方法用于增删查改操作。不错,和SQLiteDatabase很相似。
不同于SQLiteDatabase,ContentResolver使用的是内容URI参数。
标准格式为:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
内容URI主要由2部分组成:authority和path。
-
authority:用于对不同应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式来命名。
比如某个程序包名为com.example.app
,该程序的authority就可以命名为com.example.app.provider
-
path:用于对同一应用程序中不同的表做以区分,通常添加在authority后面。
4.0 闲话放最后说,先上车直接干代码,新建项目ContactsTest,目录如下:
![](https://img.haomeiwen.com/i16102290/abdd1d427aa3bf46.png)
5.0 我们现在模拟器联系人中添加两位联系人资料:
![](https://img.haomeiwen.com/i16102290/52baf5eba4b7fd66.png)
![](https://img.haomeiwen.com/i16102290/b4282005d28fb8db.png)
6.0 在布局文件activity_main.xml添加一个ListView,这里就不用RecyclerView,虽好但也增加了代码量,这里重点也不在这个。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ListView
android:id="@+id/contacts_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"></ListView>
</android.support.constraint.ConstraintLayout>
7.0 接着修改MainActivity.java中的代码:
package com.example.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);
//老老实实3步布置好一个简单的ListView列表适配器。
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(MainActivity.this,
Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
//注意,换了权限,READ_CONTACTS危险权限,获取联系人信息
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_CONTACTS}, 1);
} else {
readContacts();
}
}
private void readContacts() {
Cursor cursor = null;
try {
//查询联系人数据
//这里没有传入一个URI参数,没有调用Uri.parse()方法去解析一个内容URI字符串
//ContactsContract.CommonDataKinds.Phone类已经做好了封装
//.CONTENT_URI常量就是Uri.parse()解析出来的结果
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();
}
}
}
//调用完requestPermissions()方法后,系统会弹出一个权限申请的对话框
// 无论结果如何,最终都会回调onRequestPermissionsResult()方法
//授权的结果,会封装在grantResults中。
// 判断一下,如果同意了授权就打电话,没有就凉凉了……
@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, "抱歉,没有该权限!", Toast.LENGTH_SHORT).show();
}
}
}
}
这里没有传入一个URI参数,没有调用Uri.parse()方法去解析一个内容URI字符串,ContactsContract.CommonDataKinds.Phone类已经做好了封装,CONTENT_URI常量就是Uri.parse()解析出来的结果。
最后千万不要忘记将Cursor对象关闭掉。
9.0 到这里还差最后一步,读取系统联系人的权限千万不能忘记声明。修改AndroidManifest.xml中的代码,增加<uses-permission android:name="android.permission.READ_CONTACTS"/>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.contactstest">
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
10.0 运行,如下:
![](https://img.haomeiwen.com/i16102290/cf5e101d12f12ccf.png)
点击“拒绝”:
![](https://img.haomeiwen.com/i16102290/67b0cf8e9a0886d3.png)
重新运行该程序,点击“允许”:
![](https://img.haomeiwen.com/i16102290/207f1a5bf200408d.png)
11.0 这里继续补上枯燥的理论知识。
既然已经知道内容URI字符串的格式,那么加载解析成Uri对象可以作为参数传入的代码格式如下:
Uri uri = Uri.parse("content://com.example.app.provider/table1")
只需要调用Uri.parse()方法,就可以将内容URI字符串解析成Uri对象。
12.0 查询table1表中的数据:
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.getColumnIndex("column1"));
int column2 =
cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
13.0 讲完最难的查询操作,增加、修改、删除操作更不在话下。
向table1表中添加一条数据:
ContentValues values = new ContentValues();
values.put("column1","text");
values.put("column2",1);
getContentResolver().insert(uri,values);
可以看到,将待添加的数据封装到ContentValues 中,然后调用getContentResolver的insert()方法,将Uri和ContentValues 作为参数传入即可。
14.0 向table1表中更新一条数据:
比如把 13.0 中添加的数据中column1的值清空:
ContentValues values = new ContentValues();
values.put("column1","");
getContentResolver().update(uri,values,"column1 = ? and column2 = ?",new String[] {"text","1"});
15.0 最后,把table1表中这条数据删除掉:
getContentResolver().delete(uri,"column2 = ? ",new String[] {"1"});
END
网友评论