数据存储全方案,详解持久化技术:文件存储、SharedPreferences存储

2024-05-12 10:08

本文主要是介绍数据存储全方案,详解持久化技术:文件存储、SharedPreferences存储,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 文件存储
    • 将数据存储到文件中
    • 从文件中读取数据
  • SharedPreferences存储
    • 存入数据
    • 从 SharedPreferences中读取数据
    • 实践:实现记住密码功能
  • SQLite 数据库

文件存储

文件存储是 Android 中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的文本数据或二进制数据

将数据存储到文件中

Context 类中提供了一个 openFileOutput() 方法,可以用于将数据存储到指定文件中,这个方法接收两个参数:第一个是文件名,在文件创建的时候使用,这里指定的文件名不可包含路径,因为所有文件都默认存储到 /data/data/<package name>/files/目录下;第二个参数是操作模式,主要有 MODE_PRIVATEMODE_APPEND两种模式可选,默认是 MODE_PRIVATE,表示当指定相同文件名的时候,所写入的内容将会覆盖原文件中的内容,而 MODE_APPEND 则表示如果该文件已存在,就往文件里面追加内容,不存在就创建新文件

openFile0utput()方法返回的是一个 File0utputStream 对象,得到这个对象之后就可以使用Java流的方式将数据写人文件中了。以下是一段简单的代码示例,展示了如何将一段文本内容保存到文件中:

fun save(inputText:String){try {val output = openFileOutput("data", Context.MODE_PRIVATE)val writer = BufferedWriter(OutputStreamWriter(output))writer.use { it.write(inputText)}} catch (e: Exception) {e.printStackTrace()}}

这里使用了一个 use函数,这是 Kotlin 提供的一个内置扩展函数,它会保证在 Lambda 表达式中的代码全部执行完之后自动将外层的流关闭,这样就不需要我们再编写一个 finally 语句手动关闭流了

先看一个栗子,然后先用 java 写

栗子

activity_test.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><EditTextandroid:id="@+id/edit"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Type something here" />
</LinearLayout>

Test.java

public class MainActivity extends AppCompatActivity {private EditText edit;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);edit = findViewById(R.id.edit);}@Overrideprotected void onDestroy() {super.onDestroy();String inputText = edit.getText().toString();save(inputText);}public void save(String inputText) {FileOutputStream out = null;BufferedWriter writer = null;try {out = openFileOutput("data", Context.MODE_PRIVATE);writer = new BufferedWriter(new OutputStreamWriter(out));writer.write(inputText);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (writer != null) {try {writer.close();} catch (IOException e) {e.printStackTrace();}}}}
}

界面上有一个文本输入框。然后在文本输入框中随意输入点什么内容,再 按下 Back键,这时输入的内容肯定就已经丢失了,因为它只是瞬时数据,在活动被销毁后 就会被回收。而这里我们要做的,就是在数据被回收之前,将它存储到文件当中

这里写图片描述

打开Device File Explorer,在/data/data/包名/files/目录下,生成了一个 data 文件
这里写图片描述

将data文件导出到电脑上,查看内容
这里写图片描述
现在换 Kotlin 写法

class MainActivity : BaseActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)}override fun onDestroy() {super.onDestroy()save(edit.text.toString())}fun save(inputText:String){try {val output = openFileOutput("data", Context.MODE_PRIVATE)val writer = BufferedWriter(OutputStreamWriter(output))writer.use {it.write(inputText)}} catch (e: Exception) {e.printStackTrace()}}
}

从文件中读取数据

那么我们就来继续完善上一小节中的例子,使得重新 启动程序时EditText中能够保留我们上次输入的内容。修改TestActivity中的代码,如下所示

 @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test);edit = (EditText) findViewById(R.id.edit);String inputText = load();if(!TextUtils.isEmpty(inputText)){edit.setText(inputText);edit.setSelection(inputText.length());Toast.makeText(this,"Restoring succeeded",Toast.LENGTH_SHORT).show();}}
