数据存储方式:
对比sql数据库呗


1.文件存储
方式同java中I/O输入输出流一样

举个例子:

public class 字节流_写文本文件 {
public static void main(String[] args) throws IOException {
/**字节流创建文本文件。在D盘SS目录中创建students.txt文件,并添加以下信息:
* 姓名:张三,性别:男,年龄:19,成绩:90,入学日期:2022-9-10
* 姓名:李四,性别:女,年龄:19,成绩:95,入学日期:2022-9-9
*/
String str="姓名:张三,性别:男,年龄:19,成绩:90,入学日期:2022-9-10 " +
"姓名:李四,性别:女,年龄:19,成绩:95,入学日期:2022-9-9";
OutputStream fileOutputStream = new FileOutputStream("D:\\SS\\students.txt");
//OutputStream是所有输出流的父类,为一个抽象类
byte[] bytes = str.getBytes(); // getBytes()将字符串转换为字节数组
for (int i = 0; i < bytes.length; i++) {
fileOutputStream.write(bytes[i]);
}
fileOutputStream.close(); //关闭输出流并释放与其关联的所有系统资源
}
}
public class 字节流_读文本文件 {
public static void main(String[] args) throws IOException {
InputStream fileInputStream = new FileInputStream("D:\\SS\\students.txt");
byte[] bytes =new byte[170];
int b;
while ((b=fileInputStream.read(bytes))!=1){
System.out.println(new String(bytes,0,b));
//将指定byte数组中从起始索引off开始的len字节写入输出流
// b={'1' ,'2', '3', '4', '5', '6', '7', '8'};
// String item=new String(b,2,2)
// 结果 item=34
}
fileInputStream.close();
}
}

来看看Android中的:
首先分为内部存储和外部存储
:外部存储就是SD卡之类的 属于永久性的存储方式 但不安全

三项:
获取SD卡的状态
判断是否可用
获取SD卡路径
String state = Environment.getExternalStorageState();
state.equals(Environment.MEDIA_MOUNTED);
Environment.getExternalStorageDirectory();
然后再通过输入输出流写入读取就行
注意注意:申请SD卡写读文件的权限 要在清单文件中静态申请权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

主要来看看内部存储:
通过一个案例模拟qq登录:
image
image
layout布局就不写了

