Android学习笔记14 数据存储全攻略

这是Android学习笔记的第14篇,之前关于界面方面的学习总结暂时告一段落。这篇主要详细介绍Android开发过程中数据存储的常见几种方式。

一、概述
二、Shared Preferences 简单键值对
三、Internal Storage 内部存储
四、External Storage 外部存储
五、SQLite数据库
六、Network Connection 网络存储
七、总结

一、概述

在平常的应用开发中,数据存储应该是我们经常要接触的内容,同时也是相当重要的部分。例如,要开发一个课程表的App,如果用户将自己的课程数据输入,但是下次登录时却看不到之前的数据,那这样的App还有价值吗,我们需要采取某种方式把数据存储下来。Android为我们的数据存储提供了很多选择,根据不同的存储需要,可以采取不同的方式。

总的来说,数据存储有两种类别,一种是存储在本地,另一种是通过网络把数据存储在服务器端。本文主要介绍数据在本地存储的4种基本方式,包括Shared Preferences(简单键值对存储),Internal Storage (内部存储),External Storage (外部存储),SQLite数据库。

二、Shared Preferences 简单键值对

1、概述

Shared Preferences,直译就是共用的配置信息,它的使用很简单,主要是采用键值对的形式来保存应用的一些常用配置等少量的数据,且这些数据的格式非常简单。例如,用户登录时提供一个选择按钮,询问是否记住密码,这时就可以使用这种方式。

2、例子

Shared Preferences平时在使用过程中比较简单,经常用到的有两个类接口,SharedPreferences和SharedPreferences.Editor。其中,后面的SharedPreferences.Editor主要是用于修改数据。下面以一个例子来讲下它的用法。

1、下图是个用户登录界面,输入用户名和密码后登录前可以选择是否记住密码,如果记住了,那么下次登录的时候EditText里就直接显示上次的用户名和密码。

登录界面

2、布局文件layout_preferences.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="8dp">

    <EditText
        android:id="@+id/et_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="用户名">

        <requestFocus />
    </EditText>

    <EditText
        android:id="@+id/et_userpasswd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="密码" />

    <CheckBox
        android:id="@+id/cb_keep"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="记住密码" />

    <Button
        android:id="@+id/btn_login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="登录" />
</LinearLayout>

3、Activity里的代码SharedPreActivity 。

public class SharedPreActivity extends Activity {

    SharedPreferences sp;
    SharedPreferences.Editor spEditor;

    private static final String FILE_USER = "FILE_USER_LO";
    private static final String KEY_KEEP = "keeppswd";
    private static final String KEY_USERNAME = "username";
    private static final String KEY_USERPASSWD = "userpswd";

    private EditText etUserName, etUserPasswd;
    private Button btnLogin;
    private CheckBox cbKeep;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_preferences);

        initView();
        initData();
    }

    private void initView() {
        etUserName = (EditText) findViewById(R.id.et_username);
        etUserPasswd = (EditText) findViewById(R.id.et_userpasswd);
        cbKeep = (CheckBox) findViewById(R.id.cb_keep);
        btnLogin = (Button) findViewById(R.id.btn_login);
    }

    //初始化数据
    private void initData() {

        sp = getSharedPreferences(FILE_USER, Context.MODE_PRIVATE);

        //获取之前是否点击了保存密码
        cbKeep.setChecked(sp.getBoolean(KEY_KEEP, false));

        //如果点击了就获取密码
        if (cbKeep.isChecked()) {
            etUserName.setText(sp.getString(KEY_USERNAME, ""));
            etUserPasswd.setText(sp.getString(KEY_USERPASSWD, ""));
        }

        btnLogin.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {

                String userName = etUserName.getText().toString().trim();
                String userPasswd = etUserPasswd.getText().toString().trim();

                if (cbKeep.isChecked()) {
                    funDataSave(userName, userPasswd, 1);
                } else {
                    funDataSave(userName, userPasswd, 0);
                }
            }
        });
    }

    private void funDataSave(String username, String userpassword, int type) {

        spEditor = sp.edit();

        if (type == 0) {

            spEditor.putBoolean(KEY_KEEP, false);
            spEditor.putString(KEY_USERNAME, "");
            spEditor.putString(KEY_USERPASSWD, "");
        } else {
            spEditor.putBoolean(KEY_KEEP, true);
            spEditor.putString(KEY_USERNAME, username);
            spEditor.putString(KEY_USERPASSWD, userpassword);
        }
        spEditor.apply();
    }
}

