美文网首页
Chapter 04. Android 数据存储方式

Chapter 04. Android 数据存储方式

作者: GeekGray | 来源:发表于2018-10-12 20:53 被阅读47次

    阅读原文

    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

    • 可以设置数据只能是当前应用读取, 而别的应用不可以

    • 应用卸载时会删除此数据

    image

    相关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";

    相关文章

      网友评论

          本文标题:Chapter 04. Android 数据存储方式

          本文链接:https://www.haomeiwen.com/subject/qtfnaftx.html