文件存储

  • 前言
  • 文件存储
    • 内存
    • 内部存储
    • 外部存储
    • 内部存储操作
      • API
      • 读写操作
    • 外部存储操作
      • 公共目录
      • 私有目录
        • 私有文件
        • 私有缓存
    • 文件各种操作封装

前言

众所周知,数据存储在每个应用中都会用到,那所用到的技术应该怎么选呢,这里Android给开发者提供了几种方法去保存常用应用数据,至于你想选择哪一种方式,取决于你的特定需求;例如这个数据是本应用私有的还是跟其它应用共享以及数据存储所需的内存空间等

  • Shared Preferences:这种方式通常用来保存私有原始数据,以键值对形式存储;这也就意味着这些数据只能由本应用访问
  • Internal Storage:这种方式是将私有数据保存在内部存储(设备内存)中,实际上是使用文件流进行读写
  • External Storage:这种方式是将公共数据存储在共享外部存储上;也就是将数据存储在SD卡上,存储在这上面说明数据是开放的,其它应用可以直接访问;这跟上面一种都是平常所说的文件存储
  • SQLite Databases:这种方式是将结构化数据存储在私有数据库中;这也就是常说的数据库存储,使用SQLite保存数据,这些数据是私有的
  • Network Connection:这种方式是将数据存储在Web服务器上,也就是通常所说的网络存储

笔者上一篇文章讲述了Shared Preferences的工作原理及使用封装,这篇文章来掰掰第二种和第三种方式

文件存储

本文所含代码随时更新,可从这里下载最新代码
传送门Mango

说到文件存储,就必须说下Android世界中的文件系统了

大家使用Android手机的时候,多多少少都去过设置->应用界面,选择一个应用打开,可以看到上面有清除数据,清除缓存两个按钮;有的手机可能做的页面不一样了,只有一个清除数据按钮,但是还是能看到列举出来的类型;那你知道这两个按钮是清除的哪里的数据吗?

我们经常听到内存,内部存储,外部存储这几个概念,那它们分别表示什么呢?我们开发者存储数据的时候应该把数据存储到什么地方呢?

内存

内存指的是手机在运行应用程序时需要的存储空间,也称为RAM,即运行内存,这个值基本上在手机出厂后就确定了;当你买手机的时候,销售员跟你说这个手机配备8G内存+128G存储等配置时,这个8G指的就是RAM,也就是应用运行时理论上能利用到的最大内存了;这个有点类似于电脑上的内存条,只不过电脑内存条可以后期增加,理论上这个值越大,基本上手机运行的就越流畅

内部存储

英文称为Internal Storage,我们可以将文件直接保存在设备的内部存储中。 默认情况下,保存到内部存储的文件对应用程序是私有的,而其他应用程序无法访问它们(用户也无法访问,除非root),使用内部存储不需要额外的权限; 当用户卸载应用程序时,将删除这些文件;从技术上来讲如果你在创建内部存储文件的时候将文件属性设置成可读,其他app能够访问自己应用的数据,前提是他知道你这个应用的包名,如果一个文件的属性是私有(private),那么即使知道包名其他应用也无法访问。 内部存储空间十分有限,因而显得可贵,另外,它也是系统本身和系统应用程序主要的数据存储所在地,一旦内部存储空间耗尽,手机也就无法使用了。所以对于内部存储空间,我们要尽量避免使用

我们开发中经常说某些文件在data目录下,这个data文件夹就是我们常说的内部存储,里面有两个比较重要的子文件夹

  • app文件夹:这里存放应用的apk文件
  • data文件夹:这里有很多以应用包名命名的文件夹,存放着应用数据

打开其中一个应用文件夹,通常有如下文件

data/data/包名/shared_prefs //该目录下存放很多SharedPreferences数据,都是一些xml文件
data/data/包名/databases //该目录下存放db格式的文件,也就是数据库数据
data/data/包名/files //该目录下存放普通数据
data/data/包名/cache //该目录下存放缓存文件

外部存储

英文称为External Storage,每个Android兼容设备都支持可用于保存文件的共享“外部存储”,它可能是可移除的存储介质(典型如SD卡),也可能是不可移除的存储介质(如现在很多一体机内置的存储器);外部存储是相对于内部存储而言的,不过存储在这上面的文件是所有者可见的,所有人都有权限操作,不过前提是需要申请权限

要在外部存储上读取或写入文件,您的应用必须获取READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE系统权限;如果您需要同时读取和写入文件,则只需要请求WRITE_EXTERNAL_STORAGE权限,因为它也隐式地要求读取权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

注意:android6.0以后引入了运行时权限的概念,要注意只在AndroidManifest.xml申请是不够的
注意:从Android 4.4(api19)开始,如果您在外部存储上只读取或写入应用程序专用的文件(即在私有目录读写),则不需要这些权限

