安卓开发离不开手机存储,然而大部分人对于安卓开发中的存储概念存在误区,内部外部SD卡傻傻分不清?
概念扫盲
以下引用来自对官方文档的理解
安卓手机的存储分为 2 部分,内部存储 ( Internal ) 和外部存储
( External )
呵呵呵,先别说话,然而重点来了(敲黑板),按照官方的说法,
内部存储是指系统的存储空间,没有root是访问不到的呦亲,比如sharedPreferenced或者database都是保存在这里面的。
外部存储,又分为 2 部分:
机器自带的存储,也就是手机厂商常说的16G,32G,64G之类的存储空间
SD卡,其实正确叫法应该是TF卡,就是可插拔的那个小东西
然而现实中,常常有同事把手机那个32G,64G存储叫做内部存储= =,宝宝好累,人家明明是 ExternalStorage !!
希望本篇能让大家对内外部存储有一个正确鲜明的认识
常用路径总结
一. 内部存储
files目录
getFilesDir()
路径如下
/data/user/0/<包名>/files // 7.0手机 不确定是手机原因还是系统原因
/data/data/<包名>/files // 4.0手机
文档云:若想操作该路径,你需要一个输出流:
openFileOutput()
就像这样:
FileOutputStream output = this.openFileOutput("- -!.txt", Context.MODE_PRIVATE);
byte[] bytes = "我是刚写入的".getBytes();
output.write(bytes);
output.close();
注: this 是 context 对象
在
/data/data/<包名>/files/- -!.txt
路径下会看到新文件哦
如果你还想读取的话,文档云:你需要一个输入流:
FileInputStream input = this.openFileInput("- -!.txt");
byte[] bytess = new byte[input.available()];
String result = "";
while (input.read(bytess, 0, bytess.length) != -1) {
Log.d("qwer", "result ===>> " + new String(bytess, "UTF-8"));
}
Log如下
D/qwer: result ===>> 我是刚写入的
内部缓存目录
getCacheDir()
文档云:
如果您想要缓存一些数据,而不是永久存储这些数据,应该使用 getCacheDir() 来打开一个 File,它表示您的应用应该将临时缓存文件保存到的内部目录。
当设备的内部存储空间不足时,Android 可能会删除这些缓存文件以回收空间。 但您不应该依赖系统来为您清理这些文件, 而应该始终自行维护缓存文件,使其占用的空间保持在合理的限制范围内(例如 1 MB)。 当用户卸载您的应用时,这些文件也会被移除
路径如下:
/data/data/<包名>/cache
特别的,还有getDir() :
getDir("- -!.txt", Context.MODE_PRIVATE).getAbsolutePath()
路径如下:
/data/data/<包名>/app_- -!.txt
app_是系统自己加上去的
小结
内部存储就是系统的存储,没有root你是看不到的,内部存储最大特点就是可以用Context对象调用各个获取路径的方法。比如:context.fileList()
那就是
/data/data/<包名>/files
下的文件遍历。
而deleteFile("ABC")
就是
/data/data/<包名>/files
删除下名为 ABC 的文件
</br></br></br>
二. 外部存储
操作外部存储你首先需要以下权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
当你申请了write权限,那么read权限默认也就通过啦
再判断状态:
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState))
外部存储的根目录
Environment.getExternalStorageDirectory()
/storage/emulated/0
这个路径根据手机厂家不同会有些许变化
外部公共目录
直接传入 Environment 中的常量获取相应的路径,如下:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS));
/storage/emulated/0/Alarms
或者
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
/storage/emulated/0/Pictures
公有目录下,系统会区分不同类别(例如铃声在系统设置中显示为铃声而不是音乐)
外部私有目录
当用户卸载您的应用时,此目录及其内容将被删除
系统媒体扫描程序不会读取这些目录中的文件,但是其他应用依然可以对该目录下的文件可以读写
4.4以后访问该目录不再需要权限了
getExternalFilesDir(String type)
/storage/emulated/0/Android/data/<包名>/files/<type>
eg.
getExternalFilesDir(Environment.DIRECTORY_MUSIC)
/storage/emulated/0/Android/data/<包名>/files/Music
特别的:ContextCompat下的
ContextCompat.getExternalFilesDirs(context,type)
返回一个File[],在4.4以后第一条数据默认外部主存储目录,第二条数据就是sd卡路径啦,但是注意4.4之前是没有第二条数据的哦
外部缓存目录
该目录下的特点是卸载程序后,该目录和其下所有文件均会被删除
getExternalCacheDir()
/storage/emulated/0/Android/data/<包名>/cache
注意,使用该目录注意管理空间,你不能等系统帮你清理,而是自己清理不再需要的缓存
特别的:ContextCompat下的
ContextCompat.getExternalCacheDirs()
道理同上
内外部路径汇总一览
路径 | 方法名 | 所属 |
---|---|---|
/data/data/<包名>/files | getFilesDir() | 内部 |
/data/data/<包名>/cache | getCacheDir() | 内部 |
/data/data/<包名>/app_<name> | getDir() | 内部 |
/storage/emulated/0 | Environment.getExternalStorageDirectory() | 外部根目录 |
/storage/emulated/0/<type> | Environment.getExternalStoragePublicDirectory(type) | 外部九大公有目录 |
/storage/emulated/0/Android/data/<包名>/files/<type> | getExternalFilesDir(type) | 外部私有目录 |
/storage/emulated/0/Android/data/<包名>/cache | getExternalCacheDir() | 外部缓存目录 |
发现特点了吗朋友,无论外部内部,只有路径中有包名,那么就是私有的,而且是随着程序的卸载而被删除的,有包名的路径均是Context中的方法,而公有的路径均是Environment调用的
SD卡路径
这个货真真是要了老命,一般的方法根部不好使,结合网上有的方法加上公司项目中的方法,总结如下:
百分百好用的获取SD卡路径方法:
try {
StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
Method getVolumeList = null;
getVolumeList = sm.getClass().getDeclaredMethod("getVolumeList");
Object[] volumeList = (Object[]) getVolumeList.invoke(sm);
for (Object volume : volumeList) {
Method getPath = volume.getClass().getDeclaredMethod("getPath");
Method isRemovable = volume.getClass().getDeclaredMethod("isRemovable");
String path = (String) getPath.invoke(volume);
boolean removable = (Boolean) isRemovable.invoke(volume);
if (removable) {
paths.add(path);
}
}
for (String path : paths) {
Log.d("qwer", "path = > " + path);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
最后集合 path 中的值就是SD卡根目录
/storage/sdcard1
虽然无视版本百分百好用,但是如果你的手机有SD卡槽却没插SD卡,该方法最后 path 返回的是 null ,也就是说该方法无法判断到底是没插SD卡还是根本不支持SD卡
其实还有一种方法
String path = System.getenv("SECONDARY_STORAGE");
该方法只要你手机支持SD卡,无论你插没插SD卡,均会返回SD卡路径,但是6.0及以上该方法被移除
Environment中源码其实就是根据这个方法获取路径的
安卓官方文档大家一定要看,他就是我们开发者的权威呀,圣经呀!!