Content Provider
主要用于在不同App间共享数据。
权限申请
App中某些功能的执行需要申请Android系统权限,如打电话等。
AndroidManifest.xml中可以申请权限。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bl.blandroidpro">
<uses-permission android:name="android.permission.CALL_PHONE" />
</manifest>
Android权限机制
Android 6.0之前安装app时,提示app需要获取某些权限,其中包括一些个人隐私相关,如地理位置等,但是这些app日常粘度很高,还不能不安,所以用户只能被迫同意安装。
Android开发团队也意识到了这个问题,于是Android 6.0开始引入了运行时权限。但是安卓的权限很多,有上百个,如果让用户一个个授权也很繁琐,所以Android团队将权限分成两部分:普通权限 和 危险权限。只有危险权限才需要用户授权。
危险权限
Android的危险权限 一共9组 24个。每个权限属于一个权限组,同意其中一个权限就同意了同组中的所有权限。
-
CALENDAR
READ_CALENDAR
WRITE_CALENDAR - CAMERA
-
CONTACTS
READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS -
LOCATION
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION -
MICROPHONE
RECORD_AUTIO -
PHONE
READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS -
SENSORS
BODY_SENSORS -
SMS
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS -
STORAGE
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
运行时权限
运行时权限就是app在安装时不需要授权,在运行时具体用到某些功能时,再由用户决定是否授权,程序不可以擅自做主。
// 校验是否获取某权限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
// 没有获取则请求权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS}, 1);
} else {
// 已经获取权限则做相应业务
readContacts();
}
@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) {
readContacts();
// 不同意授权
} else {
Toast.makeText(this,
"You denied the permission", Toast.LENGTH_LONG).show();
}
break;
default:
break;
}
}
访问其他app的数据
ContentResolver 通过该类访问其它app的数据。该类提供了CRUD操作的相关方法。以Uri作为参数。
- insert() 插入数据
- update() 修改数据
- delete() 删除数据
- query() 查询数据
Uri参数格式及组成结构
以path结尾表示访问该表所有数据
以id结尾表示仅访问此条数据
'*':匹配任意长度字符
'#':匹配任意长度数字
- scheme 协议
- authority 区分应用程序,以包名命名。
- path 对同一应用不同表做区分。
- id 对同一表下的记录做区分
// path结尾
Uri uri = Uri.parse("content://com.example.app.provider/table1");
// id结尾
Uri uri = Uri.parse("content://com.example.app.provider/table2/1");
// path *
"content://com.example.app.provider/*";
// id #
"content://com.example.app.provider/table2/#";
// 查询是通过query方法返回的游标,逐行按字段获取想要的信息
// 获取联系人列表
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 disName = cursor.getString(cursor.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
));
// 获取联系人号码
String number = cursor.getString(cursor.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER
));
contactList.add(disName + "\n" + number);
}
// 刷新列表
adapter.notifyDataSetChanged();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
// 关闭游标
cursor.close();
}
}
}
自定义内容提供器
需要继承ContentProvider类,重写其中6个方法
- onCreate()
- query()
- insert()
- update()
- delete()
-
getType() 获取MIME类型
MIME类型由3部分组成 - 必须以vnd开头
- 以path结尾,后接 android.cursor.dir/, 以id结尾android.cursor.item/
- 最后接上vnd.'authority'.'path'
// path结尾
vnd.android.cursor.dir/vnd.com.exxample.app.provider.table1
// id结尾
vnd.android.cursor.item/vnd.com.exxample.app.provider.table2
示例代码
MyContentProvider.java
public class MyContentProvider 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 String URI_BOOK;
public static String URI_CATEGORY;
private static final String AUTHORITY = "com.bl.blandroidpro.provider";
private static final String DB_NAME = "BookStore.db";
private static final String PREFIX_CONTENT = "content://";
private static final String SURFIX_BOOK = "book";
private static final String SURFIX_BOOK_ID = "book/#";
private static final String SURFIX_BOOK_TABLE = "Book";
private static final String SURFIX_CATEGORY = "category";
private static final String SURFIX_CATEGORY_ID = "category/#";
private static final String SURFIX_CATEGORY_TABLE = "Category";
private static final String VND_BOOK_DIR =
"vnd.android.cursor.dir/vnd.com.bl.blandroidpro.provider.book";
private static final String VND_BOOK_ITEM =
"vnd.android.cursor.item/vnd.com.bl.blandroidpro.provider.book";
private static final String VND_CATEGORY_DIR =
"vnd.android.cursor.dir/vnd.com.bl.blandroidpro.provider.category";
private static final String VND_CATEGORY_ITEM =
"vnd.android.cursor.item/vnd.com.bl.blandroidpro.provider.category";
private static UriMatcher sUriMatcher;
private MyDatabaseHelper dbHelper;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, SURFIX_BOOK, BOOK_DIR);
sUriMatcher.addURI(AUTHORITY, SURFIX_BOOK_ID, BOOK_ITEM);
sUriMatcher.addURI(AUTHORITY, SURFIX_CATEGORY, CATEGORY_DIR);
sUriMatcher.addURI(AUTHORITY, SURFIX_CATEGORY_ID, CATEGORY_ITEM);
URI_BOOK = PREFIX_CONTENT + AUTHORITY + "/" + SURFIX_BOOK;
URI_CATEGORY = PREFIX_CONTENT + AUTHORITY + "/" + SURFIX_CATEGORY;
}
public MyContentProvider () {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int deleteRows = 0;
switch (sUriMatcher.match(uri)) {
case BOOK_DIR:
deleteRows = db.delete(SURFIX_BOOK_TABLE, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deleteRows = db.delete(SURFIX_BOOK_TABLE,
"id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
deleteRows = db.delete(SURFIX_CATEGORY_TABLE, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deleteRows = db.delete(SURFIX_CATEGORY_TABLE,
"id = ?", new String[]{categoryId});
break;
default:
break;
}
return deleteRows;
}
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case BOOK_DIR:
return VND_BOOK_DIR;
case BOOK_ITEM:
return VND_BOOK_ITEM;
case CATEGORY_DIR:
return VND_CATEGORY_DIR;
case CATEGORY_ITEM:
return VND_CATEGORY_ITEM;
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri uriReturn = null;
switch (sUriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert(SURFIX_BOOK_TABLE, null, values);
uriReturn = Uri.parse(URI_BOOK + "/" + newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert(SURFIX_CATEGORY_TABLE, null, values);
uriReturn = Uri.parse(URI_CATEGORY + "/" + newCategoryId);
break;
default:
break;
}
return uriReturn;
}
@Override
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext(), DB_NAME, 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 (sUriMatcher.match(uri)) {
case BOOK_DIR:
cursor = db.query(SURFIX_BOOK_TABLE, projection, selection,
selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query(SURFIX_BOOK_TABLE, projection, "id = ?",
new String[]{bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query(SURFIX_CATEGORY_TABLE, projection, selection,
selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query(SURFIX_CATEGORY_TABLE, 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 (sUriMatcher.match(uri)) {
case BOOK_DIR:
updateRows = db.update(SURFIX_BOOK_TABLE, values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updateRows = db.update(SURFIX_BOOK_TABLE, values,
"id = ?", new String[]{bookId});
break;
case CATEGORY_DIR:
updateRows = db.update(SURFIX_CATEGORY_TABLE, values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updateRows = db.update(SURFIX_CATEGORY_TABLE, values,
"id = ?", new String[]{categoryId});
break;
default:
break;
}
return updateRows;
}
}
activity_content_provider.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".chapter07.ContentProviderCustomActivity"
android:orientation="vertical">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_cpc_addbook"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加一本书" />
<Button
android:id="@+id/btn_cpc_querybook"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="查询书籍" />
<Button
android:id="@+id/btn_cpc_updatebook"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="修改一本书" />
<Button
android:id="@+id/btn_cpc_deletebook"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="删除一本书" />
<TextView
android:id="@+id/tv_cpc_info"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</ScrollView>
</LinearLayout>
ContentProviderActivity.java
public class ContentProviderActivity extends AppCompatActivity {
public String newId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_provider);
final TextView tv_cpc_info = findViewById(R.id.tv_cpc_info);
// 添加书籍
Button btn_cpc_addbook = findViewById(R.id.btn_cpc_addbook);
btn_cpc_addbook.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ContentValues values = new ContentValues();
values.put("name", "Book");
values.put("author", "BL");
values.put("pages", 308);
values.put("price", 38);
Uri uri = Uri.parse(MyContentProvider.URI_BOOK);
Uri newUri = getContentResolver().insert(uri, values);
newId = newUri.getPathSegments().get(1);
tv_cpc_info.setText(queryBooks());
}
});
// 查询书籍
Button btn_cpc_querybook = findViewById(R.id.btn_cpc_querybook);
btn_cpc_querybook.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tv_cpc_info.setText(queryBooks());
}
});
// 修改书籍
Button btn_cpc_updatebook = findViewById(R.id.btn_cpc_updatebook);
btn_cpc_updatebook.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse(MyContentProvider.URI_BOOK + "/" + newId);
ContentValues values = new ContentValues();
values.put("name", "Good Book");
values.put("author", "Andy");
values.put("pages", 400);
values.put("price", 40);
getContentResolver().update(uri, values, null, null);
tv_cpc_info.setText(queryBooks());
}
});
// 删除书籍
Button btn_cpc_deletebook = findViewById(R.id.btn_cpc_deletebook);
btn_cpc_deletebook.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse(MyContentProvider.URI_BOOK + "/" + newId);
getContentResolver().delete(uri, null, null);
tv_cpc_info.setText(queryBooks());
}
});
}
public String queryBooks() {
StringBuilder sb = new StringBuilder();
sb.append("书名 作者 页数 价格\n");
Uri uri = Uri.parse(MyContentProvider.URI_BOOK);
Cursor cursor = getContentResolver().query(uri,
null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String name = cursor.getString(
cursor.getColumnIndex("name"));
String author = cursor.getString(
cursor.getColumnIndex("author"));
int pages = cursor.getInt(
cursor.getColumnIndex("pages"));
double price = cursor.getDouble(
cursor.getColumnIndex("price"));
sb.append(name + " " + author + " " + pages + " " + price + "\n");
}
cursor.close();
}
return sb.toString();
}
}
网友评论