Android开发之调用系统的ContentProvider——短信的备份和恢复
最后更新于:2022-04-01 09:46:45
转载请注明出处:[http://blog.csdn.net/dmk877/article/details/50518464](http://blog.csdn.net/dmk877/article/details/50518464)
相关文章:[Android开发之内容提供者——创建自己的ContentProvider(详解)](http://blog.csdn.net/dmk877/article/details/50387741)
忍耐和坚持虽是痛苦的事情,但却能渐渐地为你带来好处.——奥维德。
可能在坚持一件事情一段时间后,我们脑海中会有很多放弃的念头,可能在放弃之后的几年后,我们会想如果当时坚持下来会怎么怎么样。。。,但是可惜的是我们没有坚持。最近比较懒,也在这里提醒自己,不要迷失自己,坚持学习。
在上一篇我们讲到了如何创建自己的ContentProvider,如果你掌握了上一篇所讲的内容,那么相信今天这一篇,你会很轻松的掌握。这一篇的主要内容就是调用谷歌工程师给我们提供好的ContentProvider,也就是说谷歌定义好一个ContentProvider后会给我们一个Uri,我们拿着这个Uri就可以得到相应的数据。如果你没调用过系统的Uri,没有关系,今天我们会通过一个案例来详细讲解怎么调用。废话不多说进入正题,如有谬误欢迎批评指正,如有疑问欢迎留言。
通过本篇博客你将学到以下知识点
①如何调用系统的ContentProvider
②如何通过谷歌给我们的Uri获得短信的数据
③一个案例将手机中的短信进行备份和恢复
**1、如何调用系统的ContentProvider**
其实阅读了上一篇文章之后,这个问题会很好的理解,谷歌工程师在将ContentProvider写好之后,肯定会给我们一个Uri,只要知道这个Uri,我们就可以拿到我们需要的数据,比方说你想获得手机短信的信息,那么必定有和其对应的Uri,你想获得图库、联系人信息,也必定有相应的Uri与之对应。知道对应的Uri后,就可以过ContentResolver这个对象,根据Uri进行数据访问。更多的内容请参考:[Android开发之内容提供者——创建自己的ContentProvider(详解)](http://blog.csdn.net/dmk877/article/details/50387741),今天的主要任务就是完成一个案例获取系统的短信数据。
**2、案例(手机短信数据的获取,以及备份和恢复)**
接下来我们就来看一个案例,这个案例的主要功能就是根据Uri获取手机短信的信息,并将其备份和恢复,它的效果图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-01_56d500e41db07.jpg)
这个图片演示了这样一种功能,首先在DDMS中向模拟器中发几条短信,然后运行我们的程序,点击备份,提示备份成功后,将所有的短信删除,然后在我们的程序中点击恢复,打开短信界面发现刚才删除的短信已经恢复,这就是我们要实现的功能
首先来分析一下怎么实现上述效果,如果想备份短信,首先要做的就是获取短信的列表,这一步比较简单因为谷歌已经将其封装好,我们所要做的就是用Uri去查询短信库,就O了,然后拿到数据后需要将数据以XML的形式保存到SD卡里面,当然你用其它的方式也可以,只要能将其恢复就行。最后恢复的时候将指定路径的XML文件解析,根据Uri将解析的短信数据插入到系统的短信列表中。思路就是这样一个思路。没有看懂没关系,下面会有源码以及对它们的分析。
了解了大概思路后,另一个重要的任务就是看看短信的表结构在模拟器中它的路径是data->data->com.android.providers.telephony->databases下,如下图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-01_56d500e5c6252.jpg)
将其导出然后用Sqlite数据打开可以看到数据的结构如下,这里只关心threads表和sms表就够了
threads表的结构如下
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-01_56d500e5d6b82.jpg)
其中
_id:用于区分不同的电话号码,系统会为不同的电话号码分配不同的_id。
date:收到信息的时间(如果收到来自同一个phone number多条信息,并且有对于一条信息未读,那么date表示收到的最后一条信息时的时间)
message_count:收到的信息的数目
read: 0. 代表未读。 1.代表 已读
对于其它字段一般很少用到,这里就不多做介绍了,
sms表的结构如下
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-01_56d500e5e9241.jpg)
其中
_id:用于区分不同的短信
date: 该条短信接收的时间
read: 0表未读,1表已读
body: 表示具体的短信内容
到这里准备工作还差一步就可以进行代码的书写了,哪一步呢?就是访问这个数据库的Uri,对于访问手机短信的Uri主要有以下这么几个
content://sms/ 所有短信
content://sms/inbox 收件箱
content://sms/sent 已发送
content://sms/draft 草稿
content://sms/outbox 发件箱
content://sms/failed 发送失败
content://sms/queued 待发送列表
在这个案例中我们用content://sms/,因为是备份肯定是备份所有的短信,好了,下面一起来看看代码吧。首先要做的就是根据Uri获取短信的列表,这里新建一个SmsManage类,将备份和恢复的方法放到这个类中,获取短信列表的代码如下
~~~
/**
* 获取短信列表
* @return
*/
public List<SmsData> getSmsList() {
//获取所有短信的 Uri
Uri uri = Uri. parse( "content://sms/");
//获取ContentResolver对象
ContentResolver contentResolver = mContext.getContentResolver();
//根据Uri 查询短信数据
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if ( null != cursor) {
Log. i( TAG, "cursor.getCount():" + cursor.getCount());
//根据得到的Cursor一条一条的添加到smsList(短信列表)中
while (cursor.moveToNext()) {
int _id = cursor.getInt(cursor.getColumnIndex("_id" ));
int type = cursor.getInt(cursor.getColumnIndex("type" ));
String address = cursor.getString(cursor.getColumnIndex( "address"));
String body = cursor.getString(cursor.getColumnIndex("body" ));
String date = cursor.getString(cursor.getColumnIndex("date" ));
SmsData smsData = new SmsData(_id, type, address, body, date);
smsList.add(smsData);
}
cursor.close();
}
return smsList;
}
~~~
可以看到上述代码就是根据content://sms/这个Uri去查询手机中短信的数据库,得到一个Cursor这个Cursor就包含了一条一条的短信。然后去遍历这个Cursor将我们需要的数据添加到smsList中。这样短信数据就拿到了,因为我们做的功能是短信备份,所以接下来需要将smsList这个集合中的数据保存到本地,以方便短信恢复的时候去读取保存的这个文件,那么问题来了,怎样将smsList这个集合以文件的形式保存到本地呢?当然方法有很多,这里我们采用的是使用XmlSerializer将其序列化,待我们需要恢复的时候使用XmlPullParser 将其反序列化,就可以拿到备份的数据,听起来感觉挺高大上的,其实很简单就是对xml的操作。下面来看看序列化的代码即将上面得到的集合smsList中的数据生成一个xml文件,并保存到本地,代码如下
~~~
/**
* 将短信数据保存到 sd卡中
*/
public void saveSmsToSdCard(){
smsList=getSmsList();
//获得一个序列化对象
XmlSerializer xmlSerializer=Xml. newSerializer();
//将生成的 xml文件保存到sd 卡中名字为"sms.xml"
File file= new File(Environment.getExternalStorageDirectory(), "sms.xml");
FileOutputStream fos;
try {
fos = new FileOutputStream(file);
xmlSerializer.setOutput(fos, "utf-8");
xmlSerializer.startDocument( "utf-8", true);
xmlSerializer.startTag( null, "smss");
xmlSerializer.startTag( null, "count");
xmlSerializer.text( smsList.size()+ "");
xmlSerializer.endTag( null, "count");
for(SmsData smsData: smsList){
xmlSerializer.startTag( null, "sms");
xmlSerializer.startTag( null, "_id");
xmlSerializer.text(smsData.get_id()+ "");
System. out.println( "smsData.get_id()=" +smsData.get_id());
xmlSerializer.endTag( null, "_id");
xmlSerializer.startTag( null, "type");
xmlSerializer.text(smsData.getType()+ "");
System. out.println( "smsData.getType=" +smsData.getType());
xmlSerializer.endTag( null, "type");
xmlSerializer.startTag( null, "address");
xmlSerializer.text(smsData.getAddress()+ "");
System. out.println( "smsData.getAddress()=" +smsData.getAddress());
xmlSerializer.endTag( null, "address");
xmlSerializer.startTag( null, "body");
xmlSerializer.text(smsData.getBody()+ "");
System. out.println( "smsData.getBody()=" +smsData.getBody());
xmlSerializer.endTag( null, "body");
xmlSerializer.startTag( null, "date");
xmlSerializer.text(smsData.getDate()+ "");
System. out.println( "smsData.getDate()=" +smsData.getDate());
xmlSerializer.endTag( null, "date");
xmlSerializer.endTag( null, "sms");
}
xmlSerializer.endTag( null, "smss");
xmlSerializer.endDocument();
fos.flush();
fos.close();
Toast. makeText( mContext, "备份完成", Toast.LENGTH_SHORT ).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
~~~
通过调用以上方法就将短信以xml的形式保存到了本地。运行这个方法后可以再sd卡中看到sms.xml文件,将其导出来可以看到它的格式如下
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-01_56d500e608b1f.jpg)
注:实际上xml文件中它是一行这里为了让大家更清楚的看清结构,我手动将其改成上述格式了。
保存到本地后,工作就剩下最后一步了,那就是将这个xml文件反序列化,并将其中的数据一条一条插入到短信的数据库中,与其对应的代码如下
~~~
/**
* 将指定路径的 xml文件中的数据插入到短信数据库中
* @param path
*/
public void restoreSms(String path) {
File file = new File(path);
//得到一个解析 xml的对象
XmlPullParser parser = Xml. newPullParser();
try {
fis = new FileInputStream(file);
parser.setInput( fis, "utf-8");
ContentValues values = null;
int type = parser.getEventType();
while (type != XmlPullParser. END_DOCUMENT) {
switch (type) {
case XmlPullParser. START_TAG:
if ( "count".equals(parser.getName())) {
} else if ("sms" .equals(parser.getName())) {
values = new ContentValues();
} else if ("type" .equals(parser.getName())) {
values.put( "type", parser.nextText());
} else if ("address" .equals(parser.getName())) {
values.put( "address", parser.nextText());
} else if ("body" .equals(parser.getName())) {
values.put( "body", parser.nextText());
} else if ("date" .equals(parser.getName())) {
values.put( "date", parser.nextText());
}
break;
case XmlPullParser. END_TAG:
if ( "sms".equals(parser.getName())) {// 如果节点是 sms
Uri uri = Uri.parse( "content://sms/");
ContentResolver resolver = mContext.getContentResolver();
resolver.insert(uri, values);//向数据库中插入数据
System. out.println( "插入成功" );
values = null; //
}
break;
}
type=parser.next();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
~~~
可以看到在上述方法中判断xml的END_TAG是不是"sms"如果是的话说明一条短信的"type"、"address"、"body"、"date"这些信息已经拿到,然后就根据Uri将这条数据插入到数据库中,就完成一条短信的恢复,待读到 *END_DOCUMENT*说明xml文件已经读完,此时所备份的短信就全部恢复了。
好了今天这篇文章就到这里了,如果发现文章中的错误,欢迎指出,如果有疑问请留言,谢谢。如果你觉的本篇文章对你有帮助,就赞一下,顶一个呗,谢谢您的支持。
[源码下载点这里](http://download.csdn.net/detail/dmk877/9404958)
转载请注明出处:[http://blog.csdn.net/dmk877/article/details/50518464](http://blog.csdn.net/dmk877/article/details/50518464)