数据持久化之Android文件系统(一)

核心代码如下:

fos=openFileOutput(filename, Context.MODE_PRIVATE);

fos.write(outData.getBytes());//写入数据

fos.close();

首先使用Context的openFileOutput方法获取FileOutputStream,然后按照Java的文件写入方式操作就行了。

##2.2读取数据

String filename=“innerFile”;

FileInputStream fis=null;

try {

fis=openFileInput(filename);

BufferedReader bf=new BufferedReader(new InputStreamReader(fis));

StringBuilder builder=new StringBuilder();

String line=null;

while((line=bf.readLine())!=null){

builder.append(line);

}

Log.i(TAG,“已读取数据:”+builder.toString());

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {//关闭资源

if(fis!=null){

try {

fis.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

关键代码如下:

fis=openFileInput(filename);

BufferedReader bf=new BufferedReader(new InputStreamReader(fis));

StringBuilder builder=new StringBuilder();

String line=null;

while((line=bf.readLine())!=null){

builder.append(line);

}

通过Context的openFileInput方法获取FileInputStream,然后按照Java的文件读取方式操作就行了。

##2.3读取静态文件

对于res/raw文件夹下的数据,可以使用Resources的openRawResource方法读取,示例代码如下:

InputStream fis=null;

try {

fis=getResources().openRawResource(R.raw.rawfile);

BufferedReader bf=new BufferedReader(new InputStreamReader(fis));

StringBuilder builder=new StringBuilder();

String line=null;

while((line=bf.readLine())!=null){

builder.append(line);

}

Log.i(TAG,“已读取Raw数据:”+builder.toString());

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {//关闭资源

if(fis!=null){

try {

fis.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

关键代码如下:

fis=getResources().openRawResource(R.raw.rawfile);

可以看到,这里只是改用openRawResource方法获取InputStream,后续操作和读取内部存储数据一致。

注意,在raw文件夹中,文件名只能包含小写字母、数字和下划线。

##2.4 缓存数据

对于应用的私有缓存数据,可以保存在内部存储的缓存目录中,关键方法是Context的getCacheDir方法。

public abstract File getCacheDir();

这个方法会返回一个File类型的对象,这个File对象对应的就是内部存储中用于保存缓存数据的根目录。通过这个File对象,我们就可以利用Java文件流的方式去读取和写入缓存数据了。

注意,应用的私有缓存文件不应该过大。如果内部存储空间不足,系统可能会删除这些缓存文件。为了保证良好的用户体验,应用应该定期主动清除自己的缓存数据。

#3 外部存储

除了内部存储,Android系统还为开发者提供了外部存储。需要注意的是,外部存储并不仅仅指SD卡,它可能是可移除的存储介质(典型如SD卡),也可能是不可移除的存储介质(如现在很多一体机内置的存储器)。外部存储是相对于内部存储的概念,用于保存全局范围可读取的文件。这也就意味着,保存在外部存储中的数据可以被设备中的任何应用访问,甚至也可以被用户查看、修改。

#4 获取权限

和访问内部存储不同,要读取外部存储中的文件首先需要获取权限,即READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限。如果应用同时有读、写的需求,只需要申请WRITE_EXTERNAL_STORAGE权限即可。

注意,Android 6.0(API 23)引入了运行时权限的概念,以上提到的两种权限都需要动态地获取。如果不了解运行时权限的概念,可以参考这篇博客:

#5 详解Android权限机制

另外,在Android 4.4(API 19)及以上,如果只是在外部存储中读、写应用的私有文件,就不需要申请这些权限。因此,我们可以使用maxSdkVersion属性实现只在较低版本申请权限,如下所示:

<uses-permission android:name=“android.permission.READ_EXTERNAL_STORAGE”

android:maxSdkVersion=“18”/>

#6 可用性检查

由于外部存储存在被移除的情况,我们在使用外部存储前首先应该进行可用性检查。使用Environment的getExternalStorageState方法可以获得外部存储的状态,通过判断返回的状态就实现了对外部存储的可用性检查。下面提供两个简单的示例:

1.判断外部存储是否可写和可读

String state=Environment.getExternalStorageState();

if(Environment.MEDIA_MOUNTED.equals(state)){

//外部存储可写、可读

}

2.判断外部存储是否至少可读

String state=Environment.getExternalStorageState();

if(Environment.MEDIA_MOUNTED.equals(state)||

Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)){

//外部存储至少可读

}

实际上,getExternalStorageState方法有10种返回值,如下:

MEDIA_UNKNOWN:未知状态

MEDIA_REMOVED:移除状态(外部存储不存在)

MEDIA_UNMOUNTED:未装载状态(外部存储存在但是没有装载)

MEDIA_CHECKING:磁盘检测状态

MEDIA_NOFS:外部存储存在,但是磁盘为空或使用了不支持的文件系统

MEDIA_MOUNTED:就绪状态(可读、可写)

MEDIA_MOUNTED_READ_ONLY:只读状态

MEDIA_SHARED:共享状态(外部存储存在且正通过USB共享数据)

MEDIA_BAD_REMOVAL:异常移除状态(外部存储还没有正确卸载就被移除了)

MEDIA_UNMOUNTABLE:不可装载状态(外部存储存在但是无法被装载,一般是磁盘的文件系统损坏造成的)

#7 公共文件(共享文件)

对于在应用中产生的多媒体类型的文件,如音乐、图片、铃声等,一般应该保存在外置存储中对应的公共目录下,如/Music、/Pictures、/Ringtones,这样方便和其他的应用共享这些文件。同时,系统的媒体扫描器也能正确地对这些文件进行归类。

要将这些公共文件(共享文件)保存到指定位置,关键是Environment的getExternalStoragePublicDirectory方法,其原型如下:

public static File getExternalStoragePublicDirectory(String type);

这个方法需要提供一个String类型的type参数,以便返回保存相应类型公共文件的根目录,即一个File对象。type的值不可为null,可选值如下(都是Environment中定义的常量):

DIRECTORY_MUSIC:音乐类型

DIRECTORY_PICTURES:图片类型

DIRECTORY_MOVIES:电影类型

DIRECTORY_DCIM:照片类型

DIRECTORY_DOWNLOADS:下载文件类型

DIRECTORY_DOCUMENTS:文档类型

DIRECTORY_RINGTONES:铃声类型

DIRECTORY_ALARMS:闹钟提示音类型

DIRECTORY_NOTIFICATIONS:通知提示音类型

DIRECTORY_PODCASTS:播客音频类型

注意,返回的文件目录可能还不存在,因此在执行文件操作前应该确保相应的文件目录已经存在,否则使用File的mkdirs方法创建文件目录。

下面演示如何在下载文件类型根目录下创建自己的文件目录(具体保存文件的代码请参考demo):

String directoryName=“PublicFileTest”;

File publicDownloadDirectory=Environment.getExternalStoragePublicDirectory(

Environment.DIRECTORY_DOWNLOADS);

File myDownloadDirectory=new File(publicDownloadDirectory,directoryName);

if(!myDownloadDirectory.exists()){//确保指定目录已经创建

boolean createResult=myDownloadDirectory.mkdirs();

if(createResult){

Log.i(TAG,“创建成功”);

}else{

Log.i(TAG,“创建失败”);

}

}

小技巧:如果不希望系统的媒体扫描器访问我们的媒体文件,可以在媒体文件所在的目录下新建一个名为.nomedia的空文件,这会阻止媒体扫描器归类我们的文件并提供给其他应用。

#8 私有文件

对于应用私有的文件,则应该使用Context的getExternalFilesDir方法访问外部存储中的私有存储目录,媒体扫描器不会扫描这些目录。可以为这个方法传入一个String类型的type参数,用于获取私有存储目录中相应的媒体文件子目录。当然,也可以传入null直接获取私有存储的根目录。这个方法的返回值也是一个File对象。

public abstract File getExternalFilesDir(String type);

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

public abstract File[] getExternalFilesDirs(String type);

另外,出于兼容性的考虑,可以使用ContextCompat的getExternalFilesDirs方法。这是一个静态方法,返回值也是一个File数组。在Android 4.4及以上,效果和Context的getExternalFilesDirs方法一致;而在Android 4.3及以下,返回的File数组始终只包含一个对象。

public static File[] getExternalFilesDirs(Context context, String type)

对于以上方法的type参数,有以下几种可选值(都是Environment中定义的常量):

DIRECTORY_MUSIC:音乐类型

DIRECTORY_PICTURES:图片类型

DIRECTORY_MOVIES:电影类型

DIRECTORY_RINGTONES:铃声类型

DIRECTORY_ALARMS:闹钟提示音类型

DIRECTORY_NOTIFICATIONS:通知提示音类型

DIRECTORY_PODCASTS:播客音频类型

注意,当应用卸载时,这些私有存储目录中的文件也会被删除。此外,虽然系统的媒体扫描器不会访问外部存储中的私有存储目录,但是其他具有READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE权限的应用依旧可以读/写这些私有存储目录中的文件。因此对于真正重要的文件,还是应该保存在应用的内部存储中。

补充:私有文件根目录的参考路径:Android/data/包名/files/

(具体保存文件的代码请参考demo)

#9 缓存文件

在外部存储中也有专门保存缓存文件的空间,可以通过Context的getExternalCacheDir方法访问缓存文件目录,返回值是一个File对象。上文曾说过,外部存储可能同时包含内置存储器和SD卡两个存储空间,因此在Android 4.4(API 19)及以上还可以通过Context的getExternalCacheDirs方法访问这两个存储空间。这个方法会返回一个File数组,包含两个对象,第一个对象默认是外部主存储对应的缓存文件目录。

public abstract File getExternalCacheDir();

public abstract File[] getExternalCacheDirs();

同样,为了兼容性,也可以使用ContextCompat的getExternalCacheDirs方法。这是一个静态方法,返回值也是一个File数组。在Android 4.4及以上,效果和Context的getExternalCacheDirs方法一致;而在Android 4.3及以下,返回的File数组始终只包含一个对象。

public static File[] getExternalCacheDirs(Context context);

注意,当应用卸载时,缓存目录下的文件也会被系统删除。当然,官方建议开发者应该主动移除不再需要的缓存文件,这有助于节省存储空间并保持应用性能。

补充:缓存文件根目录的参考路径:Android/data/包名/cache/

#10 其他常用API

除了以上提过的方法,Android文件系统还提供了其他可用的API,下面简单进行讲解:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

学习分享

在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

七大模块学习资料:如NDK模块开发、Android框架体系架构…

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

[外链图片转存中…(img-7BESRIXT-1713635763899)]

七大模块学习资料:如NDK模块开发、Android框架体系架构…

[外链图片转存中…(img-M2hWR1zx-1713635763900)]

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值