外部存储的最外层目录是storage文件夹,也可能是mnt文件夹,这个根据厂家不同有不同的结果
一般来说,在storage文件夹中有一个sdcard文件夹,这个文件夹中的文件又分为两类,一类是公共目录,还有一类是私有目录

  • 公共目录:有九大类,比如DCIM、Download、Music、Movies、Pictures、Ringtones等这种系统为我们创建的文件夹;这些目录里的文件所有应用可以分享
  • 私有目录:就是Android这个文件夹,这个文件夹打开之后里边有一个data文件夹,打开这个data文件夹,里边有许多包名组成的文件夹,这个文件夹存放了应用私有数据

私有目录如下

storage/sdcard/Android/data/包名/files
storage/sdcard/Android/data/包名/cache

经过上面的分析,应该对这些有一个大致的了解了,RAM内存是我们开发的过程中需要注意的,不要内存泄漏,不要无限new内存,因为那样会使得手机运行内存紧张,运行卡顿;通常来说我们很少去操作内部存储空间,因为没有root权限,我们动不了这块,内部存储通常是由系统来维护的,不过在代码中Google还是给了我们API访问这些文件夹

通常情况下内部存储空间都很有限,在开发中我们操作的最多的还是外部存储,Google官方也建议开发者应该将数据存储在外部存储的私有目录中该APP包名命名的文件夹下,这样某些情况下其它应用是无法对你的应用文件进行写操作;同时当应用被卸载后,相关数据也会被一起清除掉,同时也不会引起用户的反感;如果我们把数据存储在公有目录和storage/sdcard目录下,数据是不会随着应用的卸载而删除的


内部存储操作

API

操作内部存储的api都是Context类的

  • getFilesDir():返回文件系统上特定应用程序的文件目录的绝对路径;返回一个File对象,它的目录是 data/data/包名/files
  • fileList():返回应用程序当前保存的文件数组;返回一个字符串数组,即由data/data/包名/files目录下文件的文件名组成的
  • deleteFile(String name):删除保存在内部存储上的文件;该文件位于data/data/包名/files目录下,返回一个boolean值表示是否删除成功
  • getCacheDir():返回文件系统上应用程序的缓存目录的绝对路径;返回一个File对象,它的目录是 data/data/包名/cache
  • getDir(String name, int mode):在内部存储空间中创建(或打开现有)目录;返回一个File对象;请注意,通过File对象创建的文件只能由您自己的应用程序访问; 您只能设置整个目录的模式,而不能设置单个文件的模式;该文件夹是在data/data/包名 目录上创建的
  • openFileOutput(String name, int mode):打开内部存储中与本应用程序包关联的私有文件以进行写入,如果文件尚不存在,则创建该文件;返回一个输出流FileOutputStream;该文件位于data/data/包名/files目录下
  • openFileInput(String name):打开内部存储中与本应用程序包关联的私有文件以进行读取;返回一个输入流FileInputStream,该文件位于data/data/包名/files目录下

其中openFileOutput方法第二个参数有如下几个可选值:

  • Context.MODE_PRIVATE:默认模式,创建文件(或替换同名文件),只能由调用程序(或共享相同用户ID的所有应用程序)访问
  • Context.MODE_WORLD_READABLE:允许所有其他应用程序对创建的文件具有读的访问权限,使用这个模式Android N(7.0)开始将抛出SecurityException,这个模式从API17已标记被弃用,创建全局可读文件非常危险,可能引发安全漏洞
  • Context.MODE_WORLD_WRITEABLE:允许所有其他应用程序具有对创建文件的写访问权,,使用这个模式Android N(7.0)开始将抛出SecurityException,这个模式从API17已标记被弃用,创建全局可写文件非常危险,可能引发安全漏洞
  • Context.MODE_APPEND:创建文件,如果文件已存在,则将数据写入现有文件的末尾而不是抹掉它。

注意:如果您想缓存某些数据,而不是持久存储它们,则应使用getCacheDir()打开一个File,该File表示应用程序应保存临时缓存文件的内部目录;当设备内部存储空间不足时,Android可能会删除这些缓存文件以恢复空间。 但是,您不应该依赖系统来清理这些文件。 您应该始终自己维护缓存文件并保持合理的空间限制,例如1MB。 当用户卸载您的应用程序时,将删除这些文件

读写操作

