Android开发-数据存储

 

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()方法时,首先会检查是否有事务执行成功的标记,有则提交数据,无则回滚数据。如果不关闭事务,事务只有到超时才会自动结束。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luckyliuqs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值