1.数据储存
1.1 储存方式种类
Android的储存有以下5种数据储存方式:文件储存、SharedPreferences、SQLite数据库、ContentProvider及网络储存。
1.2 数据解析
对数据解析的方式有两种:XML解析,JSON解析。
2.文件储存
该储存方式是比较常用的方法,分为内部储存和外部储存。在Android中读取/写入文件的方法,与java中实现I/O程序是一样的,通过I/O流的形式把数据直接储存到文档中。Android提供了openFileInput()方法与openFileOutput()方法来读取设备上的文件。可以储存大数据,如文本、图片、音频等。、
2.1 内部储存(internal storage)
内部储存是指将应用程序中的数据以文件方式储存到设备的内部(该文件默认位于data/data/<packagename>/files/目录下,该路径挂载再手机自身储存目录),内部储存方式储存的文件被其所创建的应用程序所私有,如果其他应用程序要操作本应用程序中的文件,需要设置权限。当创建的应用程序被卸载时,其内部储存文件也随之删除。
内部储存路径调用的方法是:
context().getCacheDir().getAbsolutePath() //通过context调用 因此很多开发中获取储存路径的方法代码如下所示:
public static String getFilePath(Context context,String dir){ String directoryPath = ""; //判断SD卡是否可用 if(MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){ direcotryPath = context.getExternalFilesDir(dir).getAbsolutePath(); //direcotryPath = context.getExternalFilesDir().getAbsolutePath(); }else{ //没内存卡就存手机机身内存中 directoryPath = context.getFilesDir() + File.separator + dir; //directoryPath = context.getCacheDir() + File.separator + dir; } File file = new File(directory); if(!file.exits()){ //判断文件目录是否已经存在 file.mkdirs(); } return directoryPath(); }
注意:getExternalCacheDir()与getCacheDir()的区别,就像getExternalFilesDir()及getFilesDir()的区别相同。前者只是在路径下自动创建好了一个cache目录:/data/<package name>/files/cche/... 内部储存使用的是Context提供的openFileOutput()方法和openFileInput()方法,通过这两个方法可以分别获取FileOutputStream对象和FileInputStream对象,然后进行读写操作。实例代码如下:
FileOutputStream fos = openFileOutput(String name, int mode); FileInputStream fis = openFileInput(String name);
name:表示文件名
mode:表示文件的操作方式,如下:
》MODE_PRIVATE:该文件只能被当前程序读写
》MODE_APPEND:该文件的内容可以追加
》MODE_WORLD_READABLE:该文件的内容可以被其他程序读
》MODE_WORLD_WEITEABLE:该文件的内容可以被其他程序写
【注意】:默认应用程序创建的文件为私有,要想其他程序可以访问,要在文件创建时指定操作模式。
实例代码1如下:使用FileOutputStream对象将数据储存到文件中
String fileName = "data.txt"; String content = "hello world!"; FileOutputStream fos; try{ fos = openFileOutput(fileName,MODE_PRIVATE); fos.write(content.getBytes()); fos.close(); }catch(Exception e){ e.printStackTrace(); }
实例代码2如下:使用FileInputStream对象读取数据
String content = ""; FileInputStream fis; try{ fis = openFileInput("data.txt"); //创建缓冲区,并获取文件的长度 byte[] buffer = new byte[fis.available()]; //将文件内容读取到buffer缓冲区 fis.read(buffer); //转换成字符串 content = new String(buffer); fis.close(); }catch(Exception e){ e.printStackTrace(): }
2.2 外部储存(external storage)
2.2.1 外部储存概念
外部储存是指将文件储存到一些外部设备上,例如SD卡或者设备内嵌的储存卡,属于永久性的储存方式(该文件通常位于mnt/scard目录下)。Android的API6.0之后,根目录文件储存是需要用户授权的,即使再AndroidManifest.xml中配合了储存权限,也是需要用户动态授权的,如果用户不授权也无法使用。
外部储存的文件可以被其他应用程序所共享,当外部储存设备连接到计算机时,这些文件可以被浏览、修改和删除,因此这种方式是不安全的。
2.2.2 方法
由于外部储存设备可能被移除、丢失或者处于其他状态,因此在使用外部设备之前必须使用如下方法:
Environment.getExternalStorageState() 用于确认外部设备是否可用 Environment.getExternalStorageDirectory() 用于获取SD卡目录 当外部设备可用并且具有读写权限时,那么就可用通过FileInputStream、FileOutputStream对象来读写外部设备中的文件。
2.2.3 储存路径
Google提供了最佳的外部储存方案,也就是统一路径为:
/android/data/<package name>/files/......(该路径通常挂载再/mnt/sdcard目录下) 外部储存路径调用的两种方法如下:
context.getExternalFilesDir(String type).getAbsolutePath() 通过context调用,传入的参数是Environment类中的Environment.XXX静态变量,比如Environment.DIRECOTRY_MOVIES等
context.getExternalFilesDir().getAbsolutePath() 这个方法获取的文件储存路径适合6.0以上的系统,只要AndroidManifest.xml配置读写权限了,就不需要用户再授权了。 2.2.4 实例代码
向外部设备(SD卡)储存数据,实例代码如下:
//获取外部设备状态 String state = Environment.getExternalStorageState(); //判断外部设备是否可用 if(state.equals(Environment.MEDIA_MOUNTED)){ //获取SD卡目录 File SDPath = Environment.getExternalStorageDirectory(); File file = new File(SDPath,"data.txt"); String data = "Hello,World!"; FileOutputStream fos; try{ fos = new FileOutputStream(file); fos.write(data.getBytes()); fos.close(); }catch(Exception e){ e.printStackTrace(); } }
向外部设备(SD卡)读取数据,实例代码如下:
//获取外部设备状态 String state = Environment.getExternalStorageState(); //判断外部设备是否可用 if(state.equals(Environment.MEDIA_MOUNTED)){ //获取SD卡目录 File SDPath = Environment.getExternalStorageDirectory(); File file = new File(SDPath,"data.txt"); FileInputStream fis; try{ fis = new FileIntputStream(file); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); String data = br.readLine(); fis.close(); }catch(Exception e){ e.printStackTrace(); } }
2.3 外部储存和内部储存区别表
区别 方法 路径与备注 外部储存 Environment.getExternalStorageDirectory() SD卡根目录:/mnt/sdcard/(6.0后写入需要用户授权) context.getExternalFilesDir(dir)
context.getExternalCacheDir()
/mnt/sdcard/Android/data/<package name>/files/...
/mnt/sdcard/Android/data/<package name>/cache/...
内部储存 context.getFilesDir()
/data/data/<package name>files.,,,
context.getCacheDir() /data/data/<package name>/cache/...
3.XML解析
XML有以下解析方式:
3.1 DOM解析
DOM解析会遍历XML文件中所有内容以DOM树(文档树)的形式存放在内存中,然后通过DOM的API进行遍历、检索所需要的数据。根据树结构以节点形式对文件进行操作,支持删除、修改等功能。
需要注意的是,由于DOM需要先将整个XML文件存放在内存中,消耗内存比较大,一次较大的文件不建议采用这种方式。
3.2 SAX解析
SAX解析会逐行扫描XML文件,当遇到标签时,触发解析处理器,采用事件处理的方式解析XML文件。它在读取文件的同时即可进行解析处理,不必等到文件加载结束,相对简捷,可解析超大的XML文件。
缺点是SAX解析只能读取XML中的数据,无法进行增、删、改等功能。
3.3 PULL解析
PULL解析是一个开源的java项目,既可以用于Android项目,也可以用于JavaEE程序。Android已经集成了PULL解析器,因此在Android中最常用的解析方式就是PULL解析。
使用PULL解析XML文档,首先要创建XmlPullParser解析器,该解析器提供了很多属性,通过这些属性可以解析出XML文件中各个节点内容。XmlPullParser解析器的常用属性如下:
1)XmlPullParser.START_DOCUMENT:XML文档的开始,如<?xml version="1.0" encoding="utf-8"?>。
2)XmlPullParser.END_DOCUMENT:XML文档的结束。
3)XmlPullParser.START_TAG:开始节点,在XML文件中,除了文本之外,带有尖括号<>的都是开始节点,如<weather>。
4)XmlPullParser.END_TAG:结束节点,带有</ >的都是结束节点,如</weather>。
4.JSON解析
JSON与XML类似,都是用于储存数据,但是JSON解析速度更快,占用空间更小。
4.1 JSON数据
JSON即JavaScript Object Notation(对象表示法),是一种轻量级的数据交换格式。它是基于JavaScript的一个子集,JSON有如下两种数据结构:
1)对象结构
以“ { ”开始和“ } ”结束,中间部分由“ ,”分割的key:value对构成,关键字必须为String类型,实例如下:
{ key1:value1, key2:value2, ...... } //如 { "city":"BeiJing", "street":"001" }
2)数组结构
以“ [ ”开始和“ ] ”结束,由“ ,”分割的值的列表组成。其数值类型可为String,Number,Boolean,null。实例如下:
[ value1, value2, ...... ] //如 [ "abc", "false", null ]
上述两种数据结构可以合在一起组成复杂的数据结构,如下:
{ "name":"zhangsan", "address":{ "city":"beijing", "hobby":["篮球","羽毛球","游泳"] } }
4.2 JSON解析
Android平台有两种解析方式。一种是通过Android内置的org.json包,另一种是通过google开源的Gson库。分别使用这两种方式解析如下数据:
{ "name":"zhangsan", "age":"20" "married":"true" } 和 [16,2]
1)使用org.json解析JSON数据
Android SDK中提供了org.json包,用于解析JSON对象和数组两种数据结构。分别为JSONObject何JSONArray。
使用JSONObject解析JSON对象:
JSONObject jsonObj = new JSONObject(json1); String name = jsonObj.optString("name"); int age = jsonObj.optInt("age"); boolean married = jsonBoolean("married");
使用JSONArray解析JSON数组:
JSONArray jsonArray = new JSONArray(json2); for(int i = 0; i<jsonArray.length(); i++){ int age = jsonArray.optInt(i); }
2)使用Gson解析JSON数据
如要使用Gson库,首先需要将gson.jar添加到项目中。在【File】中选择【Project Structure】,在APP条目中选择【Depedencies】,点击【+】,选择【Library dependency】,然后搜索com.google.code.gson:gson:
在使用Gson库之前,需要创建JSON数据对应的实体类Person,实体类的成员名称要与JSON数据的key值一样。
使用Gson解析JSON对象:
Gson gson = new Gson(); Person person = gson.fromJson(json1,Person.class);
使用Gson解析JSON数组:
Gson gson = new Gson(); Type listType = new TypeToken<List<Integer>>(){}.getType(); List<Integer> ages = gson.fromJson(json2,listType);
6.SQLite数据库
适合储存大量数据,并对数据进行管理和维护。
1.简介
SQLite是一个轻量级的数据库。遵守ACID的关系型数据库管理系统。ACID指数据库事务正确执行的4个基本要素,即原子性(Atomicity),一致性(Consistency),隔离性(Isolation),持久性(Durability)。
SQLite没有服务器进程,它通过文件保存数据,该文件是跨平台的,可以放在其他平台中使用。并且在保存数据时,支持null(零)、integer(整数)、real(浮点数字)、text(字符串文本)和blob(二进制对象)5种数据类型。实际上,SQLite也接收varchar(n),char(n),decimal(p,s)等数据类型,只是在运算或保存时会转换成对应的5种数据类型。
2.数据库的创建
Android系统推荐使用SQLiteOpenHelper的子类创建数据库,因此需要创建一个类继承自SQLiteOpenHelper,并重写该类中的onCreate()方法和onUpgrade()方法即可。示例代码如下:
public class MyHelper extends SQLiteOpenHelper { public MyHelper(Context context){ super(context,"itcast.db",null,2); } //数据库第一次被创建时调用该方法 public void onCreate(SQLiteDatabase db){ //初始化数据库的表结构,执行一条建表的语句 db.execSQL("CREATE TABLE information(_id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20),price INTEGER)"); } //当数据库的版本号增加时调用 public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){ } }
execSQL()方法内的四个参数分别为:上下文对象,数据库名称,游标工厂(通常是null),数据包版本。
3.SQLite的基本操作
1)增加一条数据,示例代码如下:
public void insert (String name,String price){ SQLiteDataBase db = helper.getWritableDatabase(); //获取可读写SQLiteDatabase对象 ContentValues values = new ContentValues(); values.put("name",name); //将数据添加到ContentValues对象中 values.put("price",price); long id = db.insert("information",null,values); //插入一条数据到information表中 db.close(); }
insert()方法的三个参数分别为:表名,要插入的行(如果为空行,则将这个列名的值设为null),ContentValues对象(类似于Map类,通过键值对形式存入数据)。
2)修改一条数据,示例代码如下:
public int update (String name,String price){ SQLiteDataBase db = helper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("price",price); int number = db.update("information",values,"name=?",new String[]{name}); db.close(); return number; }
update()方法的四个参数分别为:表名,ContentValues对象,可选的where语句,表达式的站位参数列表(会替换掉where条件中的?)。
3)删除一条数据,实例代码如下:
public int delete (long id){ SQLiteDataBase db = helper.getWritableDatabase(); int number = db.delete("information","name=?",new String[]{id+""}); db.close(); return number; }
delete()方法的四个参数分别为:表名,可选的where语句,表达式的站位参数列表(会替换掉where条件中的?)。
4)查询一条语句,示例代码如下:
public boolean find (long id){ SQLiteDataBase db = helper.getWritableDatabase(); Cursor cursor = db.query("information",null,"_id=?",new String[]{id=""},null,null,null); boolean result = cursor.moveToNext(); cursor.close(); db.close(); return result; }
query()方法的四个参数分别为:表名,查询的列名,可选的where语句,表达式的占位参数列表(会替换掉where条件中的?),接收查询子句对应的条件值,分组方式,接收having条件(即定义组的过滤器),排序方式。
该方法返回的是一个行数集合Cursor。Cursor是一个游标接口,提供了遍历查询结果的方法,如移动指针方法move(),获取列值方法getString()等,通过这些方法可以获取集合中的属性值以及序号等。使用完Cursor一定要关闭,否则造成内存泄露。
5)使用SQL语句,即execSQL()方法,进行数据库操作,示例代码如下:
//插入一条数据 db.execSQL("insert into information (name,price) values (?,?)",new Object[]{name,price}); //修改一条数据 db.execSQL("update information set name=? where price=?",new Object[]{name,price}); //删除一条数据 db.execSQL("delete from informarion where _id = 1"); //查询一条数据 Cursor cursor = db.rawQuery("select * from person where name=?",new String[]{name});
4.SQLite中的事务
事务是一个对数据库执行工作的单元,是针对数据库的一组操作,可以由一条或者多条SQL语句组成。同一个事务具备同步的特点,如果其中有一条语句无法执行,那么所有的语句都不会执行。如下模拟银行转账功能:
PersonSQLiteOpenHelper helper = new PersonSQLiteOpenHelper(getContext()); //获取一个可读写的SQLiteDatabase对象 SQLiteDatabase db = helper.getWritableDatabase(); //开始数据库的事务 db.beginTransaction(); try{ //执行转出操作 db.execSQL("update person set account = account - 1000 where name = ?",new Object[]{"zhangsan"}); //执行转入操作 db.execSQL("update person set account = account +1000 where name = ?",mew Object[]{"zhangsan"}); //标记数据库事务执行成功 db.setTransactionSuccessful(); }catch(Exception e){ Log.i("事务处理失败",e.toString()); }finally{ db.endTransaction(); //关闭事务 db.close(); //关闭数据库 }
当执行到endTransaction()方法时,首先会检查是否有事务执行成功的标记,有则提交数据,无则回滚数据。如果不关闭事务,事务只有到超时才会自动结束。