/*** 获取文件内容* @param fileName 内部存储中文件名* @return 按行读取文件内容*/public List<String> getStringFromInternalStorage(String fileName){List<String> content = new ArrayList<>();InputStream is = null;BufferedReader br = null;try {is = mContext.get().openFileInput(fileName);br = new BufferedReader(new InputStreamReader(is,"UTF-8"));String line ;while ( (line = br.readLine()) != null){content.add(line);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeInputStream(is);if (br != null) {try {br.close();} catch (IOException e) {e.printStackTrace();}}}return content;}/*** 获取内部存储文件数据* @param fileName 内部存储中文件名* @return 返回文件二进制数据,以便传输*/public byte[] getDataFromInternalStorage(String fileName){BufferedInputStream bis = null;FileInputStream fis = null;ByteArrayOutputStream bos = null;try {fis = mContext.get().openFileInput(fileName);bis = new BufferedInputStream(fis);bos = new ByteArrayOutputStream();byte[] buff = new byte[8*1024];int len ;while ((len = bis.read(buff)) != -1){bos.write(buff,0,len);}return bos.toByteArray();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeInputStream(fis);closeInputStream(bis);closeOutputStream(bos);}return null;}/*** 保存字符串数据到内部存储* @param content 保存的内容* @param fileName 文件名* @param mode 访问模式*/public void putStringToInternalStorage(String content,String fileName,int mode){FileOutputStream fos = null;try {fos = mContext.get().openFileOutput(fileName,mode);fos.write(content.getBytes());fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeOutputStream(fos);}}/*** 保存数据到内部存储* @param content 保存的内容* @param fileName 文件名* @param mode 访问模式*/public void putDataToInternalStorage(byte[] content,String fileName,int mode){FileOutputStream fos = null;try {fos = mContext.get().openFileOutput(fileName,mode);fos.write(content);fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeOutputStream(fos);}}

外部存储操作

做外部存储操作之前一定要判断外部存储状态

/*** 判断sd卡是否处于就绪状态 可读可写* MEDIA_UNKNOWN:未知状态* MEDIA_REMOVED:移除状态(外部存储不存在)* MEDIA_UNMOUNTED:未装载状态(外部存储存在但是没有装载)* MEDIA_CHECKING:磁盘检测状态* MEDIA_NOFS:外部存储存在,但是磁盘为空或使用了不支持的文件系统* MEDIA_MOUNTED:就绪状态(可读、可写)* MEDIA_MOUNTED_READ_ONLY:只读状态* MEDIA_SHARED:共享状态(外部存储存在且正通过USB共享数据)* MEDIA_BAD_REMOVAL:异常移除状态(外部存储还没有正确卸载就被移除了)* MEDIA_UNMOUNTABLE:不可装载状态(外部存储存在但是无法被装载,一般是磁盘的文件系统损坏造成的)* @return*/public boolean isSdCardMount(){return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);}

上面说到外部存储的时候讲到过这里有公共目录和私有目录之分

公共目录

通常情况下,用户通过我们的应用获取的比如多媒体文件,像图片、视频、铃声等文件,应该放在这些公共目录中,例如Music /,Pictures /和Ringtones /;将文件保存到相应的媒体类型目录,系统的媒体扫描程序可以正确地对系统中的文件进行分类(例如,铃声在系统设置中显示为铃声,而不是音乐);同时用户可以轻松地从设备找到并复制它们,也能更好的与其它应用分享;即使应用被卸载,这些文件依然保留

如果要将文件存储在相应的公共目录,可以通过调用getExternalStoragePublicDirectory(String type),向其传递所需的目录类型,例如DIRECTORY_MUSIC,DIRECTORY_PICTURES,DIRECTORY_RINGTONES或其他,参数不可为null,返回一个File对象,如果不存在,即创建

/*** 获取公共目录* @param type DIRECTORY_MUSIC 音乐类型*             DIRECTORY_RINGTONES 铃声类型*             DIRECTORY_PODCASTS 播客音频类型*             DIRECTORY_ALARMS 闹钟提示音类型*             DIRECTORY_NOTIFICATIONS 通知提示音类型*             DIRECTORY_PICTURES 图片类型*             DIRECTORY_MOVIES 电影类型*             DIRECTORY_DOWNLOADS 下载文件类型*             DIRECTORY_DCIM 相机照片类型*             DIRECTORY_DOCUMENTS 文档类型* @return 相应类型目录文件*/public File getExternalStoragePublicDirectory(String type){File file = Environment.getExternalStoragePublicDirectory(type);if (!file.exists()) {file.mkdir();}return file;}

获取到的目录类似于/storage/sdcard0/Music

假如不想存放在这些目录里,需要存放在一些自定义的目录中,那就通过Environment.getExternalStorageDirectory()获取外部存储的根目录,通常是SD卡的根目录,比如/storage/sdcard0,然后你就可以在这个目录上新建目录


私有目录

私有文件

