Chapter 04. Android 数据存储方式
4.1 Android 数据存储方式
image-
文件存储:以IO流的方式把数据存入手机内存或者SD卡,可以存储大数据,如音乐、图片或者视频等
-
SharedPreferences:本质是哪个是一个xml文件,以Map<Object,Object>形式存入手机内存中常用于存储比较简单的参数配置,如QQ登录账户密码的存储、窗口功能转态的存储等,使用简单方便
-
SQLite数据库:SQLite是一个轻量级、跨平台的数据库。数据库中所有信息都存储在单一文件内,占用内存小,并且支持基本SQL语法,通常用于存储用户信息
-
ContentProvider:内容提供者,是Android四大组件之一,以数据库形式存入手机内存,可以共享自己的数据给其他应用使用。ContentProvider统一了数据访问方式,使用起来更加规范
-
网络存储:把数据存储到服务器,不存储在本地,使用的时候直接从网络获取,避免了手机端信息丢失以及其他安全隐患
-
Android中应用程序存储的数据都属于应用私有,如果要将程序中的私有数据分享给其他应用程序,可以使用文件存储,SharedPreferences以及ContentProvider,推荐使用ContentProvider共享数据。
4.2文件存储
与Java中的文件存储类似,都是通过Io流的形式把数据原封不动地保存到文档中。Android中分为内部存储和外部存储。
4.2.1 内部存储
内部存储是指将应用程序中的数据已文件方式存储到设备的内存中(该文件位于data/data/<packagename>/files/目录下),内部存储方式存储的文件被其所创建的应用程序私有,如果其它应用程序需要操作本应用程序中的文件,需要设置权限。当创建的应用程序被卸载时,其内部存储文件也随之被删除。
内部存储使用的是Context提供的openFileOutput()方法和openFileInput()方法,通过这两个方法可以分别获取FileOutputStream对象和FileInputStream对象,具体如下:
FileOutputStream openFileOutput(String name,int mode);
FileInputStream onpenFileInput(String name);
openFileOutput()用于打开应用程序中对应的输出流,将数据存储到指定的文件中;onpenFileInput()用于打开应用程序对应的输入流,用于从文件中读取数据。其中name表示文件名,mode表示文件的操作模式,也就是读写文件的方式,它的取值有4种,具体如下:
-
MODE_PRIVATE:该文件只能被当前应用程序读写,默认的操作方式
-
MODE_APPEND:该文件的内容可以追加,常用的一种模式
-
MODE_WORLD_READABLE:该文件的内容可以被其他文件读取,安全性低,通常不使用
-
MODE_WORLD_WRITEABLE:该文件的内容可以被其他应用程序写入,安全性低,通常不使用
4.2.2 FileOutputStream对象将数据存储到文件中的实例代码如下
String fileName="data.txt";//文件名称
String content="hash";//要保存的数据
FileOutputStream fos;
try
{
fos=openFileOutput(fileName,MODE_PRIVATE);
fos.write(content.getBytes());
fos.close();
}
catch(Exception e)
{
e.printStackTrace();
}
4.2.3 使用FileInputStream对象读取数据的实例代码
String content ="";
FileInputStream fis;
try
{
fis=openFileInput("data.txt");
byte[]buffer=new byte[fis.available()];
fis.read(buffer);
content=new String(buffer);
}
catch(Exception e)
{
e.printStackTrace();
}
4.2.4 外部存储
外部存储是将文件存储到一些外围设备上(该文件通常位于mmt/sdcard目录下,不同厂商生产的手机这个路径肯会不同),例如SD卡或者设备内嵌的存储卡,属于永久性的存储方式。外部存储的文件可以被其他应用程序所共享,当外围存储设备连接到计算机时,这些文件可以被浏览,修改或者删除。
由于外围存储设备肯被移除,丢失或者处于其他状态,因此使用外围设备之前必须使用Environment.getExternalStorageState()方法来确认外围设备是否可用,当外围设备可用并且具有读写权限时,就可以通过FileInputStream、FileOutputStream或者FileReader、FileWriter对象来读写外围设备中的文件。
应用运行用到的数据文件(如图片)可以保存到sd卡中
-
文件类型: 任意
-
数据保存的路径:
-
路径1: /storage/sdcard/Android/data/packageName/files/ –
-
路径2: /storage/sdcard/xxx/
-
路径1 :其它应用可以访问,应用卸载时删除
-
路径2 : 其它应用可以访问, 应用卸载时不会删除
-
必须保证sd卡挂载在手机上才能读写, 否则不能操作
向外围设备(SD卡)中存储数据的示例代码
if(Environment.getExternalStorageState().equals(Environment.MODE_MOUNTED))
{
File SDPath=Environment.getExternalStorageDirectory();
File file=new File(SDPath,"data.txt");
String data="hash";
FileOutputStream fos;
try
{
fos=new FileOutputStream(file);
fos.write(data.getBytes());
fos.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
Environment.getExternalStorageDirectory()用于获取SD卡根目录的路径,用这种方法可以避免吧路径写死而找不到SD卡
从外围设备(SD卡)中读取数据的示例代码
if(Environment.getExternalStorageState().equals(Environment.MODE_MOUNTED))
{
File SDPath=Environment.getExternalStorageDirectory();
File file=new File(SDPath,"data.txt");
FileInputStream fis;
try
{
fis=new FileInputStream(file);
BufferedReader br=new BufferedReader(new InputStreamReader(fis));
String data=br.readLine();
fis.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
这里操作SD卡中的数据就是系统中比较关键的信息,需要在清单文件的<manifest>节点中配置权限信息,具体代码示例:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
4.2.5相关API
操作asserts下的文件
• 得到AssetManager : context.getAssets();
• 读取文件: InputStream open(filename);
• 得到files文件夹对象:dada/data/包名/files/ • File filesDir = getFilesDir();
• 加载图片文件
• Bitmap BitmapFactory.decodeFile(String pathName) // .bmp/.png/.jpg
• Bitmap BitmapFactory.decodeStream(InputStream is)
• 读取文件
• FileInputStream fis = openFileInput("logo.png");
• 保存文件
FileOutputStream fos = openFileOutput("logo.png", MODE_PRIVATE)
Environment : 操作SD卡的工具类
Environment.getExternalStorageState()// 得到SD卡的状态:
Environment.getExternalStorageDirectory()// 得到SD卡的路径:
Environment.MEDIA_MOUNTED // SD卡可读写的挂载状态值:
context. getExternalFilesDir(String type)://得到/mnt/sdcard/Android/data/pageckage_name/files/xxx.txt
android.permission.WRITE_EXTERNAL_STORAGE//操作SD卡的权限
4.2.6 XML 序列化和解析
序列化是将对象状态转换为可保持或传输的过程。在序列化对象时,需要使用XmlSerialize序列化类,它可以将IO流中的对象变得像基本类型数据一样,实现传递的功能。
xml序列化的示例代码
XmlSerializer serializer=Xml.newSerializer();//创建XmlSerializer对象
serializer.setOutput(fileOutputStream,"utf-8");//设置文件编码方式
serializer.startDocument("utf-8",true);//写入XML文件标志
serializer.startTag(null,"persons");//开始结点
serializer.text("小晖");//写入的内容
serializer.endTag(null,"persons");//结束结点
序列化文件
// 将Person对象保存为xml格式
public void Serializer(View view) {
try {
XmlSerializer serializer = Xml.newSerializer();
File file = new File(Environment.getExternalStorageDirectory(),
"person.xml");
FileOutputStream os = new FileOutputStream(file);
serializer.setOutput(os, "UTF-8");
serializer.startDocument("UTF-8", true);
serializer.startTag(null, "persons");
int count = 0;
for (Person person : userData) {
serializer.startTag(null, "person");
serializer.attribute(null, "id", count + "");
//将Person对象的name属性写入XML文件
serializer.startTag(null, "name");
serializer.text(person.getName());
serializer.endTag(null, "name");
//将Person对象的age属性写入XML文件
serializer.startTag(null, "age");
serializer.text(String.valueOf(person.getAge()));
serializer.endTag(null, "age");
//将Person对象的score属性写入XML文件
serializer.startTag(null, "score");
serializer.text(String.valueOf(person.getScore()));
serializer.endTag(null, "score");
serializer.endTag(null, "person");
count++;
}
serializer.endTag(null, "persons");
serializer.endDocument();
serializer.flush();
os.close();
Toast.makeText(this, "操作成功", 0).show();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "操作失败", 0).show();
}
}
注意
使用XML序列化器来存储XML文件时,一定要严格按照XML的格式来写,每个节点都有开始节点和结束节点,都是相对应的,可以先把一个节点的开始标志和结束标志同时写出来,再在中间写入节点内容,这样不会出现遗漏。
4.2.7 XML解析
若要操作XML文档,首先要将xml文档解析出来。
DOM解析
Document Object Mode解析是一种基于对象的ApI,它将XML文件的所有内容以文档树方式放在内存中,然后允许使用DoMAPI遍历xml树,检索所需的数据,这样能根据树的结构以节点方式来对文件进行操作。较小的文件可以采用这种方式解析,大文件不建议采用
SAX解析
SAX解析会逐行扫描XML文档,当遇到标签时触发解析器,采用事件处理的方式解析XML。它在读取文档的时候同时即可对xml进行处理,不必等待文档加载完成,,不存在占用内存的问题,可以解析超大的xml。但是,SAX解析只能用来读取xml中的数据,无法进行增删改。
PULL解析
Pull解析器是一个开源的Java项目,既可以用于Android应用,也可以用于JavaEE程序。Android已经继承了pull解析器,因此在Android中常用的解析方式就是pull解析。
weather.xml
<?xml version="1.0" encoding="utf-8"?>
<infos>
<city id="1">
<temp>20℃/30℃</temp>
<weather>晴天多云</weather>
<name>上海</name>
<pm>80</pm>
<wind>1级</wind>
</city>
<city id="2">
<temp>26℃/32℃</temp>
<weather>晴天</weather>
<name>北京</name>
<pm>98</pm>
<wind>3级</wind>
</city>
<city id="3">
<temp>15℃/24℃</temp>
<weather>多云</weather>
<name>哈尔滨</name>
<pm>30</pm>
<wind>5级</wind>
</city>
</infos>
WeatherService解析xml
public class WeatherService {
//返回天气信息的集合
public static List<WeatherInfo> getWeatherInfos(InputStream is)
throws Exception {
//得到pull解析器
XmlPullParser parser = Xml.newPullParser();
// 初始化解析器,第一个参数代表包含xml的数据
parser.setInput(is, "utf-8");
List<WeatherInfo> weatherInfos = null;
WeatherInfo weatherInfo = null;
//得到当前事件的类型
int type = parser.getEventType();
// END_DOCUMENT文档结束标签
while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
//一个节点的开始标签
case XmlPullParser.START_TAG:
//解析到全局开始的标签 infos 根节点
if("infos".equals(parser.getName())){
weatherInfos = new ArrayList<WeatherInfo>();
}else if("city".equals(parser.getName())){
weatherInfo = new WeatherInfo();
String idStr = parser.getAttributeValue(0);
weatherInfo.setId(Integer.parseInt(idStr));
}else if("temp".equals(parser.getName())){
//parset.nextText()得到该tag节点中的内容
String temp = parser.nextText();
weatherInfo.setTemp(temp);
}else if("weather".equals(parser.getName())){
String weather = parser.nextText();
weatherInfo.setWeather(weather);
}else if("name".equals(parser.getName())){
String name = parser.nextText();
weatherInfo.setName(name);
}else if("pm".equals(parser.getName())){
String pm = parser.nextText();
weatherInfo.setPm(pm);
}else if("wind".equals(parser.getName())){
String wind = parser.nextText();
weatherInfo.setWind(wind);
}
break;
//一个节点结束的标签
case XmlPullParser.END_TAG:
//一个城市的信息处理完毕,city的结束标签
if("city".equals(parser.getName())){
//一个城市的信息 已经处理完毕了.
weatherInfos.add(weatherInfo);
weatherInfo = null;
}
break;
}
//只要不解析到文档末尾,就解析下一个条目。得到下一个节点的事件类型
//注意,这个一定不能忘,否则会成为死循环
type = parser.next();
}
return weatherInfos;
}
}
WeatherInfo bean类
public class WeatherInfo {
private int id;
private String name;
private String weather;
private String temp;
private String pm;
private String wind;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getWeather() {
return weather;
}
public void setWeather(String weather) {
this.weather = weather;
}
public String getTemp() {
return temp;
}
public void setTemp(String temp) {
this.temp = temp;
}
public String getPm() {
return pm;
}
public void setPm(String pm) {
this.pm = pm;
}
public String getWind() {
return wind;
}
public void setWind(String wind) {
this.wind = wind;
}
}
4.3 SharedPreferences存储
SP存储专门用来存储一些单一的小数据,例如:配置参数,用户名,密码,自定义参数的设置等
-
存储数据的类型: boolean, float, int, long, String、StringSet
-
数据保存的路径: /data/data/packageName/shared_prefs/yyy.xml
-
可以设置数据只能是当前应用读取, 而别的应用不可以
-
应用卸载时会删除此数据
相关API
使用SharedPreferences存储数据时,首先需要通过context.getSharedPreferences(String name,int mode)获取SharedPreferences的实例对象(在Activity中可以直接使用this代表上下文,如果不是在Activity中则需要传入一个Context对象获取上下文)
SharedPreferences: 对应sp文件的接口
context. getSharedPreferences (String name, int mode): 得到SP对象
- name: 文件名(不带.xml)
mode: 生成的文件模式(是否是私有的,即其它应用是否可以访问),该模式有多个值可以供选择
-
MODE_PRIVATE:指定该SharedPreferences中的数据只能被本应用程序读写。
-
MODE_APPEND:指定该文件的内容可以追加
-
MODE_WORLD_READABLE:指定该SharedPreferences中的数据可以被其他应用程序读。
-
MODE_WORLD_WRITEABLE:指定该SharedPreferences中的数据可以被其他应用程序读写。
4.3.1 SharedPreferences的相关方法
方法声明 | 功能描述 |
---|---|
Boolean contains(String key) | 判断SharedPreferences是否包含特定的key的数据 |
abstract Map<String.?>getAll() | 获取SharedPreferences中的全部ket/value键值对 |
Boolean getBoolean(String key,boolean defValue) | 获取SharedPreferences中key对应的boolean值 |
int getInt(String key,int defValue) | 获取SharedPreferences中key对应的int值 |
float getFloat(String key,float defValue) | 获取SharedPreferences中key对应的float值 |
long getLong(String key,long defValue) | 获取SharedPreferences中key对应的long值 |
String getString(String key,String defValue) | 获取SharedPreferences中key对应的String值 |
Set<String> getStringSet(String key,Set<String> defValue) | 获取SharedPreferences中key对应的Set值 |
SharedPreferences本身并不支持数据的存储和修改。数据的存储和修改需要通过SharedPreferences.Editor()对象实现,需要调用SharedPreferences.Editor edit()方法获取Editor实例对象。
4.3.2 SharedPreferences.Editor对象的相关方法
方法声明 | 功能描述 |
---|---|
SharedPreferences.Editor edit() | 创建一个Editor对象 |
SharedPreferences.Editor putString(String key,String value) | 向SharedPreferences中存入指定key对应的String值 |
SharedPreferences.Editor putInt(String key,int value) | 向SharedPreferences中存入指定key对应的int值 |
SharedPreferences.Editor putFloat(String key,float value) | 向SharedPreferences中存入指定key对应的float值 |
SharedPreferences.Editor putLong(String key,long value) | 向SharedPreferences中存入指定key对应的long值 |
SharedPreferences.Editor putBoolean(String key,boolean value) | 向SharedPreferences中存入指定key对应的boolean值 |
SharedPreferences.Editor putStringSet(String key,Set<String> value) | 向SharedPreferences中存入指定key对应的Set值 |
SharedPreferences.Editor remove(String key) | 删除SharedPreferences指定key对应的数据 |
SharedPreferences.Editor clear() | 清空SharedPreferences中的所有数据 |
boolean commit() | 编辑结束后,调用该方法提交 |
SharedPreferences存储数据
SharedPreferences存储数据时,需要选获取SharedPreferences对象,再通过该对象获取到Editor对象,让后通过Editor对象的相关方法存储数据。具体代码如下:
SharedPreferences sp=getSharedPreferences("data",MODE_PRIVATE);//data表示文件名
Editor editor=sp.edit();//获取编辑器
editor.putString("name","Hash");//存入String数据
editor.putInt("name","Hash");//存入int类型数据
editor.commit();//提交修改
SharedPreferences获取数据时,只需要创建SharedPreferences对象,让后使用该对象获取相应的key对应的value值。具体代码如下:
SharedPreferences sp=context.getSharedPreferences();
String data=sp.getString("name","");
SharedPreferences删除数据时与存储数据相似,同样需要先获取到Editor对象,然后通过该对象删除数据,最后提交,具体代码如下:
SharedPreferences sp=context.getSharedPreferences();
Editor editor=sp.edit();
editor.remove("name");//删除指定key对应的一条数据
editor.clear();//删除所有数据
editor.commit();//提交修改
注意
-
SharedPreferences存入和删除数据时,一定要在最后使用editor.commit()方法提交数据
-
获取数据的key值与存入数据的key值的数据类型要一致,否则查不到数据
-
获取SharedPreferences的key值时,可以使用静态变量保存,以免存储、删除时写错,如:private static final String key="name";