我没写trycatch这样是不对的 没有处理异常 所以在启动后会报错找不到文件
但主要是为了验证 所以手动在data/data/包名/files/中创建
package com.example.store;
import android.content.Context;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
//工具类 实现QQ的账号密码存储和读取
public class Tool_qqAcitivity {
public static boolean saveUser(Context context, String user, String pwd) throws IOException {
FileOutputStream fos = context.openFileOutput("haha.txt", Context.MODE_PRIVATE);
//将数据转换为字节码的形式写入haha.txt中
fos.write((user + ":" + pwd).getBytes());
fos.close();
return true;
}
public static Map<String, String> getUser(Context context) throws IOException {
String str = "";
FileInputStream fis = context.openFileInput("haha.txt");
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
str = new String(buffer);
HashMap<String, String> map = new HashMap<String, String>();
String[] split = str.split(":");
if (split.length >= 2) {
map.put("user", split[0]);
map.put("pwd", split[1]);
} else {
// 处理错误情况,例如文件内容格式不正确或为空
// 可以选择抛出异常、记录日志或返回null等
// 这里只是简单地返回一个空的map
return new HashMap<>();
}
fis.close();
return map;
}
}
package com.example.store;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import java.io.IOException;
import java.util.Map;
public class QQ_Activity extends AppCompatActivity {
//初始化
private EditText et_account;
private EditText et_password;
private Button btn_login;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qq);
et_account = findViewById(R.id.et_account);
et_password = findViewById(R.id.et_password);
btn_login = findViewById(R.id.btn_login);
try {
Map<String, String> user = Tool_qqAcitivity.getUser(this);
if(user!=null){
et_account.setText(user.get("user"));
et_password.setText(user.get("pwd"));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String user = et_account.getText().toString().trim();
String pwd = et_password.getText().toString();
//检查是否为空
if(TextUtils.isEmpty(user)){
Toast.makeText(QQ_Activity.this, "请输入账号", Toast.LENGTH_SHORT).show();
return;
}
if(TextUtils.isEmpty(pwd)){
Toast.makeText(QQ_Activity.this, "请输入密码", Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(QQ_Activity.this, "登录成功", Toast.LENGTH_SHORT).show();
//保存用户信息 调用工具
try {
boolean b = Tool_qqAcitivity.saveUser(QQ_Activity.this, user, pwd);
if(b){
Toast.makeText(QQ_Activity.this, "保存成功", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(QQ_Activity.this, "保存失败", Toast.LENGTH_SHORT).show();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
}

需要注意的就是:
内部存储 当创建的应用程序被卸载时 内部存储文件也随之删除
权限参数:
MODE_PRIVATE 只能被当前程序读写
MODE_APPEND 内容可以追加
MODE_WORLD_READABLE 内容可被其他程序读
MODE_WORLD_WRITEABLE 内容可被其他程序写


2.SharedPreferences存储
一个轻量级存储类

获取实例对象
SharedPreferences 名字 = getSharedPreferences("名字", MODE_PRIVATE);
获取编辑器
通过键值对形式储存(put方法)在data/data/包名/shared_prefs中的XML
SharedPreferences.Editor edit = 名字.edit();
edit.putString("key","value");
edit.commit();
读取数据
名字.getString("key","不存在返回这个");
移除数据
edit.remove("key");
edit.clear();

3.SQLite数据库存储
这个就有意思了 是Android自带的一个小型数据库
储存的文件在这里:
参考:https://blog.csdn.net/hjjshua/article/details/124150812
image
直接通过例子来看:

还是跳过layout布局文件

package com.example.store;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import org.w3c.dom.Text;
//在Activity中编写 与前面传递过来的属性 进行操作
public class SimpleDataActivity extends AppCompatActivity implements View.OnClickListener {
//数据初始化
MyHelper helper;
private EditText et_id;
private EditText et_name;
private EditText et_age;
private Button btn_add;
private Button btn_query;
private Button btn_update;
private Button btn_delete;
//用来展示结果
private TextView tv_show;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
//调用自己继承SQLiteOpenHelper的MyHelper
helper = new MyHelper(this);
et_id = findViewById(R.id.et_id);
et_name = findViewById(R.id.et_name);
et_age = findViewById(R.id.et_age);
btn_add = findViewById(R.id.btn_add);
btn_query = findViewById(R.id.btn_query);
btn_update = findViewById(R.id.btn_update);
btn_delete = findViewById(R.id.btn_delete);
tv_show = findViewById(R.id.tv_show);
//第三种方法别忘记写这个
btn_add.setOnClickListener(this);
btn_query.setOnClickListener(this);
btn_update.setOnClickListener(this);
btn_delete.setOnClickListener(this);
}
//使用第三种方法继承接口实现方法 按钮点击后的改变 增删改查也在这里实现
/*
ContentValues类 它负责存储一些名值对
它存储的名值对当中的名是一个String类型 而值都是基本类型
*/
@Override
public void onClick(View v) {
String id, name, age;
SQLiteDatabase db;
ContentValues values;
//Google写好的增删改查api
if (v.getId() == R.id.btn_add) {
//增
//获取输入框的值
id = et_id.getText().toString();
name = et_name.getText().toString();
age = et_age.getText().toString();
//获取可读写的SQLiteDatabase对象
db = helper.getWritableDatabase();
//创建ContentValues对象
values = new ContentValues();
values.put("id", id);
values.put("name", name);
values.put("age", age);
//三个参数 1.数据表名字 2.如果发现将要插入的行为空行 列名设为null 3.ContentValues对象
db.insert("gao", null, values);
Toast.makeText(this, "信息也添加", Toast.LENGTH_SHORT).show();
//当使用完SQLiteDatabase对象后调用close方法关闭数据库连接 否则数据库一直存在 消耗内存
db.close();
}
//查
if (v.getId() == R.id.btn_query) {
//获取可读的SQLiteDatabase对象
db = helper.getReadableDatabase();
//Cursor是一个游标接口 提供了便利查询结果的方法
Cursor cursor = db.query("gao", null, null, null, null, null, null);
//判断下是否有数据
//两种Cursor对象的遍历方法
//第一种先输出第一个值 再移动游标
if (cursor.getCount() == 0) {
Toast.makeText(this, "无数据", Toast.LENGTH_SHORT).show();
} else {
cursor.moveToNext();
tv_show.append("id:" + cursor.getString(0) + " name:" + cursor.getString(1) + " age" + cursor.getString(2));
}
while (cursor.moveToNext()) {
tv_show.append("\n" + "id:" + cursor.getString(0) + " name:" + cursor.getString(1) + " age" + cursor.getString(2));
}
//第二种先移动游标 再输出第一个值
// if(cursor.getCount()>0){
// while (cursor.moveToNext()) {
// tv_show.append("id:" + cursor.getString(0) + " name:" + cursor.getString(1) + " age" + cursor.getString(2));
// }
// }else {
// Toast.makeText(this, "无数据", Toast.LENGTH_SHORT).show();
// }
//用完Cursor 要关闭 否则造成内存泄漏
cursor.close();
db.close();
}
//修
if (v.getId() == R.id.btn_update) {
db = helper.getWritableDatabase();
values = new ContentValues();
values.put("name",name=et_name.getText().toString());
values.put("age",name=et_age.getText().toString());
// 数据表名字 最新数据 要修改数据的查找条件 查找条件的参数
db.update("gao",values,"id=?",new String[]{
et_id.getText().toString()
});
Toast.makeText(this,"信息已修改",Toast.LENGTH_SHORT).show();
db.close();
}
//删
if (v.getId() == R.id.btn_delete) {
db = helper.getWritableDatabase();
db.delete("gao",null,null);
Toast.makeText(this,"信息已删除",Toast.LENGTH_SHORT).show();
db.close();
}
}
//数据库和表的创建
class MyHelper extends SQLiteOpenHelper {
public MyHelper(Context context) {
//四个参数 上下文 数据库名字 游标工厂 数据库版本
super(context, "gao.db", null, 2);
}
// 创建表
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table gao(id int primary key,name varchar,age int)");
}
//数据库版本号增加时调用
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}

总结一下这个:
大致里面写的注释也够清楚了
其中两种Cursor对象的遍历方法
参考 https://blog.csdn.net/qq_64628470/article/details/129974041

当然还有其他数据存储方式
也对应着Android四大组件之一的内容提供者和观察者

posted on   不爱美女爱辣条  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?



点击右上角即可分享
微信分享提示