如果您正在处理不适合其他应用程序使用的文件(例如仅由您的应用程序使用的文件),则应通过调用getExternalFilesDir()在外部存储上使用专用存储目录。 此方法还接收类型参数来指定子目录的类型(例如DIRECTORY_MOVIES),如果目录不存在Android会创建。 如果您不需要特定的媒体目录,请传递null以接收应用程序私有文件目录的根目录(/storage/sdcard0/Android/data/包名/files/)。

这在作用上有点类似于内部存储中的getFilesDir()方法,同时都是属于Context的API

/*** 获取私有指定类型目录* @param type DIRECTORY_MUSIC 音乐类型*             DIRECTORY_PODCASTS 播客音频类型*             DIRECTORY_RINGTONES 铃声类型*             DIRECTORY_ALARMS 闹钟提示音类型*             DIRECTORY_NOTIFICATIONS 通知提示音类型*             DIRECTORY_PICTURES 图片类型*             DIRECTORY_MOVIES 电影类型* @return 相应类型目录文件 例如/storage/sdcard0/Android/data/com.mango.datasave/files/Music*/public File getExternalStoragePrivateDirectory(String type){File file = mContext.get().getExternalFilesDir(type);return file;}

注意:某些移动设备可能既提供了内置存储器作为外部存储空间(通常是手机自带的),同时又提供了SD卡作为外部存储空间。也就是说,在这些设备中外部存储实际上包含了两块磁盘。在Android 4.3(API 18)及以下,Context的getExternalFilesDir方法仅仅会返回内置存储器对应的外部存储空间,而无法访问SD卡对应的存储空间。从Android 4.4(API 19)开始,Context新增了getExternalFilesDirs方法。这个方法的返回值是一个File数组,包含两个对象(可能为null),这样就可以实现对内置存储器和SD卡的访问。数组的第一个对象默认是内置存储器,官方的开发建议是除非这个位置已满或不可用,否则应该使用这个位置

私有缓存

同内部存储一样,外部存储也有保存缓存文件的目录,可以通过Context的getExternalCacheDir方法访问缓存文件目录,返回值是一个File对象,对应的目录是 /storage/sdcard0/Android/data/com.mango.datasave/cache ,其中com.mango.datasave是我的测试应用包名;如果目录不存在Android会创建

如上面的注意点,外部存储可能同时包含内置存储器和SD卡两个存储空间,因此在Android 4.4(API 19)及以上还可以通过Context的getExternalCacheDirs方法访问这两个存储空间。这个方法会返回一个File数组,包含两个对象,第一个对象默认是外部存储内置存储器的缓存文件目录

File getExternalCacheDir()
File[] getExternalCacheDirs()

注意:当用户卸载您的应用程序时,将删除 /storage/sdcard0/Android/data/包名 目录及其所有内容。 此外,系统扫描程序不读取这些目录中的文件,因此无法从MediaStore内容提供程序访问它们。 因此,您不应将这些目录用于存放属于用户的媒体,例如使用您的应用程序捕获或编辑的照片,或用户使用您的应用程序购买的音乐 - 这些文件应保存在公共目录中

文件各种操作封装

