android使用xml备份短信

来源:互联网 发布:对云计算的看法 编辑:程序博客网 时间:2024/06/13 10:04

很多手机助手都有备份短信的功能.原理是先读取用户手机中短信,然后将读取的数据放到服务其中,恢复短信的时候再从服务器中读取备份的短信,将其写入到手机中.安卓系统提供相关API 来读取短信.
安卓保护数据安全的原则之一是一个应用不能直接访问其他应用的私有数据.如果两个应用有相同的Linux user Id(android 中每个应用相当于Linux中的一个用户) ,则他们能够访问彼此的数据.默认情况下其他应用是不能直接获取短信应用中的短信,那么其它应用是如何获取到短信的?
安卓的四大组件中有一个是Content Provider,使用Content Provider ,既然分享应用的私有数据,又能保证应用数据的安权.安卓提供了android.provider.Telephony.Sms类,通过这个类,我们可以读取手机中的短信.

声明读取短信的权限

短信涉及到个人隐私,如果 应用需要读取短信,要声明读取短信的权限.在AndriodManifes中添加如下语句.

<uses-permission android:name="android.permission.READ_SMS"/>

获取手机中的短信

查看android.provider.Telephony.Sms的源码,其中有个字段是CONTENT_URI ,不过这个字段是在API19的才加进去的的.再具体看下CONTENT_URI,

public static final Uri CONTENT_URI = Uri.parse("content://sms");

使用CONTENT_URI,只是为了获取读取断线表的uri,我们可以自己钩子一个Uri,用来读取短信表,代码如下,我们可以通过READ_SMS_URI来读取手机中短信

public static final Uri READ_SMS_URI = Uri.parse("content://sms");

读取数据库中的信息,需要使用Curosr.下面代码读取短信表中的所有信息,并返回一个Cursor,这个Cursor包含所有的短信信息.

CursorLoader readSmsLoader = new CursorLoader(context,READ_SMS_URI,null,null,null,null);Cursor readSmsCursor = smsLoader.loadInBackground();

这里使用了CursorLoader,CursorLoader会另开一个线程进行查询操作,而不是在UI线程中查询,就算查询的数据很多,也不会阻塞UI线程.
CursorLoader有两个重载构造方法,

public CursorLoader(Context context)public CursorLoader(Context context, Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)

使用第二个构造方法,传入不同的参数,可以查询不同的数据,获取不同的结果.如果后四个参数全部为null,则返回短信的所有信息,每条短信有20个相关信息,备份短信时,并不需要获取短信的全部信息,只要获取其中部分信息即可.我们要获取的信息有address(号码),body(短信内容),date(接收短信的时间),type(短信的类型,是发出去的短信还是接收的短信).重写上面的代码,只返回我们想要的信息,我们不需要的信息则不返回

public List<Message> getSms() {        List<Message> messages = new ArrayList<>();        String[] projection = new String[]{Message.ADDRESS,Message.BODY,Message.DATE, Message.TYPE};        CursorLoader readSmsLoader = new CursorLoader(MainActivity.this,READ_SMS_URI,projection,null,null,null);        Cursor readSmsCursor = readSmsLoader.loadInBackground();        if(readSmsCursor.moveToFirst()){            do {                String address = readSmsCursor.getString(0);                String body = readSmsCursor.getString(1);                String date = readSmsCursor.getString(2);                String type = readSmsCursor.getString(3);                messages.add(new Message(address,body,date,type));            }while (readSmsCursor.moveToNext());        }        return messages;    }

我们已经知道返回的cursor每列的数据类型,所以在获取每条短信的内容时,直接使用了它们所在的列的索引.这里返回了一个List对象,将获取的所有短信存放在List中,如果短信不多的话,可以直接这样用.
Message类代码如下

package com.example.test.smsbackup;/** * 用来记录每条的短信的信息 */public class Message {    public static final String ADDRESS = "address";//号码    public static final String BODY = "body";//短信正文    public static final String DATE = "date";//接收短信时间    public static final String TYPE = "type";//短信类型,是发出去的短信还是收到的短信      public static final String MESSAGE = "message";//xml文件的 message节点     public static final String SMS = "sms";//xml文件的 sms节点    private String address;//收件人号码    private String body;//短信正文    private String date;//接受短信的时间    private String type;//短信类型    private String name;//名字,如果通讯录中有这个号码,则读取该号码的名字,没有则使用号码作为名字    public Message() {    }    public Message(String address, String body,                   String date, String type) {        this.address = address;        this.body = body;        this.date = date;        this.type = type;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    public String getBody() {        return body;    }    public void setBody(String body) {        this.body = body;    }    public String getDate() {        return date;    }    public void setDate(String date) {        this.date = date;    }    public String getType() {        return type;    }    public void setType(String type) {        this.type = type;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

将获取的短信保存到xml文件中

上面我们已经将读取到了用户手机中的短信,下面就要把读取到的短信保存到xml文件中.这里不使用自动备份,用户点击备份的时候才备份短信.每当有新短信时,系统会发送一个广播,如果要实现实时自动备份,则可一监听这个广播,一旦接收到新广播,就读取短信,备份短信,或者设定一个时间,隔一定时间备份一次.
先来看下要保存的xml文件的结构,
我们要保存的信息有address,date,body,type.xml文件结构如下

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><message>    <sms>        <address>        </address>        <type>        </type>        <date>        </date>        <body>        </body>    </sms></message>

生成xml文件,可以将要写入的内容拼接成一个字符串,不过这样做效率太低,还容易出错,这里使用XmlSerializerl来生成xml文件,生成xml文件,要生成各节点和节点的数据.一个xml文件的结构可分为文档头部(在XmlSerializerl中用startDocument()表示),开始标签在XmlSerializerl中用startTag()表示,结束标签在XmlSerializerl中用endTag()表示),文档结尾(在XmlSerializerl中用endDocument表示).开始标签和结束标签之间可以嵌套开始标签和结束标签,也可以在中间插入数据(在XmlSerializerl中用text()表示).下面代码读取的短信来生产xml文件.

