为什么需要内容提供者:
第六点就是为什么需要内容提供者
SQLiteDatabase.openDatabase(path, factory, flags)
可以通过这个方法打开一个数据库的文件。但是这个数据库文件必须是其他用户可以使用的权限 -rwx(当前用户权限) rwx(当前用户组权限) rwx其他用户权限)
我们可以使用cursor.getColumnIndex("列名")来返回你指定列名的 索引.
这样的好处就是当我们 不知道索引的情况下 也可以查找出内容。
我们为了实现访问另外一个应用程序中的数据库的内容。可以使用上面的方式。
但是这种方式不是 Android 推荐的方式。
所以我们要使用内容提供者。
————————————————————————————————
内容提供者原理:
————————————————————————————————
实现内容提供者的步骤:
<span style="font-size:18px;">public class AccountProvider extends ContentProvider {
//[1]定一个一个uri路径匹配器
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int QUERYSUCESS = 0; //ctrl+shift+X 变大写 小写加y
private static final int INSERTSUCESS = 1;
private static final int UPDATESUCESS = 2;
private static final int DELETESUCESS = 3;
private MyOpenHelper myOpenHelper;
//[2]创建一个静态代码块 在这个里面添加 uri
static{
/**
* http://www.baidu.com
* authority 注意: 和清单文件里面定义的一样 com.itheima.provider/query
*
*/
sURIMatcher.addURI("com.itheima.provider", "query", QUERYSUCESS);
sURIMatcher.addURI("com.itheima.provider", "insert", INSERTSUCESS);
sURIMatcher.addURI("com.itheima.provider", "update", UPDATESUCESS);
sURIMatcher.addURI("com.itheima.provider", "delete", DELETESUCESS);
}
//当内容提供者初始化 会执行此方法
@Override
public boolean onCreate() {
//[3]初始化 myopenHelpler 对象 就可以获取到sqlitedatabases对象 我们就可以操作数据库
myOpenHelper = new MyOpenHelper(getContext());
return false;
}
//这个方法对外暴露的
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
int code = sURIMatcher.match(uri);
if (code ==QUERYSUCESS ) {
//说明路径匹配成功
SQLiteDatabase db = myOpenHelper.getReadableDatabase();
//调用query方法
Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder);
//发送一条消息 说明说明数据库被操作了
getContext().getContentResolver().notifyChange(uri, null);
// db.close();
//小细节 ☆ 这个cursor不能关
return cursor;
}else{
//说明路径不匹配
// return null;
throw new IllegalArgumentException("哥们 :uri路径不匹配 请检测路径");
}
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
int code = sURIMatcher.match(uri);
if (code == INSERTSUCESS) {
//说明路径匹配成功
SQLiteDatabase db = myOpenHelper.getReadableDatabase();
long insert = db.insert("info", null, values);
Uri uri2 = Uri.parse("com.hahaheheheihei/"+insert);
if (insert>0) {
//发送一条消息 说明说明数据库被操作了
getContext().getContentResolver().notifyChange(uri, null);
}
db.close();//关闭数据库
return uri2;
}else {
throw new IllegalArgumentException("姐们 :uri路径不匹配 请检测路径");
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int code = sURIMatcher.match(uri);
if (code == DELETESUCESS) {
//匹配成功
SQLiteDatabase db = myOpenHelper.getReadableDatabase();
//代表影响的行数
int delete = db.delete("info", selection, selectionArgs);
if (delete>0) {
//发送一条消息 说明说明数据库被操作了
getContext().getContentResolver().notifyChange(uri, null);
}
return delete;
}
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int code = sURIMatcher.match(uri);
if (code == UPDATESUCESS) {
//路径匹配成功
SQLiteDatabase db = myOpenHelper.getWritableDatabase();
//代表影响的行数
int update = db.update("info", values, selection, selectionArgs);
if(update>0){
//发送一条消息 说明说明数据库被操作了
getContext().getContentResolver().notifyChange(uri, null);
}
return update;
}else{
throw new IllegalArgumentException("大爷:uri路径不匹配 请检测路径");
}
}
}
</span>
<span style="font-size:18px;">public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//点击按钮 往数据库里面插入一条数据
public void click1(View v){
//因为第一个应用里面的私有的数据库 已经通过内容提供者暴露出来了 所以通过内容解析者去获取数据
Uri uri = Uri.parse("content://com.itheima.provider/insert");
ContentValues values = new ContentValues(); //实际是map
//key: 代表列名 value 对应的值
values.put("name", "zhaoliu");
values.put("money", 1000);
//插入一条数据
Uri uri2 = getContentResolver().insert(uri, values);
System.out.println("uri2:"+uri2);
}
//点击按钮删除 赵六删掉
public void click2(View v){
//[1]获取内容的解析者
Uri uri = Uri.parse("content://com.itheima.provider/delete");
//[2]代表影响的函数
int delete = getContentResolver().delete(uri, "name=?", new String[]{"zhaoliu"});
Toast.makeText(getApplicationContext(), "删除了"+delete+"行", 1).show();
}
//给赵六多点钱 1000元
public void click3(View v){
//[1] 创建uri
Uri uri = Uri.parse("content://com.itheima.provider/update");
//[2]获取内容的解析者
ContentValues values = new ContentValues();
values.put("money", "10000000");
int update = getContentResolver().update(uri, values, "name=?",new String[]{"zhaoliu"});
Toast.makeText(getApplicationContext(), "更新了"+update+"行", 1).show();
}
//点击按钮 查询第一个应用里面数据库的信息
public void click4(View v){
// 第二种 查询方式 因为第一个应用里面的私有的数据库 已经通过内容提供者暴露出来了 所以通过内容解析者去获取数据
Uri uri = Uri.parse("content://com.itheima.provider/query");
//通过内容解析者获取数据
Cursor cursor = getContentResolver().query(uri, new String[]{"name","money"}, null, null, null);
if (cursor!=null) {
while(cursor.moveToNext()){
String name = cursor.getString(0);
String money = cursor.getString(1);
System.out.println("第二个应用:"+name+"---"+money);
}
}
}
}</span>
————————————————————————————————
备份短信案例:
当我们看见包名有providers 时候,我们就应该知道 这些内容已经通过内容提供者暴露出来了。我们只要使用内容解析者接收 就可以了。
//点击按钮查询短信内容 然后把短信内容进行备份
public void click(View v) {
try {
//[1]获取XmlSerializer的实例
XmlSerializer serializer = Xml.newSerializer();
//[2]设置序列化器参数
File file = new File(Environment.getExternalStorageDirectory().getPath(),"smsbackup.xml");
FileOutputStream fos = new FileOutputStream(file);
serializer.setOutput(fos, "utf-8");
//[3]写xml文档开头
serializer.startDocument("utf-8", true);
//[4]写xml的根节点
serializer.startTag(null, "smss");
//[5]构造uri
Uri uri = Uri.parse("content://sms/");
//[6]由于短信的数据库已经通过内容提供者暴露出来 所以我们直接通过内容解析者查询
Cursor cursor = getContentResolver().query(uri, new String[]{"address","date","body"}, null, null, null);
while(cursor.moveToNext()){
String address = cursor.getString(0);
String date = cursor.getString(1);
String body = cursor.getString(2);
//[7]写sms节点
serializer.startTag(null, "sms");
//[8]写address节点
serializer.startTag(null, "address");
serializer.text(address);
serializer.endTag(null, "address");
//[9]写date节点
serializer.startTag(null, "date");
serializer.text(date);
serializer.endTag(null, "date");
//[10]写body节点
serializer.startTag(null, "body");
serializer.text(body);
serializer.endTag(null, "body");
serializer.endTag(null, "sms");
}
serializer.endTag(null, "smss");
serializer.endDocument();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
这里的Uri.parse("content://")前面是默认的写法。后面就是在 内容提供者里面定义的。
记住要加权限:android.permission.READ_SMS
android.permission.WRITE_SMS
————————————————————————————————
利用内容提供者插入短信:
————————————————————————————————
读取联系人案例:
记住要加权限:android.permission.READ_CONTACTS
android.permission.WRITE_CONTACTS
注意一点,谷歌工程师设计的联系人删除的时候,只是把raw_contact表 中的raw_contact_id 设置为空。但是 实际的数据并没有删除。所以我们在查询contact_id的时候要判断是否为空.
//查询联系人的工具类
public class QueryContactsUtils {
public static List<Contact> queryContacts(Context context){
//[0]创建一个集合
List<Contact> contactLists = new ArrayList<Contact>();
//[1]先查询row_contacts表 的contact_id列 我们就知道一共有几条联系人
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri dataUri = Uri.parse("content://com.android.contacts/data");
Cursor cursor = context.getContentResolver().query(uri,new String[]{"contact_id"} , null, null, null);
while(cursor.moveToNext()){
String contact_id = cursor.getString(0);
if (contact_id!=null) {
//创建javabean对象
Contact contact = new Contact();
contact.setId(contact_id);
System.out.println("contact_id:"+contact_id);
//[2]根据contact_id去查询data表 查询data1列和mimetype_id
//☆ ☆ ☆ ☆ 当我们在查询data表的时候 其实查询的是view_data的视图
Cursor dataCursor = context.getContentResolver().query(dataUri, new String[]{"data1","mimetype"}, "raw_contact_id=?", new String[]{contact_id}, null);
while(dataCursor.moveToNext()){
String data1 = dataCursor.getString(0);
String mimetype = dataCursor.getString(1);
//[3]根据mimetype 区分data1列的数据类型
if ("vnd.android.cursor.item/name".equals(mimetype)) {
contact.setName(data1);
}else if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
contact.setPhone(data1);
}else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
contact.setEmail(data1);
}
}
//把javabean对象加入到集合中
contactLists.add(contact);
}
}
return contactLists;
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<Contact> queryContacts = QueryContactsUtils.queryContacts(getApplicationContext());
for (Contact contact : queryContacts) {
System.out.println("contat:"+contact);
}
}
}
public void click(View v) {
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri dataUri = Uri.parse("content://com.android.contacts/data");
//[2]获取name phone email Textutils
String name = et_name.getText().toString().trim();
String phone = et_phone.getText().toString().trim();
String email = et_email.getText().toString().trim();
//[2.1]在插入联系人id的时候 先查询一下 row_contact 一共有几条数据 加+1就是联系人的id
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
int count = cursor.getCount();
int contact_id = count +1;
//[3] 先往row_contact表 插入联系人的id (contact_id)
ContentValues values = new ContentValues();
values.put("contact_id", contact_id);
getContentResolver().insert(uri,values);
//[4]在把name phone email 插入到data表
ContentValues nameValues = new ContentValues();
nameValues.put("data1", name);
//☆ ☆ ☆ ☆ ☆ 插入的数据要告诉数据库 属于第几条联系人 和 数据类型
nameValues.put("raw_contact_id", contact_id);
nameValues.put("mimetype", "vnd.android.cursor.item/name");
getContentResolver().insert(dataUri, nameValues);
//[5]把phone号码 插入到data表
ContentValues phoneValues = new ContentValues();
phoneValues.put("data1", phone);
phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2");
phoneValues.put("raw_contact_id", contact_id);
getContentResolver().insert(dataUri, phoneValues);
//[5]把phone号码 插入到data表
ContentValues emailValues = new ContentValues();
emailValues.put("data1", email);
emailValues.put("mimetype", "vnd.android.cursor.item/email_v2");
emailValues.put("raw_contact_id", contact_id);
getContentResolver().insert(dataUri, emailValues);
}
注意一点:
我们查询的data表,其实是查询view_data表,所以当你去查询mimetype_id的时候
你会发现报错,因为view-data表没有这个数据。 所以我们应该查询mimetype的数据。
还有data表中的raw-contacts_id 就是就是raw-contacts表中的 contacts-id的数据.
————————————————————————————————
内容观察者原理:
注意:内容观察者不是四大组件之一,不需要再清单文件中配置。
————————————————————————————————
内容观察者引用场景:
短信监听