Android 数据存储(一)-文件存储

简介: 一、数据存储概念Android系统提供了提供了多种保存应用数据的选项:文件存储:应用程序专属文件存储:内部存储(保存其他应用不应访问的敏感信息)共享文件存储:存储你的应用打算与其他应用共享的文件,包括媒体、文档和其他文件。Preferences:默认情况下,Preferences 使用 SharedPreferences 来保存值。数据库:使用 Room 持久性库将结构化数据存储在私有数据库中。 瞬时数据:是指那些存储在内存当中,有可能会因为程序关闭或其他原因导致内存被回收而丢失的数据。


一、数据存储概念


Android系统提供了提供了多种保存应用数据的选项:


  • 文件存储:
  • 应用程序专属文件存储:
  • 内部存储(保存其他应用不应访问的敏感信息)


  • 外部存储


  • 共享文件存储:存储你的应用打算与其他应用共享的文件,包括媒体、文档和其他文件。


  • Preferences:默认情况下,Preferences 使用 SharedPreferences 来保存值。


  • 数据库:使用 Room 持久性库将结构化数据存储在私有数据库中。


      瞬时数据:是指那些存储在内存当中,有可能会因为程序关闭或其他原因导致内存被回收而丢失的数据。


     数据持久化:是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失。持久化技术则提供了一种机制可以让数据在顺势状态和持久状态之间进行转换。

微信图片_20220525130330.png


二、应用程序专属文件存储


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


访问方法:


  • 内部存储(Internal Storage),


  • getFilesDir()


  • getCacheDir()


  • 外部存储(External Storage)


  • getExternalFilesDir()


  • getExternalCacheDir()


注意:卸载应用时,所以目录下的文件将被移除。并且其他应用无法访问这些专属文件。


所需权限:


  • 内部存储永远不需要


  • 当你的应用在运行 Android 4.4(API 级别 >=19)时,外部存储不需要(目前应用兼容基本5.0起步了)。


2.1 访问持久文件


       你可以使用Context对象的 filesDir 属性访问该目录。


        //From internal storage, getFilesDir() or getCacheDir()
        File filesDir = getFilesDir();//持久文件目录
        //FilesDir:/data/user/0/com.scc.datastorage/files
        Log.e("File","FilesDir:"+filesDir.getAbsolutePath());
        File cacheDir = getCacheDir();//缓存文件目录
        //CacheDir:/data/user/0/com.scc.datastorage/cache
        Log.e("File","CacheDir:"+cacheDir.getAbsolutePath());


 com.scc.datastorage:代表你的包名。


注意:为帮助保持应用程序的性能,请勿多次打开和关闭同一个文件。


2.2 将数据存储到文件


       Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文本中。