/*** @Description TODO(文件操作辅助类)* @author cxy* @Date 2018/10/30 16:26*/
public class FileStorageTools {private String TAG = FileStorageTools.class.getSimpleName();private WeakReference<Context> mContext ;private static FileStorageTools instance;private FileStorageTools(Context context){mContext = new WeakReference<>(context);}public static FileStorageTools getInstance(Context context){if(instance == null){instance = new FileStorageTools(context);}return instance;}//获取ram可用内存public String getRAMAvailMem(){ActivityManager am=(ActivityManager)mContext.get().getSystemService(Context.ACTIVITY_SERVICE);ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();am.getMemoryInfo(mi);return reviseFileSize(mi.availMem);}//获取ram总内存public String getRAMTotalMem(){ActivityManager am=(ActivityManager)mContext.get().getSystemService(Context.ACTIVITY_SERVICE);ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();am.getMemoryInfo(mi);return reviseFileSize(mi.totalMem);}//获取sd卡总大小public String getSDTotalSize(){if(isSdCardMount()){File file=Environment.getExternalStorageDirectory();StatFs statFs=new StatFs(file.getPath());long blockSize=statFs.getBlockSizeLong();long totalBlocks=statFs.getBlockCountLong();return reviseFileSize(totalBlocks*blockSize);}else {return null;}}//获取sd卡可用大小public String getSDAvailableSize(){if(isSdCardMount()){File file=Environment.getExternalStorageDirectory();StatFs statFs=new StatFs(file.getPath());long blockSize=statFs.getBlockSizeLong();long availableBlocks=statFs.getFreeBlocksLong();return reviseFileSize(availableBlocks*blockSize);}else {return null;}}/**================================================内部存储操作=================================================================**//*** 获取文件内容* @param fileName 内部存储中文件名* @return 按行读取文件内容*/public List<String> getStringFromInternalStorage(String fileName){List<String> content = new ArrayList<>();InputStream is = null;BufferedReader br = null;try {is = mContext.get().openFileInput(fileName);br = new BufferedReader(new InputStreamReader(is,"UTF-8"));String line ;while ( (line = br.readLine()) != null){content.add(line);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeInputStream(is);if (br != null) {try {br.close();} catch (IOException e) {e.printStackTrace();}}}return content;}/*** 获取内部存储文件数据* @param fileName 内部存储中文件名* @return 返回文件二进制数据,以便传输*/public byte[] getDataFromInternalStorage(String fileName){BufferedInputStream bis = null;FileInputStream fis = null;ByteArrayOutputStream bos = null;try {fis = mContext.get().openFileInput(fileName);bis = new BufferedInputStream(fis);bos = new ByteArrayOutputStream();byte[] buff = new byte[8*1024];int len ;while ((len = bis.read(buff)) != -1){bos.write(buff,0,len);}return bos.toByteArray();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeInputStream(fis,bis);closeOutputStream(bos);}return null;}/*** 保存字符串数据到内部存储* @param content 保存的内容* @param fileName 文件名* @param mode 访问模式*/public void putStringToInternalStorage(String content,String fileName,int mode){FileOutputStream fos = null;try {fos = mContext.get().openFileOutput(fileName,mode);fos.write(content.getBytes());fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeOutputStream(fos);}}/*** 保存数据到内部存储* @param content 保存的内容* @param fileName 文件名* @param mode 访问模式*/public void putDataToInternalStorage(byte[] content,String fileName,int mode){FileOutputStream fos = null;try {fos = mContext.get().openFileOutput(fileName,mode);fos.write(content);fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeOutputStream(fos);}}/*** 将内容保存到内部存储的缓存目录 尽量别将文件保存在这里,内存有限* @param content* @param fileName* @param append 是否追加到文件尾部*/public void putInternalStorageCache(String content,String fileName,boolean append){FileOutputStream fos = null;try {File cache = new File(mContext.get().getCacheDir(),fileName);fos = new FileOutputStream(cache,append);fos.write(content.getBytes());fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeOutputStream(fos);}}/**====================================================外部存储操作=======================================================**//**====================================构建目录=================================================**//*** 获取公共目录* @param type DIRECTORY_MUSIC 音乐类型*             DIRECTORY_RINGTONES 铃声类型*             DIRECTORY_PODCASTS 播客音频类型*             DIRECTORY_ALARMS 闹钟提示音类型*             DIRECTORY_NOTIFICATIONS 通知提示音类型*             DIRECTORY_PICTURES 图片类型*             DIRECTORY_MOVIES 电影类型*             DIRECTORY_DOWNLOADS 下载文件类型*             DIRECTORY_DCIM 相机照片类型*             DIRECTORY_DOCUMENTS 文档类型* @return 相应类型目录文件 例如/storage/sdcard0/Music*/public File getExternalStoragePublicDirectory(String type){File file = Environment.getExternalStoragePublicDirectory(type);if (!file.exists()) {file.mkdir();}return file;}/*** 获取私有文件目录* @param type DIRECTORY_MUSIC 音乐类型*             DIRECTORY_PODCASTS 播客音频类型*             DIRECTORY_RINGTONES 铃声类型*             DIRECTORY_ALARMS 闹钟提示音类型*             DIRECTORY_NOTIFICATIONS 通知提示音类型*             DIRECTORY_PICTURES 图片类型*             DIRECTORY_MOVIES 电影类型* @return 相应类型目录文件 例如/storage/sdcard0/Android/data/com.mango.datasave/files/Music*/public File getExternalStoragePrivateDirectory(String type){File file = mContext.get().getExternalFilesDir(type);return file;}/*** 获取私有缓存目录* @return /storage/sdcard0/Android/data/com.mango.datasave/cache*/public File getExternalStoragePrivateCache(){File file = mContext.get().getExternalCacheDir();return file;}/*** 构建文件目录* @param path 例如 /file/movie* @return 返回完整目录 /storage/sdcard0/file/movie*/public String makeFilePath(String path){if (StringTools.isEmpty(path)) throw new NullPointerException("path cant be null");return Environment.getExternalStorageDirectory().getAbsolutePath() + path;}/*** 创建文件* @param base* @param fileName* @return*/public File makeFile(File base,String fileName){if (StringTools.isEmpty(fileName)) throw new NullPointerException("fileName cant be null");if(fileName.indexOf(File.separator) < 0){return new File(base,fileName);}throw new IllegalArgumentException("File " + fileName + " contains a path separator");}/**====================================保存数据=================================================**//*** 将内容写到外部存储文件* @param content 内容* @param parent 目标文件父目录* @param fileName 文件名* @param append 内容是追加到文件末尾还是覆盖*/public void putStringToExternalStorage(String content,File parent, String fileName,boolean append){FileOutputStream fos = null;File file = makeFile(parent,fileName);try {fos = new FileOutputStream(file,append);fos.write(content.getBytes());fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeOutputStream(fos);}}/*** 将内容写到外部存储文件* @param parent 目标文件父目录* @param fileName 文件名* @param content 内容*                @String.getBytes()*/public void putDataToExternalStorage(File parent, String fileName,byte[] content){FileOutputStream fos = null;try {File file = makeFile(parent,fileName);fos = new FileOutputStream(file);fos.write(content);fos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeOutputStream(fos);}}/*** 将流数据保存到外部存储文件* @param parent 目标文件父目录* @param fileName 文件名* @param is 流*/public void putStreamToExternalStorage(File parent, String fileName,InputStream is){BufferedOutputStream bos = null;BufferedInputStream bis = null;try {File file = makeFile(parent,fileName);bis = new BufferedInputStream(is);bos = new BufferedOutputStream(new FileOutputStream(file));byte[] buff = new byte[8*1024];int len;while ( (len = bis.read(buff)) != -1) {bos.write(buff,0,len);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeInputStream(is,bis);closeOutputStream(bos);}}/*** 将bitmap保存到外部存储文件* @param parent 目标文件父目录* @param fileName 文件名* @param bitmap 图片*/public void putBitmapToExternalStorage(File parent, String fileName, Bitmap bitmap){BufferedOutputStream bos = null;File bit = makeFile(parent,fileName);try {bos = new BufferedOutputStream(new FileOutputStream(bit));if (fileName.contains(".png") || fileName.contains(".PNG")) {bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);} else {bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);}bos.flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeOutputStream(bos);}}/**====================================读取数据=================================================**//*** 读取外部文件数据* @param path 文件路径* @return 文件的字节数组*/public byte[] getDataFromExternalStorage(String path){byte[] data = null;File file = new File(path);if (!file.exists()) return null;BufferedInputStream bis = null;ByteArrayOutputStream bos = null;try {bis = new BufferedInputStream(new FileInputStream(file));bos = new ByteArrayOutputStream();byte[] buff = new byte[8*1024];int len;while ((len = bis.read(buff)) != -1) {bos.write(buff,0,len);}data = bos.toByteArray();bos .flush();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeInputStream(bis);closeOutputStream(bos);}return data;}/*** 按行读取文件内容* @param path 文件路径* @return*/public List<String> getStringFromExternalStorage(String path){File file = new File(path);if (!file.exists()) return null;List<String> data = new ArrayList<>();InputStreamReader isr = null;BufferedReader br = null;try {isr = new InputStreamReader(new FileInputStream(file),"utf-8");br = new BufferedReader(isr);data.add(br.readLine());isr.close();br.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return data;}/**====================================文件拷贝操作=================================================**//*** 单个文件复制* @param oldFile 原文件目录* @param newFile 新文件*/public void copyFile(String oldFile,File newFile){File oldF = new File(oldFile);if(!oldF.exists()) return;FileInputStream fis = null;FileOutputStream fos = null;try {fis = new FileInputStream(oldF);fos = new FileOutputStream(newFile);byte[] buff = new byte[8*1024];int len;while ((len = fis.read(buff)) != -1) {fos.write(buff,0,len);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {closeInputStream(fis);closeOutputStream(fos);}}/**====================================资源文件操作=================================================**//*** 获取raw目录下文件数据流 调用 getDataFromRaw(R.raw.mango)* @param resourceID R.raw.mango* @return*/public InputStream getStreamFromRaw(int resourceID){InputStream in = null;try {in = mContext.get().getResources().openRawResource(resourceID);} catch (Exception e) {e.printStackTrace();}return in;}/*** 获取assert目录下文件数据流* 调用getDataFromAssets("mango.txt")* 如果多层目录就要带上父级目录 getStreamFromAssets("today/day.txt")* @param fileName 文件全名,包括后缀* @return 数据流*/public InputStream getStreamFromAssets(String fileName){InputStream in = null;try {in = mContext.get().getResources().getAssets().open(fileName);} catch (IOException e) {e.printStackTrace();}return in;}/*** 从资源目录下读取数据* @param is 数据流* @return*/public byte[] getDataFromResource(InputStream is){if(is == null) return null;try {int lenght = is.available();byte[]  buffer = new byte[lenght];//将文件中的数据读到byte数组中is.read(buffer);is.close();return buffer;} catch (Exception e) {e.printStackTrace();}return null;}/*** 将资源文件拷贝到外部存储* @param file 输出目标文件* @param is 资源文件流*/public  void moveResourceFileToExternalStorage(File file, InputStream is){if(is == null) return;FileOutputStream os = null;try {os = new FileOutputStream(file);byte[] buffer = new byte[2*1024];int len;while ((len = is.read(buffer)) != -1){os.write(buffer, 0, len);}os.flush();} catch (IOException e) {e.printStackTrace();}finally {closeInputStream(is);closeOutputStream(os);}}/**====================================文件普通操作=================================================**/private List<File> fList = new ArrayList<>();public void clearFlist(){fList.clear();}/*** 给文件重命名* @param oldPath 原文件* @param newPath 新文件* @return*/public boolean renameFile(String oldPath,String newPath){File oldFile = new File(oldPath);File newFile = new File(newPath);return oldFile.renameTo(newFile);}/*** 遍历目录* @param path 文件夹目录* @return*/public List<File> listFile(String path){File file = new File(path);if (!file.exists()) {return null;}File[] data = file.listFiles();if(data == null) return null;for(int i=0; i<data.length; i++){File child = data[i];if (child.isFile()) {fList.add(child);} else {listFile(child.getAbsolutePath());}}return fList;}/**** @param path*/public void delFile(String path){List<File> file = listFile(path);if(file == null)return;for(int i=0; i<file.size(); i++){file.get(i).delete();}}/*** 获取文件的文件名(不包括扩展名)*/public String getFileNameWithoutExtension(String path) {if(path == null) {return null;}int separatorIndex = path.lastIndexOf(File.separator);if(separatorIndex < 0) {separatorIndex = 0;}int dotIndex = path.lastIndexOf(".");if(dotIndex < 0) {dotIndex = path.length();} else if(dotIndex < separatorIndex) {dotIndex = path.length();}return path.substring(separatorIndex + 1, dotIndex);}/*** 获取文件名*/public String getFileName(String path) {if(path == null) {return null;}int separatorIndex = path.lastIndexOf(File.separator);return (separatorIndex < 0) ? path : path.substring(separatorIndex + 1, path.length());}public void closeInputStream(InputStream... is){for (int i=0; i<is.length; i++){if (is[i] != null) {try {is[i].close();} catch (IOException e) {e.printStackTrace();}}}}public void closeOutputStream(OutputStream... os){for (int i=0; i<os.length; i++){if (os[i] != null) {try {os[i].close();} catch (IOException e) {e.printStackTrace();}}}}/*** 判断sd卡是否处于就绪状态 可读可写* MEDIA_UNKNOWN:未知状态* MEDIA_REMOVED:移除状态(外部存储不存在)* MEDIA_UNMOUNTED:未装载状态(外部存储存在但是没有装载)* MEDIA_CHECKING:磁盘检测状态* MEDIA_NOFS:外部存储存在,但是磁盘为空或使用了不支持的文件系统* MEDIA_MOUNTED:就绪状态(可读、可写)* MEDIA_MOUNTED_READ_ONLY:只读状态* MEDIA_SHARED:共享状态(外部存储存在且正通过USB共享数据)* MEDIA_BAD_REMOVAL:异常移除状态(外部存储还没有正确卸载就被移除了)* MEDIA_UNMOUNTABLE:不可装载状态(外部存储存在但是无法被装载,一般是磁盘的文件系统损坏造成的)* @return*/public boolean isSdCardMount(){return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);}public String reviseFileSize(long size){String str="KB";float reviseSize = 0f;if(size>1024){reviseSize = size/1024f;if(reviseSize>1024){str="M";reviseSize = reviseSize/1024f;if (reviseSize>1024) {str="G";reviseSize = reviseSize/1024f;}}}DecimalFormat formatter=new DecimalFormat();formatter.setGroupingSize(3);String result = formatter.format(reviseSize) + str;return result;}}

后续文件其它操作再陆续补充

Android开发--文件系统中的内部存储和外部存储最全解析 附文件数据保存操作封装相关推荐

