sqlite还是文件存储?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sqlite还是文件存储?相关的知识,希望对你有一定的参考价值。

本人android小菜一枚,最近在开发一个自己的app。
现在的问题,要把用户编辑后的文章(含图片,以后可能含音频)在本地存储。
是sqlite还是文件IO速度快?
我在数据流的设计思路如下,有哪些不合理的地方?
用户编辑完一次页面确认,异步一个线程。该线程将数据实例化到BO类,然后将其持久化(到sqlite或xml文件);图片存在本地(是否安全?用户使用“猎豹清理大师”之类的时候,会不会给清掉?)。
后续还有数个需要读取该数据的功能模块。

如果sqlite,我不需要复杂的查询;如果xml,担心解析DOM耗资源。

小弟谢谢大神!

比较好的方案是,内容结构用sqlite存,便于检索,访问也快。二进制数据(图片,音频等)用文件存本地。

比如你一篇文章的内容,我假设是这样, 分别有文本, 图片和音频内容
<record>
<title>tttttttt</title>
<content>xxxxxxxxxxxxx</content>
<image>/mnt/sdcard/yyyyy.jpg</image>
<image>/mnt/sdcard/kkk.jpg</image>
<audio>/mnt/sdcard/zzzzzzzz.amr</audio>
</record>
设计一数据库表 record,列分别是
rid 主键 int
title 文章标题 text
content 文章内容 text

再设计一个附件表attachment, 列分别是
aid 主键 int 附件的主键
rid 外键 int 关联到record
type 附件类型 int 自定义一下,比如0代表图片,1代表音频,其他可以扩展
name 附件名 text 实际上是保存时生成的MD5摘要,保证附件不重复保存
path 文件存放路径 text

这样一篇文章可以有任意数量的图片/音频和它对应,便于扩展,重复指定图片或音频,也不会重复保存。

都不需要写什么数据持久类,写一个数据封装就可以了,比如定义一个
class RecordWrapper
public int getRecordCount(); //查询表record的记录总数
public SparseArray<String> getTitles(); //返回标题列表(这个如果太多,可以做分页)
//SparseArray稀疏数组,这个容器很方便,可以按下标像数组一样访问顺序访问,也可以按 //key-value像map一样访问,非常适合这里,key就保存记录主键,value保存title,
//遍历这个表,可以访问title,也可以映射到主键
public RecordData getRecord(int rid) ; //通过主键查询记录

public int saveRecord(RecordData record); //入库一条记录,返回主键
public void updateRecord(int rid, RecordData record); //更新记录
public void deleteRecord(int rid); //删除一条记录

RecordData AttachmentData分别是记录,和附件等数据封装类,这些类实际上是双重用途的
用于查询和用于入库时,其某些字段(主要是主键和附件表)内容不一定相同。
比如
class RecordData
int rid; //用于入库时填一个负数,代表入库前主键还未知,用于查询时它是记录的主键值
String title; //标题

String context; //正文
List<AttachmentData> attachList;//附件列表,入库时它容纳待入库附件,查询时,
//返回和记录相关联的附件清单


class AttachmentData
int aid;

int rid;
int type;
String name;
String path;


这里需要注意的一点就是name是一个用md5算法,根据附件文件数据生成的摘要字符串。
每次入库时,把附件原来的文件,复制到你的数据目录,并改名成这个摘要字符串。如果发现已经存在了,就不复制,这样就不会重复拷贝数据文件。
删除记录时,同时检查它的附件表中每一个附件,如果同名附件只有这一个了,就删除附件,如果还有其他记录使用了这个附件,就保留附件文件,只删除当前处理的附件项。

另外,只要数据不是太多(5M以内)保存单条记录都不大需要另开线程异步保存的,android还是比较快,直接存也是可以接受的。
参考技术A sqlite是用来存一些应用数据,例如100张图片,每张图片的信息就用数据库,包括文件的路径

但是像文件,图片等资源通常内存占用非常大,通常是保存到SD卡,不会保存到数据库的
做自己的app? 要帮忙吗,刚帮人做完一个,现在有空
参考技术B android的数据库是保存在虚拟AVD设备中的吧。(就是你给AVD分配的那块空间里)
你用adb连接上AVD设备,在从相应的路径里取出数据库文件。
是否可以解决您的问题?追问

