1.文件存储
保存文件到手机内存
Context接口:字面意思上下文,提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它允许获取以应用为特征的资源和类型。同时启动应用级的操作,如启动Activity,broadcasting和接收intents。
context.getFileDir():帮助我们返回一个目录(data/data/包名/files/)
context.getCacheDir():帮助我们返回一个目录(data/data/包名/cache/)
Android文件操作模式:
Context.MODE_PRIVATE = 0:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND。
Context.MODE_APPEND = 32768:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE = 1和Context.MODE_WORLD_WRITEABLE = 2用来控制其他应用是否有权限读写该文件。
Context.MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;
Context.MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
//如果希望文件被其他应用读和写,可以传入:
openFileOutput("itcast.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
Android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需要userid匹配。默认情况下,任何应用创建的文件,sharedpreferences,数据库都应该是私有的(位于/data/data/<package name>/files),其他程序无法访问。除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。
保存文件:
public static boolean saveUserInfo(Context context, String username,String password){
try {
//得到一个目录 /data/data/当前应用程序包名/files/
//第一种方法:
//File file = new File(context.getFileDir(),"info.dat");
//FileOutputStream fos = new FileOutputStream(file);
//第二种方法:调用context下的operFileOutput(String 文件名,访问权限)
FileOutputStream fos = context.openFileOutput("info.dat", Context.MODE_PRIVATE);
fos.write((username+"##"+password).getBytes());
fos.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
读取文件:
public static Map<String,String> readUserInfo(Context context){
try {
File file = new File(context.getFileDir(),"info.dat");
FileInputStream fis = new FileInputStream(file);
//FileInputStream fis = context.openFileInput("info.dat");
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String line = br.readLine();
String username = line.split("##")[0];
String password = line.split("##")[1];
Map<String, String> map = new HashMap<String, String>();
map.put("username", username);
map.put("password", password);
return map;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
保存文件到SD卡
与保存到手机内存不一样的是,保存文件到SD卡需要相关权限:
写:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />读:<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> (Android4.0出现,4.0之前可以不用请求这个权限而对文件进行读取)
将相关权限代码添加至AndroidManifest.xml文件中
查看SD卡状态方法:Environment.getExternalStorageState(),返回的是描述SD卡状态的字符串
Environment.MEDIA_MOUNTED:可读可写
相关代码:public static boolean saveUserInfo(Context context,String username,String password){
try{
//判断SD卡状态
if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
//Environment.getExternalStorageDirectory()得到SD卡路径
File file = new File(Environment.getExternalStorageDirectory(),"/info.dat");
FileOutputStream fos = new FileOutputStream(file);
// zhangsan##123
fos.write((username+"##"+password).getBytes());
fos.close();
return true;
}else{
Toast.makeText(context,"sd卡不可用,请检查sd卡状态",0).show();
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
获取内存大小:
package com.itheima.calcsize;
import java.io.File;
import android.os.Bundle;
import android.os.Environment;
import android.os.StatFs;
import android.app.Activity;
import android.text.format.Formatter;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv_info = (TextView) findViewById(R.id.tv_info);
tv_info.setText("sd卡可用:"+getAvailSDSize()+"\n"+"内存可用:"+getAvailROMSize());
}
/**
* 获取sd卡的可用空间
* @return
*/
private String getAvailSDSize() {
//得到SD卡路径
File path = Environment.getExternalStorageDirectory();
//获取路径所指向的文件系统状态
StatFs stat = new StatFs(path.getPath());
//得到每一块的大小
long blockSize = stat.getBlockSize();
//得到可用有多少块
long availableBlocks = stat.getAvailableBlocks();
//得到总共多少块
//long totalBolcks = stat.getBlocksCount();
//计算出可用内存大小,单位byte
long availablesize = blockSize * availableBlocks; // byte
//将数据格式化并返回
return Formatter.formatFileSize(this, totalsize);
}
/**
* 获取手机内部存储rom的可用空间
* @return
*/
private String getAvailROMSize() {
//得到手机内存路径
File path = Environment.getDataDirectory();
//获取路径所指向的文件系统状态
StatFs stat = new StatFs(path.getPath());
//得到每一块的大小
long blockSize = stat.getBlockSize();
//得到可用有多少块
long availableBlocks = stat.getAvailableBlocks();
//得到总共多少块
//long totalBolcks = stat.getBlocksCount();
//计算出可用内存大小,单位byte
long totalsize = blockSize * availableBlocks; // byte
//将数据格式化并返回
return Formatter.formatFileSize(this, totalsize);
}
}
2.SharedPreferences
SharedPreferences是一种轻型的数据存储方式,它的本质是基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息。其存储位置在/data/data/<包名>/shared_prefs目录下。SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过Editor对象实现。实现SharedPreferences存储的步骤如下:1.根据Context获取SharedPreferences对象
2.利用edit()方法获取Editor对象。
3.通过Editor对象存储key-value键值对数据。
4.通过commit()方法提交数据。
保存文件:
public static void saveUserInfo(Context context,String username,String password){
SharedPreferences sp = context.getSharedPreferences("config",Context.MODE_PRIVATE);
//得到编辑器
Editor editor = sp.edit();
//添加信息
editor.putString("username",username);
editor.putString("password",password);
//提交保存数据
editor.commit();
}
调用以上方法后,会在/data/data/(包名)/shared_prefs目录下生成了一个config.xml文件,一个应用可以创建多个这样的xml文件,保存文件格式如下:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
- <map>
<string name="password">123</string>
<string name="username">wangwu</string>
</map>
读取文件:
SharedPreferences sp = context.getSharedPreferences("config",Context.MODE_PRIVATE);
//第二个参数为缺省值
String username = sp.getString("username","");
String password = sp.getString("password","");
SharedPreferences有其自身缺陷,比如其只能存储boolean,int,float,long和String五种简单的数据类型,并且不能像SQLite进行条件查询。
3.XML文件序列化
通过备份短信演示XML文件序列化的各种方法。将短信的各种信息备份至XML文件中,格式如下图:
手动将短信数据写入xml文件的方法
public void click(View view){
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
sb.append("<smss>");
for(SmsInfo info : smsInfos){
sb.append("<sms>");
sb.append("<body>");
sb.append(info.getBody());
sb.append("</body>");
sb.append("<address>");
sb.append(info.getAddress());
sb.append("</address>");
sb.append("<date>");
sb.append(info.getDate());
sb.append("</date>");
sb.append("</sms>");
}
sb.append("</smss>");
try {
File file = new File(Environment.getExternalStorageDirectory(),"backup.xml");
FileOutputStream fos = new FileOutputStream(file);
fos.write(sb.toString().getBytes());
fos.close();
Toast.makeText(this, "备份短信成功", 0).show();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "备份短信失败", 0).show();
}
}
以上方法是使用StringBuilder进行字符串添加,然后写入文件,这种方式的缺点就是在对xml进行属性设置的时候非常麻烦,下面使用xml文件的序列化器进行xml文件的创建。
public void click(View view){
try {
//1,初始化一个xml文件的序列化器
XmlSerializer serializer = Xml.newSerializer();
//2.初始化序列器参数,并且指定文件的编码方式
File file = new File(Environment.getExternalStorageDirectory(),"backup.xml");
FileOutputStream fos = new FileOutputStream(file);
serializer.setOutput(fos, "UTF-8");
//3.开始写xml文件.
serializer.startDocument("UTF-8", true);//文件头,true代表是否独立
//开始节点
serializer.startTag(null, "smss");
for(SmsInfo info : smsInfos){
serializer.startTag(null, "sms");
//添加属性
serializer.attribute(null,"id",info.getId()+"");
serializer.startTag(null, "body");
serializer.text(info.getBody());
serializer.endTag(null, "body");
serializer.startTag(null, "address");
serializer.text(info.getAddress());
serializer.endTag(null, "address");
serializer.startTag(null, "date");
serializer.text(info.getDate()+"");
serializer.endTag(null, "date");
serializer.endTag(null, "sms");
}
//结束节点
serializer.endTag(null, "smss");
//xml文件结尾
serializer.endDocument();
fos.close();
Toast.makeText(this, "备份成功", 0).show();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "备份失败", 0).show();
}
}
使用xml的序列化器XmlSerializer还有一个好处就是在序列化诸如“<”这样的字符的时候会自动替换成相应可以被xml解析的字符.
使用pull解析器解析xml文件
Pull解析器是一个开源的java项目,既可以用于android,也可以用于JavaEE。如果用在javaEE需要把其jar文件放入类路径中,因为Android已经集成进了Pull解析器,所以无需添加任何jar文件。android系统本身使用到的各种xml文件,其内部也是采用Pull解析器进行解析的。 Pull解析器的运行方式与 SAX 解析器相似。它提供了类似的事件,如:开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件。跟SAX不同的是, Pull解析器产生的事件是一个数字,而非方法,因此可以使用一个switch对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text类型节点的值。
解析短信Xml文件:
public static List<SmsInfo> getSmsInfos(InputStream is) throws Exception{
XmlPullParser parser = Xml.newPullParser();
//初始化解析器
parser = setInput(is,"utf-8");
List<SmsInfo> smsInfos = null;
SmsInfo smsInfo = null
//得到当前事件类型:0代表文档开头,1代表文档结尾
int type = parser.getEventType();
while(type!=XmlPullParser.END_DOCUMENT)
{
switch(type){
case XmlPullParser.START_TAG:
if("smss".equalse(parser.name())){
smsInfos = new ArrayList<SmsInfo>();
}else if("sms".equalse(parser.getName())){
smsInfo = new SmsInfo();
}else if("body".equalse(parser.getName())){
String body = parser.nextText();
smsInfo.setBody(body);
}else if("date".equalse(parser.getName())){
String date = parser.nextText();
smsInfo.setDate(date);
}else if("type".equalse(parser.getName())){
String type = parser.nextText();
smsInfo.setType(type);
}else if("address".equalse(parser.getName())){
String address = parser.nextText();
smsInfo.setAddress(address);
}
case XmlPullParser.END_TAG:
if("sms".equals(parser.name())){
smsInfos.add(smsInfo);
snsInfo = null;
}
}
type = parser.next();
}
return smsInfos;
}