这个方法接受两个参数:


  • 第一个参数是文件名,在文本创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路径(因为默认存储到/data/data//files/目录下)。


  • 第二个参数是文件的操作模式,主要有两种:


  • MODE_PRIVATE:默认的操作模式,表示当指定同样文件名的时候,当该文件名有内容时,再次调用会覆盖原内容。


  • MODE_APPEND:表示该文件如果已存在就往文件里面追加内容。


       调用 openFileOutput() 来获取 FileOutputStream,得到这个对象后就可以使用Java流的方式将数据写入到文件中了。


    public void write(){
        //文件名
        String filename = "sccFile";
        //写入内容(shuaici)
        String fileContents = fileStorageBinding.etInput.getText().toString();
        //内容不能为空
        if (fileContents.isEmpty()) {
            Log.e("File","FileContents.isEmpty()");
            return;
        }
        BufferedWriter writer = null;
        try  {
            //获取FileOutputStream对象
            FileOutputStream fos = openFileOutput(filename, Context.MODE_PRIVATE);
            //通过OutputStreamWriter构建出一个BufferedWriter对象
            writer = new BufferedWriter(new OutputStreamWriter(fos));
            //通过BufferedWriter对象将文本内容写入到文件中
            writer.write(fileContents);
        }catch (IOException e){
            Log.e("File",e.getMessage());
        }finally {
            try {
                //不管是否抛异常,都手动关闭输入流
                if(writer != null) writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


微信图片_20220525130732.png微信图片_20220525130735.png


如果你想在同一个文件写入多个内容那么需要将第二个参数改为MODE_APPEND


out = openFileOutput("data", Context.MODE_PRIVATE);


存储了3次内容(ANDROID、shuaici、123456)如下:


微信图片_20220525130815.png


2.3 从文件中读取数据


    //读取文件
    public void read(){
        Log.e("File","read.start");
        String filename = "sccFile";
        BufferedReader reader = null;
        try  {
            //获取FileInputStream对象
            FileInputStream fis = openFileInput(filename);
            //通过InputStreamReader构建出一个BufferedReader对象
            reader = new BufferedReader(new InputStreamReader(fis));
            StringBuilder sb = new StringBuilder();
            String line = "";
            //一行一行的读取,当数据为空时结束循环
            while ((line=reader.readLine())!=null){
                sb.append(line);//将数据添加到StringBuilder
            }
            Log.e("File",sb.toString());
            fileStorageBinding.etInput.setText(sb.toString());
        }catch (IOException e){
            Log.e("File",e.getMessage());
        }finally {
            Log.e("File","read.finally");
            try {
                if(reader != null) reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

微信图片_20220525130847.png


注意:如果你需要在安装时以流的形式访问文件,请将文件保存在项目的 /res/raw 目录中。 你可以使用 openRawResource() 打开这些文件,传入以 R.raw 为前缀的文件名作为资源 ID。此方法返回一个可用于读取文件的 InputStream。你无法写入原始文件


2.4 查看文件列表


微信图片_20220525131002.png


你可以通过调用 fileList() 获取包含 filesDir 目录中所有文件名称的数组,如下:


    //查看文件列表
    public void filelist(){
        String [] files = fileList();
        for (String file : files) {
            Log.e("File","FileList:"+file);
        }
    }


微信图片_20220525131033.png


2.5 删除文件


  • 删除文件最简单的方法是让打开的文件引用本身调用delete()。
  • 如果文件保存在内部存储中,你还可以通过调用deleteFile()删除文件。


微信图片_20220525131054.png


    //删除文件
    public void delete(){
        String filename = fileStorageBinding.etInput.getText().toString();
        Log.e("File","Delete:"+deleteFile(filename));
        filelist();
    }


微信图片_20220525131118.png


注意:当用户卸载你的应用程序时,Android系统会删除保存在内部存储和外部存储中的所有文件。 当然你也可以定期删除你所创建的不需要的缓存文件。


三、缓存文件(cache目录下)


3.1 创建缓存文件


       如果你只需要临时存储敏感数据,你应该使用应用程序在内部存储中指定的缓存目录来保存数据。与files目录下存储一样,存储在此目录中的文件会在用户卸载你的应用程序时被删除


注意:

此缓存目录旨在存储你应用的少量敏感数据。

当设备内部存储空间不足时,Android 可能会删除这些缓存文件以恢复空间。因此,在读取缓存文件之前检查它们是否存在。


    //创建缓存文件
    private void createCache() {
        try {
            //方法一
            File timpfile = File.createTempFile("scc2022", ".txt", this.getCacheDir());
            Log.e("File","是否存在:"+timpfile.exists());
            Log.e("File","timpfile:"+timpfile.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        //方法二:创建文件失败
        File file = new File(this.getCacheDir(), "file2022");
        Log.e("File","是否存在:"+file.exists());
        Log.e("File","file:"+file.getAbsolutePath());
    }

微信图片_20220525131239.png微信图片_20220525131232.png


  方法一和方法二都会在应用程序缓存目录中添加文件,但是这里我用方法二创建文件始终不成功,方法二后面找找原因。这里重点介绍方法一。


  • 第一个参数prefix:文件名前缀


  • 第二个参数suffix:文件名后缀(如果不设置,默认.tmp)


  • 第三个参数directory:文件存放目录


       如上图不管是否指定后缀都会在文件名末尾添加一个随机数,以保持文件的唯一性。如上面文件名设置的为:scc2022.txt,但是创建出来的是:scc2022784042118208981946.txt。


       由于末尾添加一个随机数给删除或读取文件带来麻烦,所以可能需要调用file.getName()进行文件名存储。否则不好找。如此下来其实我更喜欢第二种方式,但是不知道什么情况创建不成功这就比较尴尬了。


3.2 删除文件


       尽管 Android 系统有时会自行删除缓存文件,但你可以自己删除数据。


微信图片_20220525131343.png


    //删除缓存文件
    private void deleteCache() {
        //scc20221181329566644563700891.tmp和scc2022118都存在
        //但是cache目录下没有,咱们调用删除试试
        File cacheFile = new File(getCacheDir(), "scc20221181329566644563700891.tmp");
        //判断文件是否存在
        if(cacheFile.exists())
        {
            //使用File对象的delete()方法
            Log.e("File","delete17:"+cacheFile.delete());
        }
        File cacheFile2 = new File(getCacheDir(), "scc2022118");
        if(cacheFile2.exists())
        {
            //应用Context的deleteFile()方法
            Log.e("File","deleteFile19:"+deleteFile("scc2022118"));
        }
    }

微信图片_20220525131405.png


四、外部存储


如果内部存储太小无法提供更大的空间那么可以使用外部存储


  • 外部存储(External Storage)
  • getExternalFilesDir()//持久文件


  • getExternalCacheDir()//缓存文件


       在 Android 4.4(API 级别 >=19),你的应用无需请求任何与存储相关的权限即可访问外部存储中的应用特定目录。当你的应用程序被卸载时,存储在这些目录中的文件将被删除。


注意:

1、不能保证文件可以访问,因为可能SD卡被移除。

2、在 Android 11(API 级别 >=30),应用无法在外部存储上创建自己的应用特定目录。感觉外部存储还是少用。


验证存储是否可用


// 检查外部存储是否可用于读写。
private boolean isExternalStorageWritable() {
    return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}
// 检查外部存储至少可读。
private boolean isExternalStorageReadable() {
     return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ||
            Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
}


五、共享文件存储


以前写过一篇,感兴趣的可以去看看:


https://shuaici.blog.csdn.net/article/details/116942978

https://shuaici.blog.csdn.net/article/details/116942978


项目地址


Android 数据全方案处理


相关文章
|
3月前
|
开发工具 Android开发 开发者
Android平台如何不推RTMP|不发布RTSP流|不实时录像|不回传GB28181数据时实时快照?
本文介绍了一种在Android平台上实现实时截图快照的方法,尤其适用于无需依赖系统接口的情况,如在RTMP推送、RTSP服务或GB28181设备接入等场景下进行截图。通过底层模块(libSmartPublisher.so)实现了截图功能,封装了`SnapShotImpl.java`类来管理截图流程。此外,提供了关键代码片段展示初始化SDK实例、执行截图、以及在Activity销毁时释放资源的过程。此方案还考虑到了快照数据的灵活处理需求,符合GB/T28181-2022的技术规范。对于寻求更灵活快照机制的开发者来说,这是一个值得参考的设计思路。
|
5月前
|
XML 存储 JSON
51. 【Android教程】JSON 数据解析
51. 【Android教程】JSON 数据解析
152 2
|
6月前
|
数据库 Android开发
Android 通过升级SettingsProvider数据强制覆盖用户的设置项
Android 通过升级SettingsProvider数据强制覆盖用户的设置项 【5月更文挑战第7天】
158 5
|
6月前
|
JSON Android开发 数据格式
android与Web服务器交互时的cookie使用-兼谈大众点评数据获得(原创)
android与Web服务器交互时的cookie使用-兼谈大众点评数据获得(原创)
85 2
|
1月前
|
存储 大数据 数据库
Android经典面试题之Intent传递数据大小为什么限制是1M?
在 Android 中,使用 Intent 传递数据时存在约 1MB 的大小限制,这是由于 Binder 机制的事务缓冲区限制、Intent 的设计初衷以及内存消耗和性能问题所致。推荐使用文件存储、SharedPreferences、数据库存储或 ContentProvider 等方式传递大数据。
62 0
|
3月前
|
JSON Java Android开发
Android 开发者必备秘籍:轻松攻克 JSON 格式数据解析难题,让你的应用更出色!
【8月更文挑战第18天】在Android开发中,解析JSON数据至关重要。JSON以其简洁和易读成为首选的数据交换格式。开发者可通过多种途径解析JSON,如使用内置的`JSONObject`和`JSONArray`类直接操作数据,或借助Google提供的Gson库将JSON自动映射为Java对象。无论哪种方法,正确解析JSON都是实现高效应用的关键,能帮助开发者处理网络请求返回的数据,并将其展示给用户,从而提升应用的功能性和用户体验。
89 1
|
3月前
|
缓存 API Android开发
Android经典实战之Kotlin Flow中的3个数据相关的操作符:debounce、buffer和conflate
本文介绍了Kotlin中`Flow`的`debounce`、`buffer`及`conflate`三个操作符。`debounce`过滤快速连续数据,仅保留指定时间内的最后一个;`buffer`引入缓存减轻背压;`conflate`仅保留最新数据。通过示例展示了如何在搜索输入和数据流处理中应用这些操作符以提高程序效率和用户体验。
51 6
|
3月前
|
编解码 网络协议 前端开发
如何实现Android平台GB28181设备接入模块按需打开摄像头并回传数据
后台采集摄像头,如果想再进一步扩展,可以把android平台gb28181的camera2 demo,都移植过来,实现功能更强大的国标设备侧,这里主要是展示,收到国标平台侧的回传请求后,才打开摄像头,才开始编码打包,最大限度的减少资源的占用
|
3月前
|
编解码 网络协议 Android开发
Android平台GB28181设备接入模块实现后台service按需回传摄像头数据到国标平台侧
我们在做Android平台GB28181设备对接模块的时候,遇到这样的技术需求,开发者希望能以后台服务的形式运行程序,国标平台侧没有视频回传请求的时候,仅保持信令链接,有发起视频回传请求或语音广播时,打开摄像头,并实时回传音视频数据或接收处理国标平台侧发过来的语音广播数据。
|
3月前
|
算法 数据处理 开发工具
Android平台RTSP|RTMP播放器如何回调YUV或RGB数据
在开发Android平台上的RTSP或RTMP播放器时,开发者不仅追求低延迟播放,还希望获取解码后的视频数据(如YUV或RGB格式),以便进行视觉算法分析。使用大牛直播SDK中的SmartPlayer,可在确保播放流畅的同时,通过设置外部渲染器(`SmartPlayerSetExternalRender`)来高效地回调原始视频数据。例如,对于RGBA数据,需实现`NTExternalRender`接口,并重写相关方法以处理数据和尺寸变化。同样地,对于I420(YUV)数据,也需要相应地实现接口以满足需求。这种方式使得开发者能在不影响常规播放功能的情况下,进行定制化的视频处理任务。