......public String load(){FileInputStream in = null;BufferedReader reader = null;StringBuilder content = new StringBuilder();try {in = openFileInput("data");reader = new BufferedReader(new InputStreamReader(in));String line = "";while ((line = reader.readLine())!=null){content.append(line);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if(reader != null){try {reader.close();} catch (IOException e) {}}}return content.toString();}

现在换 Kotlin 写法

fun load():String{val content = StringBuilder()val input = openFileInput("data")val reader = BufferedReader(InputStreamReader(input))reader.use { reader.forEachLine { content.append(it)}}return content.toString()}

类似于将数据存储到文件中,Context 类中还提供了一个 openFileInput() 方法,用于从文件中读取数据,它只接收一个参数,即要读取的文件名,然后系统会自动到 /data/data/<package name>/files/目录下加载这个文件,并返回一个 FileInputStream 对象

首先通过 openFileInput()方法获取一个 FileInputStream 对象,然后借助它构建出一个 InputStreamReader 对象,接着再使用 InputStreamReader 构建出一个 BufferedReader 对象,这样我们就可以通过 BufferedReader 将文件的数据一行行读取出来

这里从文件中读取数据使用了一个 forEachLine 函数,这也是 Kotlin 提供的一个内置扩展函数,它会将读到的每行内容都回调到 Lambda 表达式中,我们在 Lambda 表达式中完成拼接逻辑即可

现在重新运行一下程序,刚才保存的 Content字符串肯定会被填充到 EditText中,然后编写一点其他的内容,比如在 EditText中输入 Hello,接着按下 Back键退出程序,再重新启 动程序,这时刚才输入的内容并不会丢失,而是还原到了 EditText中

文章目录

  • 文件存储
    • 将数据存储到文件中
    • 从文件中读取数据
  • SharedPreferences存储
    • 存入数据
    • 从 SharedPreferences中读取数据
    • 实践:实现记住密码功能
  • SQLite 数据库

SharedPreferences存储

SharedPreferences是使用键值对的方式来存储数据的。也就是 说当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可 以通过这个键把相应的值取出来

而且 SharedPreferences 还支持多种不同的数据类型存储, 如果存储的数据类型是整型,那么读取出来的数据也是整型的,存储的数据是一个字符串, 读取出来的数据仍然是字符串

存入数据

xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:id="@+id/save_data"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Save data" />
</LinearLayout>

Activity.java

import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private Button saveData;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);saveData = findViewById(R.id.save_data);saveData.setOnClickListener(view -> {SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();editor.putString("name", "Tom");editor.putInt("age", 28);editor.putBoolean("married", false);editor.apply();});}
}

运行结果是,点击按钮会生成一个 data.xml 保存一些数据,打开 Device File Explorer,在目录/data/data/包名/shared_prefs/下生成了一个data.xml文件,双颊打开查看内容
在这里插入图片描述

可以看到,我们刚刚在按钮的点击事件中添加的所有数据都已经成功保存下来了,并且 SharedPreferences文件是使用 XML格式来对数据进行管理的

要想使用 SharedPreferences 来存储数据,首先需要获取到 SharedPreferences 对象

Android 中主要提供了三种方法用于得到 SharedPreferences对象

  1. Context 类中的 getSharedPreferences()方法
    此方法接收两个参数,第一个参数用于指定 SharedPreferences文件的名称,如果指 定的文件不存在则会创建一个,SharedPreferences 文件都是存放在/data/data/包名/shared_prefs/目录下的。名称不用带后缀,后缀会由Android自动加上

    第二个参数用于指定操作模式,主要只有一种模式可以选 择,MODE_PRIVATE
    MODE_PRIVATE仍然是默认的操 作模式,和直接传入 0 效果是相同的,表示只有当前的应用程序才可以对这个 SharedPreferences文件进行读写。其他几种模式都已废弃,不说了

  2. Activity类中的 getPreferences()方法
    这个方法和 Context中的 getSharedPreferences() 方法很相似,不过它只接收一个操 作模式参数,因为使用这个方法时会自动将当前活动的类名作为 SharedPreferences 的文件名

得到了SharedPreferences对象之后,就可以开始向SharedPreferences文件中存储数据了, 主要可以分为三步实现
① 调用 SharedPreferences对象的 edit()方法来获取一个 SharedPreferences.Editor 对象
② 向 SharedPreferences.Editor 对象中添加数据,比如添加一个布尔型数据就使用 putBoolean()方法,添加一个字符串则使用 putString()方法,以此类推
③ 调用 apply() 方法将添加的数据提交,从而完成数据存储操作

下面改用 Kotlin 写一遍刚才的例子,布局相同

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)save_data.setOnClickListener {val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()editor.putString("name","Tom")editor.putInt("age",28)editor.putBoolean("married",false)editor.apply()}}
}

从 SharedPreferences中读取数据

SharedPreferences对象 中提供了一系列的 get 方法用于对存储的数据进行读取, 每种 get 方法都对应了 SharedPreferences. Editor 中的一种 put 方法

比如读取一个布尔型数据就使用 getBoolean() 方法,读取一个字符 串就使用 getString()方法

这些 get方法都接收两个参数,第一个参数是键,传入存储数据 时使用的键就可以得到相应的值了,第二个参数是默认值,即表示当传入的键找不到对应的 值时,会以什么样的默认值进行返回

修改上面的栗子,布局增加一个恢复数据的按钮

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical">......<Buttonandroid:id="@+id/restore_data"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Restore data" />
</LinearLayout>
public class Test extends AppCompatActivity {...private Button restoreData;@Overrideprotected void onCreate(Bundle savedInstanceState) {...restoreData = (Button)findViewById(R.id.restore_data);...restoreData.setOnClickListener(view -> {SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE);String name = pref.getString("name","");int age = pref.getInt("age",0);boolean married = pref.getBoolean("married",false);Log.d("MainActivity", "name is " + name);Log.d("MainActivity", "age is " + age);Log.d("MainActivity", "married is " + married);});}
}