  1. Android开发中内存、内部存储、外部存储详解

    手机是有两个内存的.2G和16G同时出现在一个手机中,2G是指运行内存,16G是指存储内存. 手机的内存,分两种,一个是存储内存,相当于电脑的硬盘,一般手机参数里超过4G的都是指这个.存储内存是可以扩 ...

  2. android以ini文件形式保存在内部存储_彻底了解android中的内部存储与外部存储

    打开手机设置,选择应用管理,选择任意一个App,然后你会看到两个按钮,一个是清除缓存,另一个是清除数据,那么当我们点击清除缓存的时候清除的是哪里的数据?当我们点击清除数据的时候又是清除的哪里的数据?读 ...

  3. android 根目录缓存,Android系统中内部存储和外部存储(公有目录、私有目录、缓存目录)详解...

    首先,明确一个概念,Android内部存储和外部存储并非所谓的手机自带内存是内部存储,SD卡是外部存储云云. Android对内部存储和外部存储不是在物理上区分的,而是在逻辑上区分的.git 1.概念 ...

  4. Android内存解析(二)— 详解内存,内部存储和外部存储

    总述 觉得十分有必要搞清楚内存,内部存储和外部存储的区别,还有我们在开发中真正将数据存在了手机的哪儿. 先提一个问题:手机设置的应用管理中,每个App下都有清除数据和清除缓存,清除的分别是哪里的数据? ...

