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)
';