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月前
|
安全 API Android开发
Android网络和数据交互: 解释Retrofit库的作用。
Android网络和数据交互: 解释Retrofit库的作用。
38 0
|
3月前
|
存储 安全 Android开发
Android数据存储:请解释ContentProvider是什么,它的主要作用是什么?
Android数据存储:请解释ContentProvider是什么,它的主要作用是什么?
25 0
|
3月前
|
存储 Java 数据库
Android数据存储:什么是Room Persistence Library?
Android数据存储:什么是Room Persistence Library?
42 0
|
3月前
|
存储 数据库连接 数据库
Android数据存储:解释SQLite数据库在Android中的使用。
Android数据存储:解释SQLite数据库在Android中的使用。
41 0
|
4月前
|
XML 物联网 API
Android Ble蓝牙App(五)数据操作
Android Ble蓝牙App(五)数据操作
|
8天前
|
Android开发 开发者
Android网络和数据交互: 请解释Android中的AsyncTask的作用。
Android's AsyncTask simplifies asynchronous tasks for brief background work, bridging UI and worker threads. It involves execute() for starting tasks, doInBackground() for background execution, publishProgress() for progress updates, and onPostExecute() for returning results to the main thread.
9 0
|
8天前
|
网络协议 安全 API
Android网络和数据交互: 什么是HTTP和HTTPS?在Android中如何进行网络请求?
HTTP和HTTPS是网络数据传输协议,HTTP基于TCP/IP,简单快速,HTTPS则是加密的HTTP,确保数据安全。在Android中,过去常用HttpURLConnection和HttpClient,但HttpClient自Android 6.0起被移除。现在推荐使用支持TLS、流式上传下载、超时配置等特性的HttpsURLConnection进行网络请求。
9 0
|
22天前
|
XML Java Android开发
Android每点击一次按钮就添加一条数据
Android每点击一次按钮就添加一条数据
23 1
|
1月前
|
存储 Android开发 C++
【Android 从入门到出门】第五章:使用DataStore存储数据和测试
【Android 从入门到出门】第五章:使用DataStore存储数据和测试
34 3
|
2月前
|
JavaScript Java 数据安全/隐私保护
安卓逆向 -- POST数据解密
安卓逆向 -- POST数据解密
25 2