深入剖析Android中最简单的数据存储方式:SharedPreferences

简介: 深入剖析Android中最简单的数据存储方式:SharedPreferences

 

目录

一. 使用SharedPreferences存储数据

二. 数据的存储位置和格式

三、存储数据的获取

四. 存取复杂类型的数据

五. 设置数据文件的访问权限


Hello,你好呀,我是灰小猿,一个超会写bug的程序猿!

时隔好几天,我又回来了,最近一直忙着考试没时间更新~

今天来和大家分享一篇有关Android中数据存储的文章,它可以说是Android对数据的所有存储方式中最简单的一种存储了,它就是SharedPreferences.

这篇文章,我将详细的和大家讲一下如何使用SharePreferences进行数据的存储和读取,

一. 使用SharedPreferences存储数据

使用SharedPreferences进行存储的数据是存放在一个XML文件中的,同时它的存储方式是是以key-value的形式,key对应着将要存储的数据的名称,value对应存储的值,

使用 SharedPreferences 保存key-value对的步骤一般是这样

1、使用Activity类的getSharedPreferences方法获取到 SharedPreferences 对象,并且在其中指定文件名和该文件的访问权限。在使用该方法时,如果该名称的文件不存在就创建爱你它,如果存在就直接打开。

SharedPreferences sharedPreferences = getSharedPreferences("logininfo", MODE_PRIVATE);

image.gif

其中的第一个参数是文件名,第二个参数是访问权限,我们平常都会将其访问权限设置为private类型,以保护数据不被外界所访问

2、通过sharedPreferences的edit()方法,我们可以获取到该对象的修改权限,并返回SharedPreferences.Editor对象,

SharedPreferences.Editor edits = sharedPreferences.edit();

image.gif

3、使用该对象的 putXxx方法保存key-value对。如我们要在文件中保存用户名为“张三”的这条信。

edits.putString("username",张三);

image.gif

4、通过SharedPreferences.Editor的commit方法保存(提交)key-value对。这样该条信息就会被写入到文件中,并且在不删除的情况下会一直保存。

edits.commit();

image.gif

下面我们通过一个实例来完整的看下数据存储到SharedPreferences的过程,存储用户登录的用户名和密码。

//获取一个文件名为login、权限为private的xml文件的SharedPreferences对象
SharedPreferences sharedPreferences = getSharedPreferences("login", MODE_PRIVATE);
//得到SharedPreferences.Editor对象,并保存数据到该对象中
SharedPreferences.Editor editor = sharedPreferences.edit();
//保存key-value对到文件中
editor.putString("username", “灰小猿”);
editor.putString("password", “123456”);
editor.commit();

image.gif

我们上面也说过,使用该对象的 putXxx方法保存key-value对。

所以 SharedPreferences 提供可以直接保存的数据格式肯定不仅只有String,还有 int、boolean、float、long以及Set<String>。但是存储数据的方式都是一样的步骤。

二. 数据的存储位置和格式

既然是将数据存储到了文件中,那么总会有文件的存储位置的,实际上,SharedPreferences 将数据文件写在了手机内存私有的目录中该app的文件夹下。

下面是在AndroidStudio中查看该文件的方法。

首先打开手机文件的存放目录【File Explorer】,在里面找到data\data\程序包名\shared_prefs目录,

image.gif编辑

就可以发现你命名的login.xml文件。

image.gif编辑

点击文件并查看:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="username">灰小猿</string>
    <string name="password">123456</string>
</map>

image.gif

三、存储数据的获取

上面我们已经讲解了如何对数据进行存储,那么这些存储的数据我们是应该如何获取的呢?

其实通过sharedPreferences存储的数据的读取也是十分容易的,我们只需要使用getSharedPreferences()方法获取到对应的XML文件之后,使用其getXxx方法传入存储时的key即可,

代码如下:

SharedPreferences sharedPreferences = getSharedPreferences("logininfo", MODE_PRIVATE);
String username = sharedPreferences.getString("username", "");
String password = sharedPreferences.getString("password", "");

image.gif

其中的getString()方法中的第一个参数是key的名称,

第二个参数的含义是:“如果没有找到以该key存储的value,那就返回该参数”,如上代码,没有相应的username或password的话就返回“”。

四. 存取复杂类型的数据

如果要用 SharedPreferences 存取复杂的数据类型(类,图像等),就需要对这些数据进行编码。通常会将复杂类型的数据转换成Base64编码,然后将转换后的数据以字符串的形式保存在XML文件中。

实例:

使用 SharedPreferences 保存Product类的一个对象和一张图片。