运行结果:我们在还原数据按钮的点击事件中首先通过 getSharedPreferences()方法得到 了 SharedPreferences对象,然后分别调用它的 getString()getInt()getBoolean()方法去获取 前面所存储的姓名、年龄和是否已婚,如果没有找到相应的值就会使用方法中传入的默认值 来代替,最后通过 Log将这些值打印出来
在这里插入图片描述
下面改用 Kotlin 实现

restore_data.setOnClickListener {val prefs = getSharedPreferences("data", Context.MODE_PRIVATE)val name = prefs.getString("name","")val age = prefs.getInt("age",0)val married = prefs.getBoolean("married",false)Log.d("MainActivity", "name is $name")Log.d("MainActivity", "age is $age")Log.d("MainActivity", "married is $married")}

实践:实现记住密码功能

在这里插入图片描述

login.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:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="60dp"android:orientation="horizontal"><TextViewandroid:layout_width="90dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:text="Account:"android:textSize="18sp" /><EditTextandroid:id="@+id/accountEdit"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="1" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="60dp"android:orientation="horizontal"><TextViewandroid:layout_width="90dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:text="Password:"android:textSize="18sp" /><EditTextandroid:id="@+id/passwordEdit"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="1" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><CheckBoxandroid:id="@+id/rememberPass"android:layout_width="wrap_content"android:layout_height="wrap_content" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Remember password"android:textSize="18sp" /></LinearLayout><Buttonandroid:id="@+id/login"android:layout_width="200dp"android:layout_height="60dp"android:layout_gravity="center_horizontal"android:text="Login" />
</LinearLayout>

LoginActivity

public class MainActivity extends AppCompatActivity {private EditText accountEdit;private EditText passwordEdit;private Button login;private CheckBox rememberPass;private SharedPreferences preferences;private SharedPreferences.Editor editor;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);accountEdit = findViewById(R.id.accountEdit);passwordEdit = findViewById(R.id.passwordEdit);rememberPass = findViewById(R.id.rememberPass);login = findViewById(R.id.login);preferences = PreferenceManager.getDefaultSharedPreferences(this);boolean isRemember = preferences.getBoolean("remember_password", false);if (isRemember) {//将账号和密码都设置到文本框String account = preferences.getString("account", "");String password = preferences.getString("password", "");accountEdit.setText(account);passwordEdit.setText(password);rememberPass.setChecked(true);}login.setOnClickListener(view -> {String account = accountEdit.getText().toString();String password = passwordEdit.getText().toString();if (account.equals("admin") && password.equals("123456")) {editor = preferences.edit();if (rememberPass.isChecked()) {editor.putBoolean("remember_password", true);editor.putString("account", account);editor.putString("password", password);} else {editor.clear();}editor.apply();/*Intent intent = new Intent(MainActivity.this, MainActivity.class);startActivity(intent);finish();*/Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();} else {Toast.makeText(MainActivity.this, "accountorpassword is invalid", Toast.LENGTH_SHORT).show();}});}
}

登录成功时,如果没有被选中,就简单地调用一下 clear()方法,将 SharedPreferences 文件中的数据全部清除掉。 当用户选中了记住密码复选框,并成功登录一次之后,remember_password 键对应的值 就是 true了,这个时候如果再重新启动登录界面,就会从 SharedPreferences文件中将保存的 账号和密码都读取出来,并填充到文本输入框中,然后把记住密码复选框选中,这样就完成 记住密码的功能了

不过需要注意,这里实现的记住密码功能仍然还只是个简单的示例,并不能在实际的项 目中直接使用。因为将密码以明文的形式存储在 SharedPreferences文件中是非常不安全的, 很容易就会被别人盗取,因此在正式的项目里还需要结合一定的加密算法来对密码进行保护 才行

下面改用 Kotlin 实现

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val prefs = getPreferences(0)val isRemmber = prefs.getBoolean("remember_password", false)if (isRemmber) {val account = prefs.getString("account", "")val password = prefs.getString("password", "")accountEdit.setText(account)passwordEdit.setText(password)rememberPass.isChecked = true}login.setOnClickListener {val account = accountEdit.text.toString()val password = passwordEdit.text.toString()if (account == "admin" && password == "123456") {val editor = prefs.edit()if (rememberPass.isChecked) {editor.putBoolean("remember_password", true)editor.putString("account", account)editor.putString("password", password)} else {editor.clear()}editor.apply()Toast.makeText(this, "登陆成功", Toast.LENGTH_SHORT).show()} else {Toast.makeText(this,"accountorpassword is invalid",Toast.LENGTH_SHORT).show();}}}
}

SQLite 数据库

可以查看以下文章
https://blog.csdn.net/u010356768/article/details/79391064

这篇关于数据存储全方案,详解持久化技术:文件存储、SharedPreferences存储的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/982325

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了