在日常的开发工作中,经常会保存文件、图片等操作,但是你可曾想过这些文件保存在什么位置才是最合适的?例如保存图片之后,根据是否可一在图库中查看保存在不同的位置。接下来我们就相信解释一下Android各式各样的存储方案。
SharedPreferences
SharedPreference使用键值对的形式来保存数据,类似于Map,用法较简单,直接上代码:
SharedPreferences sharedPreferences = getSharedPreferences("sharedPreferenceName", MODE_PRIVATE); //获取SharedPerferences对象
SharedPreferences.Editor edit = sharedPreferences.edit();//获取句柄
edit.putInt("key", 2);//添加数据
edit.commit();//在添加数据之后必须提交
sharedPreferences.getInt("key", 0);//从SharedPreferences中获取数据
复制代码
SharedPreferences有4种创建模式:
- MODE_PRIVATE:仅在本应用下可以访问,且如果再次写入该文件,则直接覆盖原文件
- MODE_APPEND: 仅在本应用下可以访问,如果再次写入该文件,则追加
- MODE_WORLD_WRITABLE: 全局访问模式,已废弃
- MODE_WORLD_READABLE: 全局访问模式,已废弃
SharedPreferences通常用来保存一些轻量级的数据,例如通过保存用户名和密码来实现自动登陆。
内部存储与外部存储
所有 Android 设备都有两个文件存储区域:“内部”和“外部”存储。这些名称在Android早期产生,当时大多数设备都提供内置的非易失性内存(内部存储),以及移动存储介质,比如微型SD卡(外部存储)。一些设备将永久性存储空间划分为“内部”和“外部”分区,即便没有移动存储介质,也始终有两个存储空间,并且无论外部存储设备是否可移动,API的行为均一致。以下列表汇总了关于各个存储空间的实际信息。
内部存储 | 外部存储 |
---|---|
始终可用 | 不一定可用 |
默认情况只有本应用可以访问此处保存的文件 | 全局可读(除外部存储的私有目录) |
卸载应用时,位于此处的所有数据都会被删除 | 需要手动删除(除外部存储的私有目录) |
内部存储
在android中内部存储仅有一个位置:
./data/data/packageName/
主要用到两个方法
getFilesDir() 返回/data/data/packageName/files
getCacheDir() 返回应用临时缓存文件的内部目录的File。务必删除所有不再需要的文件并对在指定时间您使用的内存量实现合理大小限制,比如,1MB。如果在系统即将耗尽存储,它会在不进行警告的情况下删除您的缓存文件。
或者,您可以调用 openFileOutput() 获取写入到内部目录中的文件的 FileOutputStream 。例如:
String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;
try {
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(string.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
复制代码
如果需要缓存某些文件,则应使用createTempFile()。例如,以下方法从URL提取文件名并在您的应用的内部缓存目录中以该名称创建文件:
public File getTempFile(Context context, String url) {
File file;
try {
String fileName = Uri.parse(url).getLastPathSegment();
file = File.createTempFile(fileName, null, context.getCacheDir());
} catch (IOException e) {
// Error while creating file
}
return file;
}
复制代码
外部存储
正如前面所说,我们无法保证外部存储时时刻刻都是可用的,所以我们在使用它之前必须进行检查,如果挂载了外部存储,且该外部存储是可读写的,才可以进行操作,具体实现如下:
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
//只能进行读操作
} else {
//可以进行写操作
}
}
复制代码
外部存储分为九大共有目录和私有文件两类,我们分别来讲解一下两种目录
九大共有目录
九大共有目录分别为:DIRECTORY_MUSIC,DIRECTORY_PODCASTS,DIRECTORY_RINGTONES,DIRECTORY_ALARMS,DIRECTORY_NOTIFICATIONS,DIRECTORY_PICTURES,DIRECTORY_MOVIES,DIRECTORY_DOWNLOADS,DIRECTORY_DCIM。
我们最常食用的就是DIRECTORY_DCIM这个类型了。因为用户想要在图库中看到他在应用中保存的图片,一种简单且友好的方式就是使用Environment.getExternalStoragePublicDirectory(type)进行存储,直接上代码了:
/**
* 将文件保存到本地磁盘(一般情况下,应用无关数据必须存储在本地磁盘,在应用卸载之后也不会丢失)
*
* @param file 源文件
* @param type type The type of storage directory to return. Should be one of
* {@link Environment#DIRECTORY_MUSIC}, {@link Environment#DIRECTORY_PODCASTS},
* {@link Environment#DIRECTORY_RINGTONES}, {@link Environment#DIRECTORY_ALARMS},
* {@link Environment#DIRECTORY_NOTIFICATIONS}, {@link Environment#DIRECTORY_PICTURES},
* {@link Environment#DIRECTORY_MOVIES}, {@link Environment#DIRECTORY_DOWNLOADS}, or
* {@link Environment#DIRECTORY_DCIM}. May not be null.
* @param targetFileName 目标文件名
* @return 文件路径
*/
public static String saveFileToDisk(Context ctx, File file, String type, String targetFileName) {
File directory = Environment.getExternalStoragePublicDirectory(type);
if (directory.exists()) {
try {
String targetPath = directory + File.separator + ctx.getString(R.string.app_name)
+ File.separator;
File targetDir = new File(targetPath);
//判断storage/emulated/0/DCIM/AppName目录是否存在
if (!targetDir.exists()) {
targetDir.mkdir();
}
FileInputStream is = new FileInputStream(file);
FileOutputStream os = new FileOutputStream(targetPath + targetFileName);
byte[] bytes = new byte[1024];
int length = -1;
while ((length = is.read(bytes)) != -1) {
os.write(bytes, 0, length);
}
os.flush();
os.close();
is.close();
return targetPath;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
复制代码
通过这段代码,就可以将九大共有类型的文件保存起来。如果是图片,则保存在storage/emulated/0/DCIM/appName/文件名。 /storage/emulated/0/Android/data/cn.bupt.sse309.summary/cache
外部存储的私有文件
尽管这些文件在技术上可被用户和其他应用访问(因为它们在外部存储上),它们是实际上不向您的应用之外的用户提供值的文件。当用户卸载您的应用时,系统会删除私有文件。
外部存储的私有存储路径仅有两个:
- getExternalFilesDir(type); 该方法有一个type参数,如果为null,则默认返回/storage/emulated/0/Android/data/packageName/files/,如果为Enviroment.DIRECTORY_DCIM,则返回//存储在/storage/emulated/0/android/data/packageName/files/DCIM。保存在该文件夹下的文件默认不可见,但是可以在文件浏览器中搜索到(华为 荣耀7亲测)。
- getExternalCacheDir(); 该方法返回/storage/emulated/0/Android/data/packageName/cache,通常用来存放图片缓存。
!!!注意:一定要记得添加以下权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORRAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
复制代码
Raw
位置:res/raw/ 在raw文件夹下不可有文件夹,只能存放单独的文件。程序打包为apk之后,该位置的文件均以原格式存在。
该目录通常用来放置一些大图片,或者某些数据文件,例如全国省市县.json。
Raw目录下的文件会映射到resource中,因此我们 访问Raw文件时,可以通过resource来访问
InputStream inputStream = getResource().openRawResource(R.raw.test);
复制代码
Assets
位置:src/main/assets 在assets我文件夹下的文件可以以任意深度存在。程序打包为apk之后,该位置的文件均以原格式存在。
Assets下的文件不会映射到resource中,我们必须使用AssetManager来访问。
AssetManager am = getResource().getAssets();
InputStream is = am.open("test.png");
复制代码
缓存文件
现在我们已经知道,在内部存储中和外部存储中系统都为我们提供了缓存区,但是这两者有什么区别呢?何时使用内部缓存区,何时使用外部缓存区呢?我们一一来说明。
内部缓存区
我们可以通过**getCacheDir()**方法来获取内部缓存区。因为内部存储是应用私有的,所以内部缓存区也是应用私有的。由于内部存储相对较为宝贵,所以我们必须对缓存文件的大小进行限制,比如1MB,当内部缓存大小超过1M时,则删除某些文件。也正因为内部存储更为宝贵,当内部存储紧张时,系统会不进行任何警告的删除内部缓存区文件。所以一般情况下,都会将需要持久性访问的缓存文件放置在外部缓存区。
外部缓存区
getExternalCacheDir(); 该方法返回/storage/emulated/0/Android/data/packageName/cache,通常用来存放图片缓存。
SQLite
数据格式
Xml
参见简书--@Carson_Ho:Android开发:XML简介及DOM、SAX、PULL解析对比
Json
参见: 简书--@怪盗kidou:Gson使用指南-1 简书--@怪盗kidou:Gson使用指南-2 简书--@怪盗kidou:Gson使用指南-3 简书--@怪盗kidou:Gson使用指南-4