数据存储方式:
对比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登录:
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
直接通过例子来看:
还是跳过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四大组件之一的内容提供者和观察者
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?