前情概要
Android 的文件存储也是非常重要的,比如微信中的聊天时产生的语音,视频,图片,文件等等。在视频 app 中下载的视频,某某音乐 app 所产生的缓存文件等等都是需要保存到安卓手机上磁盘的。
然后,由于过多的文件被保存或者缓存到安卓手机的文件,就会占用非常多的存储空间。这也是一个原因,还有一个原因是因为其 app 中所带的素材实在是非常之多,还有第三方库的导入。有很多 app 都是五六十兆,甚至是上百兆的都有。加上现在的存储空间特别大,app 大小也在变大。
刚开始学,所注意到的也就这些。
内部存储和外部存储
在学习文件存储之前,我还是有去了解 Android 的内部存储和外部存储。
什么是内部存储,我们一直认为,手机内置的存储设备就是内部存储;使用 SD 卡,U盘等这种外部设就是外部存储。在 Android 4.4 之前还确实是如此,不过在 Android 4.4 之后。安卓手机本身的存储空间变得越来越大,从以前的 16G 变成了现在的 512G ,那他们这个存储是不是内部存储呢?
不是的,在 Android 4.4 之后,以上所列出的空间大小是“机身存储”,在概念上区分了“内部存储”和“外部存储”。
如果我们在手机上插了一张 SD 卡,那怎么获取 SD 卡上的存储位置呢?
Android 有给我们一个方法,用于调取全部的外部路径,它就是 context.getExternalFilesDir()
,这个方法可以得到所有的外部设备的存储。以及概念上的外部存储。
内部存储
内部存储的保存路径是在 /data/data/applicationId
路径下面,这个路径用户不能通过文件管理器直接访问到,也不能使用 Android 命令行终端来访问。需要获取 root 权限,而且现在的手机使用工具来 root 的成功率实在是太低了,所以我们可以利用安卓模拟器查看里面的内容
因为,安卓模拟器可是自带 root 的。
我们在内部存储的目录中可以看到以下的文件夹,这些文件夹全部都是 app 的文件夹
内部存储的目录
SharedPreferences:/data/data/
数据库目录:/data/data/
文件目录:/data/data/
缓存目录:/data/data/
内部存储路径的方法调用
获取内部存储对应的三个路径非常简单,使用以下三个方法。至于第一个 SharedPreferences 可以看看这篇日志《Android 数据存储之 SharedPreferences》
文件目录:getFilesDir()
缓存目录:getCachesDir()
数据库目录:getDatabasePath()
读写内部存储
本次学习的是文件的读写,这里就展示了读取文件、写入文件和删除文件的一些方法。调用方法之后,就可以像 java io 一样操作内部存储的数据。
而且内部存储的读写是不需要在 AndroidManifest.xml 来声明读写权限的。
- 读取:
openFileInput()
- 参数
- string:文件名
- 参数
- 写入:
openFileOutput()
- 参数
- string:文件名
- int:模式(MODE_PRIVATE,MODE_APPEND)
- 参数
- 删除:
deleteFile()
- 参数
- string:文件名
- 参数
读取和写入,我感觉有点绕,写着写着就混了。
我们可以这样子理解:
老师要求我们背一首诗,要把纸上的诗背到脑子里,肯定需要多读,读的过程相当于输入(Input)到脑子里。脑子相当于是一个“变量”。
然后,老师要开始我们默写这首诗,这个时候就是把脑子里的诗写在一张新纸上,写的过程相当于输出(Output),将变量中的值给打印出来。
例子虽然不怎么好吧,但是我还是能理解的。
调用示例
我们看看使用这三个方法,看看调用的结果如何。这里的 fileName 的值是 test.txt 文件
写入方法
// 存储数据到内部存储
private void saveInternal(String content){
FileOutputStream fos = null;
try {
// 打开文件
fos = openFileOutput(fileName, MODE_PRIVATE);
// 将脑子中的数据写入到文件中
fos.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 关闭文件流
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
写入之后,这个目录下就会生成一个 test.txt 的文件
打开之后,发现里面有我们写入的内容
读取方法
// 从内部存储读取数据
private String readInternal() {
FileInputStream fis = null;
StringBuilder sb = new StringBuilder();
try {
fis = openFileInput(fileName);
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) > 0) {
sb.append(new String(bytes, 0, len));
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
return null;
}
点击读取之后,那个文本控件就会把它给显示出来
删除方法
@Override
public void onClick(View v) {
boolean isDeleted = deleteFile(fileName);
if(isDeleted){
Toast.makeText(FileStorageActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(FileStorageActivity.this, "删除失败,该文件可能不存在", Toast.LENGTH_SHORT).show();
}
}
提示删除成功
删除之后,发现那个 test.txt 的文件不见了
外部存储
外部存储分为共有目录和私有目录。外部存储是我们可以见到的目录,一般,读取外部存储目录的所使用的方法是 Environment.getExternalStorageDirectory()
在模拟器获取到的值是:/storage/emulated/0
在我自己的手机上:/storage/0007-CF27/heguo/
为什么会这样呢?那是因为在我自己的手机上,设置把外部存储的默认值指定到我的 SD 卡上面。我们通过调用 getExternalFilesDirs("")
方法来获取我的手机中的全部的外部存储路径。
调用的结果如下
/storage/emulated/0/Android/data/top.bestguo.androidlayout/files
/storage/0007-CF27/Android/data/top.bestguo.androidlayout/files
可以发现,在自己的手机上,内置的外部存储的路径也是 /storage/emulated/0/
。
不过在读取外部存储空间的时候,需要用户来同意读写。否则,程序会报出异常。关于 Android 的权限那一块也会单独来写,毕竟没有学到。这里通过一行代码来让用户是否同意我们读写
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
其中,Manifest.permission.WRITE_EXTERNAL_STORAGE
这个就是用于读写外部存储的声明。同时这个声明也需要在 AndroidManifest.xml 文件下来声明
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="top.bestguo.androidlayout">
<uses-permission android:name="android.permission.INTERNET" />
<!-- 需要添加的 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 以下是 application 的声明,省略 -->
</manifest>
公有目录
通过方法调用:
Environment.getExternalStoragePublicDirectory(String type)
其中 type 有这些,这些就是文件夹名。通过 Environment.DIRXXX
来获取
DIRECTORY_MUSIC 音乐文件夹
DIRECTORY_PODCASTS, 音乐或电台录音或视频中的剪辑
DIRECTORY_RINGTONES, 铃声文件夹
DIRECTORY_ALARMS, 闹钟相关文件夹
DIRECTORY_NOTIFICATIONS, 通知文件夹
DIRECTORY_PICTURES, 图片文件夹
DIRECTORY_MOVIES, 视频保存文件夹
DIRECTORY_DOWNLOADS, 下载文件夹
DIRECTORY_DCIM, 相机照片缓存文件夹
DIRECTORY_DOCUMENTS 文档文件夹
比如,微信发朋友圈,发图片需要获取 DCIM 下的文件,或者 Pictures 下的文件夹。让用户来选择这些图片,最来上传到朋友圈中。
私有目录
私有目录有以下几种:
/storage/emulated/Android/data/
/storage/emulated/Android/data/
不过外部存储的私有目录可以直接使用文件管理器能查看的到,只是用户可能不是很在意。还有一个是因为,它的存储路径有点长,对于大多数的安卓用户来说可能就非常难找了。
外部存储示例
以下代码示例来对外部存储进行读写
注意:在读写外部存储时需要声明读写权限
写入
private void saveOuter(String context) {
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
FileOutputStream fos = null;
try {
// 获取外部存储的路径
File dir = new File(Environment.getExternalStorageDirectory(), "bestguo");
// 创建文件夹
if(!dir.exists()) {
Log.d("isCreatedDir", dir.mkdirs() + "");
}
// 创建文件
File file = new File(dir, fileName);
Log.d("file_path", file.toString());
if(!file.exists()){
Log.d("isCreatedDir", "" + file.createNewFile());
}
// 创建输入流
fos = new FileOutputStream(file);
fos.write(context.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行完成之后,外部存储中创建了对应的文件夹和文件
打开之后,对应的内容同样也能读取到
读取
private String readOuter() {
FileInputStream fos = null;
// 获取外部存储目录
String path = Environment.getExternalStorageDirectory().toString();
// 创建文件类
File file = new File(path + File.separator + "bestguo", fileName);
try {
StringBuilder sb = new StringBuilder();
// 创建字节流对象
fos = new FileInputStream(file);
int len;
byte[] bytes = new byte[1024];
while((len = fos.read(bytes)) > 0) {
sb.append(fos.read(bytes, 0, len));
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 关闭
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
点击读取,发现读取得到了数据
删除外部文件的话可以直接调用 File 对象下的 delete 方法即可
File file = new File(path + File.separator + "bestguo", fileName);
file.delete();
后记
项目的代码都在 AndroidStudy 的 github 仓库中。
本日志对应的代码:点我进入
参考链接:https://blog.csdn.net/csdn_aiyang/article/details/80665185
请勿发布违反中国大陆地区法律的言论,请勿人身攻击、谩骂、侮辱和煽动式的语言。