一 四大组件
1.1 Activity组件,它一个单独的窗口,程序流程都必须在Activity中运行。
1.2 service组件,用于在后台完成用户指定的操作。
1.3 content provider组件,会为所有的应用准备一个内容窗口,并且保留数据库、文件。
1.4 broadcast receiver组件,是程序之间传递信息时的一种机制,作用就是接收或者发送通知。
二 content provider介绍
2.1 ContentProvider 主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另外一个程序的数据,同时还能保证被访问数据的安全性
2.2 Content Provider 不同于 文件存储 和 ShardPreferences存储 全局可读写操作模式,它可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。
2.3 Content Provider 的用法一般有两种:
- 使用现有的内容提供器来读取和操作相应程序中的数据,比如系统的通讯录,系统短信信息;
- 创建自己的内容提供器给我们程序的数据提供外部访问接口,即自定义内容提供者
2.4 如果一个应用程序通过内容提供器对其数据提供了外部访问接口,那么任何其他的应用程序就都可以对这部分数据进行访问。 例如:系统中自带的电话簿、短信、媒体库等。
2.5 Content Provider 的使用和 SQLite 使用非常相似,提供增删改查数据,两者的区别:
- SQLite可以为应用程序创建完全封装的关系数据库。使用这些数据库可以存储和管理复杂的、结构化的应用程序数据。Android数据库存储在设备上的/data/data/
/databases文件夹中。所有的数据库都是私有的,只能被创建它们的应用程序访问。 - Content Provider提供了一种基于使用content:://模式的简单URI寻址模型来发布和使用数据的接口。它们允许将应用层从底层数据层中分离出来,通过抽象底层数据源使应用程序不必依赖于某个数据源。
三 content provider 使用
3.1 常用方法:
- onCreate():当提供者被启动时调用。
- query():该方法从客户端接受请求,结果是返回指针(Cursor)对象。
- insert():该方法向内容提供者插入新的记录。
- delete():该方法从内容提供者中删除已存在的记录。
- update():该方法更新内容提供者中已存在的记录。
- getType():该方法为给定的URI返回元数据类型。
3.2 查询构造函数,返回Cursor方便下一步遍历数据
Cursor cursor = getContentResolver().query(
Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder
);
- uri:查询路径,如查询某个应用下的一张表,结构一般是content://包名.provider/表名。比如 Uri uri = Uri.parse("content://com.example.app.provider/table1")。对应sqlite中的 “ from tableName ”
- projection:查询指定列名,对应sqlite中的 “ select column ”
- selection:约束条件,对应sqlite的中 “ where column = value ”
- selectionArgs:为where的占位符附上具体的值,对应sqlite的中 execute("select * from table where id= ? ",(100) )
- sortOrder:查询结果的排序方式,对应sqlite中的 “ order by column ”
3.3 插入数据,返回uri路径
Uri uri = getContentResolver().insert(
Uri uri,
ContentValues values);
- uri:查询路径,表名
- ContentValues:是一个类似map的键值对,使用put可以添加数据,如values.put("列名","值");
3.4 更新数据,返回更新条数
int count= getContentResolver().update(
Uri uri,
ContentValues values,
String where,
String[] selectionArgs);
- uri:查询路径,表名
- ContentValues:是一个类似map的键值对,使用put可以添加数据,如values.put("列名","值");
- selection:约束条件,对应sqlite的中 “ where column = value ”
- selectionArgs:为where的占位符附上具体的值,对应sqlite的中 execute("select * from table where id= ? ",(100) )
3.5 删除数据,返回改变条数
int count = getContentResolver().delete(
Uri url,
String where,
String[] selectionArgs);
- uri:查询路径,表名
- 约束条件,对应sqlite的中 “ where column = value ”
- selectionArgs:为where的占位符附上具体的值,对应sqlite的中 execute("select * from table where id= ? ",(100) )
四 content provider调用系统数据
4.1 获取通讯录数据:
清单文件需要注册通讯录查询修改权限
然后动态请求权限,查询通讯录数据
public class MainActivity extends AppCompatActivity {
ArrayAdapter adapter;
List contactsList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView contactsView = findViewById(R.id.contacts_view);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, contactsList);
contactsView.setAdapter(adapter);
// 判断当前是否有权限
if (ContextCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.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);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
@Override
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, "you denied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}
4.2 插入通讯录数据
private void insertData(){
String name = "张三";
String phone = "123456789101";
ContentValues values = new ContentValues();
Uri uri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContentID = ContentUris.parseId(uri);
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContentID);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name);
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
}
4.3 根据用户名更新手机号信息
private void updateData(){
String name_update = "张三";
String phone_update = "123456789101";
Long rawContactId = 0L;
ContentValues valuesUpdate= new ContentValues();
valuesUpdate.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone_update);
Cursor cursorUpdate = getContentName(name_update);
if (cursorUpdate.moveToFirst()) {
rawContactId = cursorUpdate.getLong(0);
}
getContentResolver().update(ContactsContract.Data.CONTENT_URI, valuesUpdate, "raw_contact_id=?", new String[]{rawContactId + ""});
cursorUpdate.close();
}
//根据名字查询
private Cursor getContentName(String name_search) {
String[] query_all = new String[]{
ContactsContract.CommonDataKinds.Identity.RAW_CONTACT_ID, //用户id
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, //联系人姓名
ContactsContract.CommonDataKinds.Phone.NUMBER //联系人电话
};
String selections = ContactsContract.Contacts.DISPLAY_NAME + "=?";
String[] selection_args = new String[]{name_search};
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, query_all, selections, selection_args, null);
return cursor;
}
4.4 删除通讯录信息
private void deleteData() {
String name1 = "张三";
int count = getContentResolver().delete(ContactsContract.RawContacts.CONTENT_URI, ContactsContract.Contacts.DISPLAY_NAME + "=?", new String[]{name1});
if (count > 0) {
Toast.makeText(this, "删除成功!", Toast.LENGTH_SHORT).show();
}
}
五 自定义content provider
5.1 系统为我们提供了固定的表名,列名,不能在修改表结构。如果我们需要自己写一个内容提供者给外部程序使用,还是要借助SQLite数据库进行自定义数据的增删改查。
5.2 如下示例,自定义contentprovider
public class PersonProvider extends ContentProvider {
private DBHelper mHelper;
private static final int PERSON_DIR = 0;
private static final int PERSON_ITEM = 1;
private static final String tag = "PersonProvider";
private static UriMatcher mMatcher;
static{
// 添加匹配规则
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI("com.example.contentproviderdemo.PersonProvider.provider", "person", PERSON_DIR);
mMatcher.addURI("com.example.contentproviderdemo.PersonProvider.provider", "person/#", PERSON_ITEM);
}
@Override
public boolean onCreate() {
mHelper = new DBHelper(getContext(), "test.db");
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
SQLiteDatabase db = mHelper.getReadableDatabase();
switch (mMatcher.match(uri)) {
case PERSON_DIR:
Log.d(tag, "PERSON_DIR");
cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
break;
case PERSON_ITEM:
Log.d(tag, "PERSON_ITEM");
// 获取id.内部是把uri是"/"进行分割了,所以获取get(1)就是id值。
String personID = uri.getPathSegments().get(1);
cursor = db.query("person", projection, "_id = ?", new String[]{personID}, null, null, sortOrder);
break;
default:
Log.d(tag, "Invalid uri");
break;
}
return cursor;
}
@Override
public String getType(Uri uri) {
String result = null;
switch (mMatcher.match(uri)) {
case PERSON_DIR:
result = "vnd.android.cursor.dir/com.example.contentproviderdemo.PersonProvider.provider.person";
break;
case PERSON_ITEM:
result = "vnd.android.cursor.item/com.example.contentproviderdemo.PersonProvider.provider.person";
break;
default:
break;
}
return result;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Uri result = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
switch (mMatcher.match(uri)) {
case PERSON_DIR:
long id = db.insert("person", null, values);
String uriString = "content://com.example.contentproviderdemo.PersonProvider.provider/person/" + id;
result = Uri.parse(uriString);
break;
default:
break;
}
return result;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mHelper.getWritableDatabase();
int result = 0;
switch (mMatcher.match(uri)) {
case PERSON_DIR:
result = db.delete("person", selection, selectionArgs);
break;
case PERSON_ITEM:
String personID = uri.getPathSegments().get(1);
result = db.delete("person", "_id = ?", new String[]{personID});
break;
default:
break;
}
return result;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = mHelper.getWritableDatabase();
int result = 0;
switch (mMatcher.match(uri)) {
case PERSON_DIR:
result = db.update("person", values, selection, selectionArgs);
break;
case PERSON_ITEM:
String personID = uri.getPathSegments().get(1);
result = db.update("person", values, "_id = ?", new String[]{personID});
break;
default:
break;
}
return result;
}
}
5.3 如果其它程序能够访问,还需要在清单文件注册自定义的内容提供者
六 总结
内容提供者大部分场景还是调用系统的资源,比如通讯录,短信,通话记录等,还有Android7.0版本以后访问文件也需要添加内容提供者,自定义数据的访问还是主要通过数据库方案。