3、用法解析

  1. Shared Preferences使用过程中,接口SharedPreferences通过getSharedPreferences(String name, int mode)方法来实例化,其中name是配置文件名,mode是配置文件操作模式。我们这里传入Context.MODE_PRIVATE,表示该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容。操作模式还有MODE_WORLD_READABLE,表示当前文件可以被其他应用读取。
    MODE_WORLD_WRITEABLE,表示当前文件可以被其他应用写入
  2. Shared Preferences允许我们存储的数据主要有Boolean、String、Int、Float、Long等几种,如果要对数据进行操作,通过SharedPreferences.edit()实例化接口SharedPreferences.Editor,之后通过putFloat(String key, float value)等方法可以将指定键名的数据存储到配置文件里,通过getFloat(String key, float defValue)等可以获取到指定键名的数据。
  3. 对数据进行操作,必须要提交才能生效,可以选择commit()或者apply(),官方推荐采用apply(),因为这种方法是异步进行的。

三、Internal Storage 内部存储

1、概述

Internal Storage,内部存储,主要是将数据文件保存到设备的内部存储中。

2、用法与解析

  1. 将数据写入到指定文件
    /**
     * 写入数据到内部存储
     *
     * @param wData    数据
     * @param filename 写入到的文件
     */
    private void funInternalWrite(String wData, String filename) {

        FileOutputStream fos;

        try {

            fos = openFileOutput(filename, Context.MODE_PRIVATE);
            fos.write(wData.getBytes());
            fos.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  1. 读取指定文件的内容
    private String funInternalRead(String filename) {

        String rData = "";

        try {

            FileInputStream fis = openFileInput(filename);
            byte[] bytes = new byte[fis.available()];
            fis.read(bytes);
            fis.close();

            rData = new String(bytes);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return rData;
    }
  1. 其它有用的方法
    getFilesDir() 得到文件的绝对路径
    getDir() 在内部存储上创建目录,如果已经存在就打开。
    deleteFile() 删除指定文件
    fileList() 返回应用存储的文件列表

四、External Storage 外部存储

1、概述

每个Android设备都支持一个共享的外部存储,可以允许你存储文件。它可能是一个可拆卸的SD卡,也可能是不能拆卸的。

2、用法与解析

  • 添加权限
  • 检查外部存储是否可用
  • 外部存储操作
  1. 添加权限
读写权限

这里需要注意, WRITE_EXTERNAL_STORAGE其实包括了读写两个权限,如果只需要读可以只添加读权限。

2、检查外部存储是否可用

    private static boolean isExternalStorageWritable() {

        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        }
        return false;
    }

3、数据写入外部存储指定文件。

public static void funExternalWrite(String data, String filename) {

        FileOutputStream fos = null;

        File file = new File(Environment.getExternalStorageDirectory(), filename);

        if (isExternalStorageWritable()) {

            try {

                fos = new FileOutputStream(file, true);
                fos.write(data.getBytes());

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

4、读取外部存储指定文件

    public static String funExternalRead(String filename) {

        FileInputStream fileInputStream = null;

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        File file = new File(Environment.getExternalStorageDirectory(), filename);

        if (isExternalStorageWritable()) {

            try {

                fileInputStream = new FileInputStream(file);
                int len = 0;
                byte[] data = new byte[1024];
                while ((len = fileInputStream.read(data)) != -1) {
                    byteArrayOutputStream.write(data, 0, len);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fileInputStream != null) {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return new String(byteArrayOutputStream.toByteArray());
    }

五、SQLite数据库

Android provides full support for SQLite databases. Any databases you create will be accessible by name to any class in the application, but not outside the application.

Android完全支持SQLite数据库,应用中的任何数据库都可以通过名称来访问,但是别的应用不可以,当然应用间的数据共享可以用Content Provider来实现。

SQLite数据库使用的一般步骤有:

  1. 新建一个继承自SQLiteOpenHelper 的子类,在其中完成数据库的初始化操作,包括创建表等等。重写onCreate方法在里面完成数据表的创建,onUpdate是数据库更新时调用的。下面这张图是个简单的例子。


    SQLiteOpenHelper 类
  2. 对数据库进行操作前一般先获取一个SQLiteDatabase 对象,利用它我们可以完成对数据库表的操作,比如增删改查等等。下面这张图是一个对数据表执行删除操作的函数,应该比较清楚。


    数据库表删除指定记录

SQLite数据库其实很强大,熟悉常见的sql语句,可以很简单地完成对数据库表的操作。

六、Network Connection 网络存储

网络存储主要是把数据存储到服务器端,这部分内容我会在以后的博客中继续更新,欢迎大家继续关注。

七、总结

应用中数据存储的常见几种方式就是以上这些,内容提供者Content Provider的使用过程中通常会用到SQLite数据库,这个部分的内容大家可以看我之前的第3篇文章。最后,关于数据存储方式部分的使用,稍后会贴出源码链接。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容