sqlite还是文件IO速度快?

Android数据存储文件存储SQLite数据库简单使用 sharedPreferences存储

5 数据存储

5.1 数据的存储方式

补充

  • 文件存储:读取方式和java中I/O程序是完全一样的
  • SharedPreferences:这是Android提供的用来存储一些简单的配置信息的一种机制,用于存储一些应用程序的各种配置信息,入用户名,密码等
  • SQlite数据库:是Android自带的一个轻量级的数据库,,一般使用它作为复杂数据的存储引擎,可以存储用户信息等
  • ContentProvider:Android四大组件之一
  • 网络存储:需要Android网络数据包打交道,间数据存储到服务器上

5.2 文件存储

5.2.1 将数据存入文件中

1.内部存储

​ 内部存储值得是将应用程序中的数据以文件的形式存储到应用中(该文件默认位于data/data/< packagename > ,此时存储的文件会被所在的应用程序私有化,如果其他程序想要操作本应用程序中的文件,需要权限设置,当创建的应用程序被卸载的时候,期内部存储文件也会随之被删除

示例代码:

参数:“name”表示文件名:“mode”表示文件的操作模式,也就是读写文件的方式

2.外部存储

​ 外部存储是将数据以文件的形式存储到一些外部设备上,例如SD卡或者设备内钱嵌的存储卡,属于永久性的存储方式(外部存储的文件通常位于mnt/sdcard目录下,不同的手机路径可能不同)外部的存储文件可以被其他应用程序所共享,当将外部存储文件设备连接计算机上时,这些文件可以被浏览,修改和删除,因此此方式不安全

第一行:获取外部设备的状态

第二行:判断外部设备是否可用

加粗样式

5.2.2 从文件中读取数据

1.读取内部存储中的文件数据

byte[] bytes= new byte[fis.available()]创建缓冲区,并获取文件的长度

2.读取外部存储中的文件数据

​ 首先需要读取外部设备(SD卡)的路径,并通过该路径读取相应的文件的数据

静态申请权限

动态申请权限

​ 申请正常权限时使用静态申请权限的方式即可,但对于一些涉及用户隐私的危险权限需要用户的授权后才可以使用,因此危险权限不仅需要在清单文件(AndroidManifest.xml)的< manifest >节点添加权限,还需在代码中动态申请权限,以动态申请SD卡的写权限为例:

requestPermissions()方法中包含3个参数,第1个参数为Context上下文,第2个参数需要申请的权限,第3个参数为请求码

​ 添加完动态申请权限后,运行程序,界面会弹出是否允许请求权限的对话框,有用户进行授权,如下图所示:

​ 上述图片内容为“是否允许访问设备上照片、媒体和文件的申请权限”DENY按钮表示拒绝,ALLOW按钮表示允许,当用户点击对话框中的“ALLow"按钮时,程序会执行动态申请权限回调方法onRequestPermissionsResult(),该方法中可以获去用户授予申请权限的结果。

​ 方法中包含三个参数:第一个参数requestCode表示请求码,permissions表示请求的权限,grantResults表示用户授予权限的结果。

5.2.3 实战演练-保存QQ账号和密码

使用文件存储的方式保存数据

FileSaveQQ.java

package com.example.administrator.saveqq;

import android.content.Context;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class FileSaveQQ {
    //保存QQ账号和登录密码到data.txt 文件中
    public static Boolean saveUserInfo(Context context, String account, String passwoed) {
        FileOutputStream fos = null;
        try {
            //获取文件的输出流对象fos
            fos = context.openFileOutput("data.txt", Context.MODE_PRIVATE);
            //将数据以字节码的方式写入data.txt文件中
            fos.write((account + ":" + passwoed).getBytes());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
    //从data.txt文件中获取存储的QQ号账号密码
    public static Map<String,String> getUsetInfo(Context context){
        String content = "";
        FileInputStream fis = null;
        try {
            //获取文件的输入流对象
            fis = context.openFileInput("data.txt");
            //将输入流对象中的数据转换为字节码的形式
            byte[] bytes= new byte[fis.available()];
            fis.read(bytes);//通过read()方法读取字节码文件中的数据
            content = new String(bytes);//将获取的字节码转换为字符串
            Map<String ,String> map = new HashMap<>();
            //将字符串以":"进行分割,然后形成一个数组的形式
            String[] infos= content.split(":");
            //讲述租的第一个数据放入userMap集合中
            map.put("account",infos[0]);
            map.put("password",infos[1]);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if(fis!=null){
                    fis.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

}

MainActivity.java

package com.example.administrator.saveqq;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.util.HashMap;
import java.util.Map;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText et_account;//账号输入框
    private EditText et_password;//密码输入框
    private Button but_login;//登录按钮
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

        //通过工具类FileSaveQQ中的getUserInfo()方法获取QQ帐号和密码信息
        Map<String ,String> userInfo = FileSaveQQ.getUsetInfo(this);
        //Map<String ,String> userInfo = SPSaveQQ.getUserInfo(this);
        if(userInfo!=null){
            et_account.setText(userInfo.get("account"));
            et_password.setText(userInfo.get("password"));
        }
    }

    private void initView() {
        et_account=findViewById(R.id.et_account);
        et_password=findViewById(R.id.et_password);
        but_login=findViewById(R.id.but_login);
        but_login.setOnClickListener(this);

    }
    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.but_login:
                //当点击“登录”按钮的时候,获取界面输入的账号和密码
                String account = et_account.getText().toString().trim();
                String password = et_password.getText().toString();
                //检验输入的账号和密码是否为空
                if(TextUtils.isEmpty(account)){
                    Toast.makeText(this,"你输入的账号为空",Toast.LENGTH_SHORT).show();
                    return;
                }
                if(TextUtils.isEmpty(password)){
                    Toast.makeText(this,"你输入的密码为空",Toast.LENGTH_SHORT).show();
                    return;
                }
                Toast.makeText(this,"登陆成功!",Toast.LENGTH_SHORT).show();

                boolean isSave = FileSaveQQ.saveUserInfo(this,account,password);
               // boolean isSave = SPSaveQQ.saveUserInfo(this,account,password);
                if(isSave){
                    Toast.makeText(this,"保存成功",Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(this,"保存失败",Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

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">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/a1"
        android:layout_gravity="center"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="用户名:" />

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ems="10"
            android:id="@+id/et_account"/>

    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="密码:" />

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ems="10"
            android:id="@+id/et_password"
            android:inputType="textPassword"/>

    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="登录"
            android:id="@+id/but_login"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="取消" />

    </LinearLayout>
</LinearLayout>

5.3 sharedPreferences存储

5.3.1 将数据存入SharedPreferences中

第一步:获取sp对象,参数data表示文件名,MODE_PRIVATE表示文件操作模式

由于该对象本身只能获取数据,不能对数据进行存储和修改,因此需要调用SharePreferences类的edit()的方法获取可编辑的Editor对象。

editor.commit(); //提交修改

5.3.2 读取和删除数据

  • 获取数据的key值与存入数据的key值数据类型要一致,否则找不到指定数据

  • 保存SharedPreferences的key值是,使用静态变量保存,以免操作是写错,如private final String key= “itcast”。

5.3.3 实战演练—保存QQ账号和密码

使用SharedPreferences存储的方式保存数据

SPSaveQQ.java

package com.example.administrator.saveqq;

import android.content.Context;
import android.content.SharedPreferences;

import java.util.HashMap;
import java.util.Map;

public class SPSaveQQ {
    public static boolean saveUserInfo(Context context,String account,String password){
        SharedPreferences sp = context.getSharedPreferences("data",Context.MODE_PRIVATE);
        SharedPreferences.Editor editor= sp.edit();
        editor.putString("account",account);
        editor.putString("password",password);
        editor.commit();
        return true;
    }
    public  static Map<String,String> getUserInfo(Context context){
        SharedPreferences sp = context.getSharedPreferences("data",Context.MODE_PRIVATE);
        String account = sp.getString("account",null);
        String password = sp.getString("password",null);
        Map<String,String> map = new HashMap<>();
        map.put("account",account);
        map.put("password",password);
        return map;
    }
}

xml文件和5.2.3节相同,这里省略。

MainActivity.java

  • 这里面修改两句代码即可(调用的代码)分别是5.2.3节中的第27行代替26行,和60行代替59行即可。

  • 如果想要查看你存储的文件是否保存了数据,可以在以下位置找到。

    1. 运行你的项目

    2. 在右下角找到Device File Explorer文件(便于观察我将图片旋转了一下)

    3. 打开data文件中的data文件

    4. 在data文件中找到你的项目文件

    5. 上面两个分别就是上述两种存储方式生成的文件,可以右键,然后点击Save保存至桌面进行查看。

5.4 SQlite数据库存储

​ 前面介绍了如何使用文件进行存储和SharedPreferences存储数据,这两种方式适合存储简单数据,当你需要存储大量的数据的时候,显然不适合,为此Android提供了一个SQLite数据库,它可以存储应用程序中的大量数据,并对数据进行管理和维护。

5.4.1 SQLite数据库的创建

  • 只需要创建一个类继承SQLiteOpenHelper类,在该类中重写onCreate()方法和onUpgrade()方法即可
  • super()调用父类SQLiteOpenHelper,并传入4个参数,分别表示上下文对象,数据库名称,游标工厂(通常是null),数据库版本
  • onGrate()方法是在数据库第一次创建的时候调用的,此方法通常用于初始化结构
  • onUpgrade()方法在数据库版本号增加时调用,如果版本号不增加,此方法不调用。

扩展 SQLite Expert Personal可视化工具

1.在Android系统中,数据库创建完成后是无法直接对数据进行查看的,想要查看需要使用SQLite Expert Personal可视化工具可在官网(点击查看)中下载SQLite Expert Personal工具

2.接下来通过SQLite Expert Personal查看已将创建好的数据库文件,可在Device File Explorer视图中找到数据库文件所在的目录【data】–>【data】–>【项目包名全路径】–>【databases】

3.数据库文件是.db为扩展名,右键点击【Save As】将文件导入到指定目录下

4.在SQLite Expert Personal工具中点击【File】–>【Open Database】选项,选择你想要查看的数据库文件

5.4.2 SQLite数据库的基本操作

1.新增数据 (inset)

  • 通过getWritableDatabase()方法得到SQLiteDatabase对象,然后获取ContentValues对象中,最后调用insert()方法将数据插入到information表中
  • **inset()**方法接收3个参数,第一个参数是数据库表的名称,第二个参数是表示如果发现将要插入的行行为为空时,会将这个列名的值设为null,第三个参数为ContentValues对象
  • ContentValues类似于Map类,通过键值对元素的形式将数据存入,这里的key值表示插入数据库的列名,value表示要插入的数据
  • 最后一定要使用close()方法将它关闭,否则数据库连接会一直存在,不断消耗内存,当系统内存不足的时候获取不到SQLiteDatabase对象,并且会报出数据库未关闭异常。

2.删除数据(delete)

  • 删除操作不需要使用ContentValue来添加数,而是使用一个字符串和一个字符数组来添加参数名和参数值

3.修改数据(update)

  • update()方法中有四个参数,第一个参数表示数据库表名称,第二个参数是修改后的数据,第三个参数表示要修改的数据的查询条件,第四个参数是表示查询条件的参数

4.查询数据(query)

  • query方法中有7个参数,第一个参数表名称,第二个参数表示查询的列名,第三个参数表示接收查询条件的子句,第四个参数表示接收查询子句对应的条件值,第五个参数值表示分组方式,第六个阐释是接收having条件,即定义的过滤器,第七个参数表示排序的方式
  • getCount()方法获取查询到的结果总数
  • moveToNext()方法移动游标指向下一行数据
  • getString()方法传入列名获取对应的数据

iOS开发之数据存储

iOS开发之数据存储

Android数据存储文件存储SQLite数据库简单使用 sharedPreferences存储

删除以前的 sqlite 存储文件后重新创建持久性存储

预填充核心数据存储:使用 JSON 属性列表还是 XML 文件?

获取照片和视频文件路径并存储到 sqlite