这里提供了两个Button,用来保存和获取使用 SharedPreferences 保存的Product对象和图片。

保存按钮的点击事件:

bt_prod_save.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    //保存对象
                    Product product = new Product();
                    product.setId(et_prod_id.getText().toString().trim());
                    product.setName(et_prod_name.getText().toString().trim());
                    SharedPreferences sharedPreferences = getSharedPreferences("base64", MODE_PRIVATE);
                    SharedPreferences.Editor editor = sharedPreferences.edit();
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ObjectOutputStream oos = new ObjectOutputStream(baos);
                    oos.writeObject(product);
                    String base64Product = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
                    editor.putString("product", base64Product);
                    //保存图片
                    ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
                    ((BitmapDrawable) getResources().getDrawable(R.drawable.lanbojini)).getBitmap().compress(Bitmap.CompressFormat.JPEG, 100, baos2);
                    String imageBase64 = Base64.encodeToString(baos2.toByteArray(), Base64.DEFAULT);
                    editor.putString("productImg", imageBase64);
                    editor.commit();
                    baos.close();
                    oos.close();
                    Toast.makeText(MainActivity.this, "保存成功!!!", Toast.LENGTH_SHORT).show();
                } catch (Exception e) {
                    Toast.makeText(MainActivity.this, "保存出错了!!!" + e.getMessage(), Toast.LENGTH_SHORT).show();
                }
            }
    });

image.gif

回显按钮的点击事件:

bt_prod_show.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    //获取对象
                    et_prod_id = (EditText) findViewById(R.id.et_prod_id);
                    et_prod_name = (EditText) findViewById(R.id.et_prod_name);
                    SharedPreferences sharedPreferences = getSharedPreferences("base64", MODE_PRIVATE);
                    String productString = sharedPreferences.getString("product", "");
                    byte[] base64Product = Base64.decode(productString, Base64.DEFAULT);
                    ByteArrayInputStream bais = new ByteArrayInputStream(base64Product);
                    ObjectInputStream ois = new ObjectInputStream(bais);
                    Product product = (Product) ois.readObject();
                    et_prod_id.setText(product.getId());
                    et_prod_name.setText(product.getName());
                    //获取图片
                    iv_prod_img = (ImageView) findViewById(R.id.iv_prod_img);
                    byte[] imagByte = Base64.decode(sharedPreferences.getString("productImg",""), Base64.DEFAULT);
                    ByteArrayInputStream bais2 = new ByteArrayInputStream(imagByte);
                    iv_prod_img.setImageDrawable(Drawable.createFromStream(bais2,  "imagByte"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

image.gif

原理:使用Base64把Product对象和图片进行编码成字符串后,然后通过 SharedPreferences 把转换后的字符串保存到xml文件中,在需要使用该对象或者图片时,通过Base64把从 SharedPreferences 获取的字符串解码成对象或者图片再使用。

查看base64.xml 文件,会看到如下内容:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="productImg">/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDABALDA4MChAOoOjM9PDkz......
    </string>
    <string name="product">rO0ABXNyACNjbHViLmxldGdldC50ZXN0c2hhGJqb......
    </string>
</map>

image.gif

注意:

虽然可以采用编码的方式通过 SharedPreferences 保存任何类型的数据,但不建议使用 SharedPreferences 保存尺寸很大的数据。如果要存取更多的数据,一般使用文件存储、SQLite数据库等技术。

五. 设置数据文件的访问权限

我们知道Android系统本质上是Linux,那么Android文件权限也和Linux一致。Linux文件属性分为4段,

第一段:

    • [d]:表示目录
    • [-]:表示文件
    • [l]:表示链接文件
    • [b]:表示可供存储的接口设备文件
    • [c]:表示串口设备文件,例如键盘、鼠标

    第二段:

      • [r]:表示可读
      • [w]:表示可写
      • [x]:表示可执行

      如果不具备某个属性,该项将以[-]代替,例如rw-、--x等

      第三段和第四段和第二段相同,区别在于,第二段表示文件所有者拥有的权限,第三段表示文件所有者所在的用户组中其他用户的权限,第四段表示其他用户的权限。

      例如:-rw-rw-rw,表示这是一个文件,并且该用户和该组内其他用户和其他用户的权限都为可读和可写不可执行。

      那么现在来对比一下我们这篇文章中文件的存储权限,我们在获取 SharedPreferences 对象时,使用的是下面这行代码:

      SharedPreferences sharedPreferences = this.getSharedPreferences("test", MODE_PRIVATE);

      其中getSharedPreferences方法第二个参数就是对文件权限的描述。

      这个参数有四个可选值:

        • Activity.MODE_PRIVATE:表示该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容
        • Activity.MODE_APPEND:也是私有数据,新写入的内容会追加到原文件中
        • Activity.MODE_WORLD_READABLE:表示当前文件可以被其他应用读取
        • Activity.MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入

        但是出于对数据文件安全性的考虑,我们一般使用MODE_PRIVATE权限对文件进行存储。

        好了,关于Android中使用SharedPreferences对数据进行存储的方法就和大家分享到这里,

        有问题的小伙伴可以评论区留言或者添加我的微信!

        我是灰小猿,我们下期见!

        image.gif编辑

        目录
        相关文章
        |
        1月前
        |
        开发框架 前端开发 Android开发
        Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
        本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
        142 4
        |
        4月前
        |
        开发工具 Android开发 开发者
        Android平台如何不推RTMP|不发布RTSP流|不实时录像|不回传GB28181数据时实时快照?
        本文介绍了一种在Android平台上实现实时截图快照的方法,尤其适用于无需依赖系统接口的情况,如在RTMP推送、RTSP服务或GB28181设备接入等场景下进行截图。通过底层模块(libSmartPublisher.so)实现了截图功能,封装了`SnapShotImpl.java`类来管理截图流程。此外,提供了关键代码片段展示初始化SDK实例、执行截图、以及在Activity销毁时释放资源的过程。此方案还考虑到了快照数据的灵活处理需求,符合GB/T28181-2022的技术规范。对于寻求更灵活快照机制的开发者来说,这是一个值得参考的设计思路。
        |
        6月前
        |
        XML 存储 JSON
        51. 【Android教程】JSON 数据解析
        51. 【Android教程】JSON 数据解析
        162 2
        |
        6月前
        |
        存储 API 文件存储
        47. 【Android教程】SharedPreferences 存储
        47. 【Android教程】SharedPreferences 存储
        74 2
        |
        7月前
        |
        数据库 Android开发
        Android 通过升级SettingsProvider数据强制覆盖用户的设置项
        Android 通过升级SettingsProvider数据强制覆盖用户的设置项 【5月更文挑战第7天】
        204 5
        |
        2月前
        |
        存储 大数据 数据库
        Android经典面试题之Intent传递数据大小为什么限制是1M?
        在 Android 中,使用 Intent 传递数据时存在约 1MB 的大小限制,这是由于 Binder 机制的事务缓冲区限制、Intent 的设计初衷以及内存消耗和性能问题所致。推荐使用文件存储、SharedPreferences、数据库存储或 ContentProvider 等方式传递大数据。
        98 0
        |
        4月前
        |
        存储 安全 API
        Android经典实战之存储方案对比:SharedPreferences vs MMKV vs DataStore
        本文介绍了 Android 开发中常用的键值对存储方案,包括 SharedPreferences、MMKV 和 DataStore,并对比了它们在性能、并发处理、易用性和稳定性上的特点。通过实际代码示例,帮助开发者根据项目需求选择最适合的存储方案,提升应用性能和用户体验。
        144 1
        |
        4月前
        |
        JSON Java Android开发
        Android 开发者必备秘籍:轻松攻克 JSON 格式数据解析难题,让你的应用更出色!
        【8月更文挑战第18天】在Android开发中,解析JSON数据至关重要。JSON以其简洁和易读成为首选的数据交换格式。开发者可通过多种途径解析JSON,如使用内置的`JSONObject`和`JSONArray`类直接操作数据,或借助Google提供的Gson库将JSON自动映射为Java对象。无论哪种方法,正确解析JSON都是实现高效应用的关键,能帮助开发者处理网络请求返回的数据,并将其展示给用户,从而提升应用的功能性和用户体验。
        115 1
        |
        4月前
        |
        缓存 API Android开发
        Android经典实战之Kotlin Flow中的3个数据相关的操作符:debounce、buffer和conflate
        本文介绍了Kotlin中`Flow`的`debounce`、`buffer`及`conflate`三个操作符。`debounce`过滤快速连续数据,仅保留指定时间内的最后一个;`buffer`引入缓存减轻背压;`conflate`仅保留最新数据。通过示例展示了如何在搜索输入和数据流处理中应用这些操作符以提高程序效率和用户体验。
        57 6
        |
        4月前
        |
        编解码 网络协议 前端开发
        如何实现Android平台GB28181设备接入模块按需打开摄像头并回传数据
        后台采集摄像头,如果想再进一步扩展,可以把android平台gb28181的camera2 demo,都移植过来,实现功能更强大的国标设备侧,这里主要是展示,收到国标平台侧的回传请求后,才打开摄像头,才开始编码打包,最大限度的减少资源的占用