  5. 解析Android内部存储、外部存储的区别

    1.背景 在开发过程中我们都会使用到手机的内部缓存.外部缓存.但有些开发者对这两个存储区域理解还够透彻,以为手机内置的存储卡(不可手机移除)就是内部存储, 可插拔的SD卡就是外部存储,其实这些理解都是 ...

  6. Android内部存储与外部存储(私有目录与公共目录)图文详解

    目录 一.存储空间概述 二.存储空间的划分 1.存储划分 2.内部存储 2.1 内部存储概述 2.2 内部存储 - 私有目录 3. 外部存储 3.1 外部存储概述 3.2 外部存储 - 私有目录 3. ...

  7. Android内部存储与外部存储解析

    Android开发的过程中.经常会涉及到存储,之前一直没有一个整体的概念,这篇文章就是进行一个知识点的梳理. Android的存储有:内部存储.外部存储. 想要了解这两个概念,我们先将开发软件中的DD ...

  8. 转:彻底搞懂Android文件存储---内部存储,外部存储以及各种存储路径解惑

    转自:https://blog.csdn.net/u010937230/article/details/73303034 前言: 对于任何一个应用来说,无论是PC端应用还是Android应用,存储肯定 ...

  9. Android文件存储---内部存储,外部存储以及各种存储路径解惑