public void backSms(List<Message> messages) {        XmlSerializer xmlSerializer = Xml.newSerializer();        try {            FileOutputStream fos = openFileOutput("back.xml",MODE_APPEND);            xmlSerializer.setOutput(fos, "utf-8");//"utf-8"设置文档的编码格式            xmlSerializer.startDocument("utf-8", true);//设置文档的头部,"utf-8"设置文档头部的编码格式            xmlSerializer.startTag(null, Message.MESSAGE);//开始标签,所有短信都在message下            for (Message message : messages){                xmlSerializer.startTag(null, Message.SMS);//开始标签,表示一条短信                xmlSerializer.startTag(null, Message.ADDRESS);//开始标签,表示号码                xmlSerializer.text(message.getAddress());//设置号码的内容                xmlSerializer.endTag(null, Message.ADDRESS);//结束标签,表示该字段结束                xmlSerializer.startTag(null, Message.DATE);                xmlSerializer.text(message.getDate());                xmlSerializer.endTag(null, Message.DATE);                xmlSerializer.startTag(null, Message.TYPE);                xmlSerializer.text(message.getType());                xmlSerializer.endTag(null, Message.TYPE);                xmlSerializer.startTag(null, Message.BODY);                xmlSerializer.text(message.getBody());                xmlSerializer.endTag(null,Message.BODY);                xmlSerializer.endTag(null, Message.SMS);//结束标签,表示一条短信结束            }            xmlSerializer.endTag(null,Message.MESSAGE);//结束标签            xmlSerializer.endDocument();//文档结束,xml文件中没有相应的标签            fos.close();        } catch (Exception e) {            e.printStackTrace();            Toast.makeText(this,"备份失败,请重试",Toast.LENGTH_SHORT).show();        }    }

在我用我自己的手机测试上面的代码的时候,如果读取全部的短信,会抛出java.lang.IllegalArgumentException: Illegal character (U+0).好像是某些短信中有奇怪的字符,导致异常,暂时还没有解决办法.能生成xml文件,通过adb命令查看的时候有乱码.

解析xml文件

将短信保存成xml文件后,还能从xml文件中读取短信数据,叫做解析xml文件,安卓提供了XmlPullParser用来解析xml文件.解析xml文件和生成xml文件相反,生成xml文件是生成文件节点和节点的内容,解析xml文件是读取每个节点,取出节点包裹的数据.
要解析xml文件,要遍历整个文件.获取读到的节点的名字,再根据读取到的节点执行相应的操作,下面代码解析前面生成的xml文件

public List<Message> pullXml(){        XmlPullParser xmlPullParser = Xml.newPullParser();//使用XmlPullParser来解析xml文件        List<Message> messages = null ;        try {            FileInputStream fin = openFileInput("back.xml");//读取文件            xmlPullParser.setInput(fin, String.valueOf(Xml.Encoding.UTF_8));            int type = xmlPullParser.getEventType();//获取xml文件的节点            Message message = new Message();            //遍历xml文件            while (type != XmlPullParser.END_DOCUMENT){                switch (type){                    case XmlPullParser.START_TAG:                        if (Message.MESSAGE.equals(xmlPullParser.getName())){                            messages = new ArrayList<>();                        }                        else if (Message.ADDRESS.equals(xmlPullParser.getName())){                            message.setAddress(xmlPullParser.nextText());                        }                        else if (Message.DATE.equals(xmlPullParser.getName())){                            message.setDate(xmlPullParser.nextText());                        }                        else if (Message.TYPE.equals(xmlPullParser.getName())){                            message.setType(xmlPullParser.nextText());                        }                        else if (Message.BODY.equals(xmlPullParser.getName())){                            message.setBody(xmlPullParser.nextText());                        }                        break;                    case XmlPullParser.END_TAG:                        if (Message.SMS.equals(xmlPullParser.getName())){                            messages.add(message);                        }                        break;                }                type = xmlPullParser.next();            }        } catch (Exception e) {            e.printStackTrace();        }        return messages;    }

这次主要学习了读取手机中的短信,生成xml文件和解析xml文件,解析xml文件还有其他的方法,这里使用了安卓提供的XmlPullParser,是一个比较简单的方法.

0 0
原创粉丝点击