4.1 数据存储方式
- 文件存储是一种较常用的方法,与Java中的文件存储类似,都是通过I/O流的形式存储数据。
- SharedPreferences是Android提供的用来存储一些简单的配置信息的一种机制。
- SQLite数据库是Android自带的一个轻量级数据库,支持基本SQL语法。
- ContentProvider是Android四大组件之一,可以将自己的数据共享给其他应用程序。
- 网络存储是通过网络提供的存储空间来存储/获取数据信息。
4.2 文件存储
4.2.1 文件存储简介
-
文件存储是Android中最基本的一种数据存储方式,它与Java中的文件存储类似,都是通过I/O流的形式把数据存储到文档中。
-
Android中的文件存储分为内部存储和外部存储。
-
1.内部存储—写入
String fileName = "data.txt";
String content = "helloworld";
FileOutputStream fos;
try {
fos = openFileOutput(fileName, MODE_PRIVATE);
fos.write(content.getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
2.内部存储—读取
String content = "";
FileInputStream fis;
try {
fis = openFileInput("data.txt");
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
content = new String(buffer);
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
3.外部存储—存入
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
File SDPath = Environment.getExternalStorageDirectory();
File file = new File(SDPath, "data.txt");
String data = "HelloWorld";
FileOutputStream fos;
try {
fos = new FileOutputStream(file);
fos.write(data.getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
4.外部存储—读取
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_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();
} catch (Exception e) {
e.printStackTrace();
}
}
5.外部存储—声明权限
- Android系统为了保证应用程序的安全性做了相关规定,如果程序需要访问系统的一些关键信息,必须要在清单文凭中声明权限才可以,否则程序运行时会直接崩溃。
- 由于操作SD卡中的数据属于系统中比较关键的信息,因此需要在清单文件的节点中添加SD卡的读写权限。
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
4.2.2 实战演练——保存QQ密码
1.MainActivity
package cn.itcast.saveqq;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.util.Map;
public class MainActivity extends AppCompatActivity implements
View.OnClickListener {
private EditText etNumber;
private EditText etPassword;
private Button btnLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化界面
initView();
Map<String, String> userInfo = SPSaveQQ.getUserInfo(this);
if (userInfo != null) {
etNumber.setText(userInfo.get("number"));
etPassword.setText(userInfo.get("password"));
}
}
private void initView() {
etNumber = (EditText) findViewById(R.id.et_number);
etPassword = (EditText) findViewById(R.id.et_password);
btnLogin = (Button) findViewById(R.id.btn_login);
//设置按钮的点击事件
btnLogin.setOnClickListener(this);
}
@Override
public void onClick(View v) {
//当单击“登录”按钮时,获取QQ账号和密码
String number = etNumber.getText().toString().trim();
String password = etPassword.getText().toString();
//检验账号和密码是否正确
if (TextUtils.isEmpty(number)) {
Toast.makeText(this, "请输入QQ账号", Toast.LENGTH_SHORT).show();
return;
}
if (TextUtils.isEmpty(password)) {
Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show();
return;
}
//登录成功
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
//保存用户信息
boolean isSaveSuccess = SPSaveQQ.saveUserInfo(this, number, password);
if (isSaveSuccess) {
Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show();
}
}
}
2.FileSaveQQ
package cn.itcast.saveqq;
import android.content.Context;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;
public class FileSaveQQ {
//保存QQ账号和登录密码到data.txt文件中
public static boolean saveUserInfo(Context context, String number,
String password) {
try {
FileOutputStream fos = context.openFileOutput("data.txt",
Context.MODE_PRIVATE);
fos.write((number + ":" + password).getBytes());
fos.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
//从data.txt文件中获取存储的QQ账号和密码
public static Map<String, String> getUserInfo(Context context) {
String content = "";
try {
FileInputStream fis = context.openFileInput("data.txt");
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
content = new String(buffer);
Map<String, String> userMap = new HashMap<String, String>();
String[] infos = content.split(":");
userMap.put("number", infos[0]);
userMap.put("password", infos[1]);
fis.close();
return userMap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
3.SPSaveQQ
package cn.itcast.saveqq;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.HashMap;
import java.util.Map;
public class SPSaveQQ{
// 保存QQ账号和登录密码到data.xml文件中
public static boolean saveUserInfo(Context context, String number,
String password) {
SharedPreferences sp = context.getSharedPreferences("data",
Context.MODE_PRIVATE);
SharedPreferences.Editor edit = sp.edit();
edit.putString("userName", number);
edit.putString("pwd", password);
edit.commit();
return true;
}
//从data.xml文件中获取存储的QQ账号和密码
public static Map<String, String> getUserInfo(Context context) {
SharedPreferences sp = context.getSharedPreferences("data",
Context.MODE_PRIVATE);
String number = sp.getString("userName", null);
String password = sp.getString("pwd", null);
Map<String, String> userMap = new HashMap<String, String>();
userMap.put("number", number);
userMap.put("password", password);
return userMap;
}
}
4.3 XML解析
4.3.1 三种解析方式
1.DOM解析
将XML文件中所有内容以DOM树形式存放在内存中,支持删除、修改等功能。缺点是消耗内存较大。
2.SAX解析
逐行扫描XML文件,读取文件的同时即可进行解析处理,不必等到文件加载结束。缺点是无法进行增、删、改等操作。
3.PULL解析
一个开源的Java项目,既可以用于Android应用,也可以JavaEE程序。Android中已经集成了PULL解析器。
4.3.2 实战演练——天气预报
1.XML文件
<?xml version="1.0" encoding="utf-8"?>
<infos>
<city id="sh">
<temp>20℃/30℃</temp>
<weather>晴天多云</weather>
<name>上海</name>
<pm>80</pm>
<wind>1级</wind>
</city>
<city id="bj">
<temp>26℃/32℃</temp>
<weather>晴天</weather>
<name>北京</name>
<pm>98</pm>
<wind>3级</wind>
</city>
<city id="gz">
<temp>15℃/24℃</temp>
<weather>多云</weather>
<name>广州</name>
<pm>30</pm>
<wind>5级</wind>
</city>
</infos>
2.MainActivity
package cn.itcast.weather;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity implements
View.OnClickListener {
private TextView tvCity;
private TextView tvWeather;
private TextView tvTemp;
private TextView tvWind;
private TextView tvPm;
private ImageView ivIcon;
private Map<String, String> map;
private List<Map<String, String>> list;
private String temp, weather, name, pm, wind;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化文本控件
initView();
try {
//读取weather1.xml文件
InputStream is = this.getResources().openRawResource(R.raw.weather1);
//把每个城市的天气信息集合存到weatherInfos中
List<WeatherInfo> weatherInfos = WeatherService.getInfosFromXML(is);
//循环读取weatherInfos中的每一条数据
list = new ArrayList<Map<String, String>>();
for (WeatherInfo info : weatherInfos) {
map = new HashMap<String, String>();
map.put("temp", info.getTemp());
map.put("weather", info.getWeather());
map.put("name", info.getName());
map.put("pm", info.getPm());
map.put("wind", info.getWind());
list.add(map);
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "解析信息失败", Toast.LENGTH_SHORT).show();
}
//自定义getMap()方法,显示天气信息到文本控件中,默认显示北京的天气
getMap(1, R.drawable.sun);
}
private void initView() {
tvCity = (TextView) findViewById(R.id.tv_city);
tvWeather = (TextView) findViewById(R.id.tv_weather);
tvTemp = (TextView) findViewById(R.id.tv_temp);
tvWind = (TextView) findViewById(R.id.tv_wind);
tvPm = (TextView) findViewById(R.id.tv_pm);
ivIcon = (ImageView) findViewById(R.id.iv_icon);
findViewById(R.id.btn_sh).setOnClickListener(this);
findViewById(R.id.btn_bj).setOnClickListener(this);
findViewById(R.id.btn_gz).setOnClickListener(this);
}
@Override
public void onClick(View v) { //按钮的点击事件
switch (v.getId()) {
case R.id.btn_sh:
getMap(0, R.drawable.cloud_sun);
break;
case R.id.btn_bj:
getMap(1, R.drawable.sun);
break;
case R.id.btn_gz:
getMap(2, R.drawable.clouds);
break;
}
}
//将城市天气信息分条展示到界面上
private void getMap(int number, int iconNumber) {
Map<String, String> cityMap = list.get(number);
temp = cityMap.get("temp");
weather = cityMap.get("weather");
name = cityMap.get("name");
pm = cityMap.get("pm");
wind = cityMap.get("wind");
tvCity.setText(name);
tvWeather.setText(weather);
tvTemp.setText("" + temp);
tvWind.setText("风力 : " + wind);
tvPm.setText("pm: " + pm);
ivIcon.setImageResource(iconNumber);
}
}
3.WeatherServer
package cn.itcast.weather;
import android.util.Xml;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.xmlpull.v1.XmlPullParser;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class WeatherService {
//解析xml文件返回天气信息的集合
public static List<WeatherInfo> getInfosFromXML (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(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;
}
//解析json文件返回天气信息的集合(下一个例子的)
public static List<WeatherInfo> getInfosFromJson(InputStream is)
throws IOException {
byte[] buffer = new byte[is.available()];
is.read(buffer);
String json = new String(buffer, "utf-8");
//使用gson库解析JSON数据
Gson gson = new Gson();
Type listType = new TypeToken<List<WeatherInfo>>() { }.getType();
List<WeatherInfo> weatherInfos = gson.fromJson(json, listType);
return weatherInfos;
}
}
4.WeatherInFo
package cn.itcast.weather;
public class WeatherInfo {
private String id;
private String temp;
private String weather;
private String name;
private String pm;
private String wind;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTemp() {
return temp;
}
public void setTemp(String temp) {
this.temp = temp;
}
public String getWeather() {
return weather;
}
public void setWeather(String weather) {
this.weather = weather;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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.4 JSON解析
4.4.1 JSON数据
1.JSON数据特点
- JSON即JavaScript Object Notation(对象表示法),是一种轻量级的数据交换格式。
- JSON是基于纯文本的数据格式,它可以传输String、Number、Boolean类型的数据,也可以传输数组,或者Object对象。
- JSON文件的扩展名为.json。
- JSON分为JSON对象和JSON数组两种数据结构。
2.对象结构
以“{”开始,以“}”结束。中间部分由0个或多个以“,”分隔的key:value对构成,注意关键字和值之间以“:”分隔。
3.数组结构
以“[”开始,以“]”结束。中间部分由0个或多个以“,”分隔的值的列表组成。
4.4.2 JSON解析
1.两种解析方式
2.解析JSON对象
3.解析JSON数组
4.解析JSON
4.4.3 实战演练——天气预报
代码见上一节
4.5 SharedPreferences
4.5.1 SharedPreferences的使用
1.SharedPreferences的特点
- SharedPreferences是Android平台上一个轻量级的存储类。
- 用于存储应用程序的配置参数,如用户名、密码等。
- 通过key/value(键值对)的形式将数据保存在XML文件中。
- value值只能是float、int、long、boolean、String、StringSet类型数据。
2.存储数据
3.获取数据
获取数据可以直接通过SharedPreferences对象的getXXX()方法 来实现。
4.5.2 实战演练——保存QQ密码
4.6 本章小结
本章主要讲解了Android中的数据存储,首先介绍了Android中常见的数据存储方式,然后讲解了文件存储以及XML和JSON数据的解析,最后讲解了SharedPreferences。数据存储是Android中非常重要的内容,每个应用程序基本上都会涉及到数据存储,因此要求初学必须熟练掌握本章知识。
✎ 本章作业 Android系统中的五种数据存储方式各自的特点。 SharedPreferences如何存储数据。
✎ 预习作业 SQLite数据库的使用 ListView控件的作用
【学习笔记】
【学习资料】
- 教材:《Android移动开发案例教程》
- 学习资料:Android移动开发配套资料
-1.源代码:每个章节配套案例代码
-2.补充案例:项目、课后练习代码
-3.课程资料:课后习题及答案、ppt - github:Android
包含自己学习过程的全部案例、实验、项目代码 - 推荐学习视频:Android开发从入门到精通(项目案例版)