    本文转自:https://blog.csdn.net/u010937230/article/details/73303034 前言: 对于任何一个应用来说,无论是PC端应用还是Android应用,存储 ...

最新文章

  1. 【camera】基于YOLO的车辆多维特征识别系统(车色,车品牌,车标,车型)与PYQT实现(课程设计)
  2. 利用csc.exe 手动编译C#程序
  3. 卷积神经网络 卷积的概念
  4. Map 遍历取值及jstl的取值
  5. 转:高等数学、线性代数、概率论数理统计书籍推荐
  6. 确认过眼神是先用上5G的人!中国联通将在7个城市开通5G试验网
  7. qt如何在label中显示汉字_工控机中如何实现多屏显示
  8. Maven依赖中scope的含义
  9. c语言数组用户注册登入管理系统_[内附完整源码和文档] 基于JAVA的干部档案管理系统...
  10. delphi7 获取计算机名,远程控制篇:获得网络邻居所有机器名_delphi教程
  11. oracle alter user identified,11g 使用 alter user identified by values password 恢复历史密码
  12. 揭秘抖音快手刷赞刷评论骗局,揭秘抖音刷赞兼职套路
  13. Google chrome浏览器快捷方式
  14. 启用计算机上的无线,如何使用命令提示符打开或关闭计算机上的wifi
  15. 65个最常见的面试问题与技巧性答复(面试技巧和注意事项),很不错,求职之前,多看看
  16. React(10)-组件通信(important)
  17. Web大学生网页作业成品:基于html制作中国科技发展网站设计题材【航天之路7页】HTML+CSS+JavaScript
  18. 澳洲留学:说说在澳洲打工的那些苦与痛
  19. python换图片_详解Python给照片换底色(蓝底换红底)
  20. 一元流量参数为null_了解JavaScript中null和undefined之间的差异和相似之处

热门文章

  1. 计算机配置64位,任何电脑都可以装64位系统吗|是不是所有的电脑都可以装64位系统...
  2. 六.实战——Excel表格的导入和导出
  3. 逍遥B2C商城源码(PC H5)v1.1.3
  4. ? 一图看完国内手机市场占有率
  5. Kitty代码生成器
  6. Linux:CPU状态信息us,sy,ni,id,wa,hi,si,st含义
  7. “燕云十六将”之橙子刘龙静
  8. Task1.2 A.I. 发展史
  9. 最新微信QQ域名防封、微信群活码系统
  10. 供应链管理系统--(1)退供管理