移动开发 架构设计 编程语言 互联网 开发经验 Web前端
开发杂谈 系统运维 研发管理 数据库 云 计 算 Java开发
首页 开发总结 移动开发 编程语言 开发经验 Java开发
 当前位置:首页 > android基础总结 内部存储和外部存储 > 正文显示
 最新推荐文章
获取扩展sd卡跟路径 android基础总结 内部存储 Android 获取系统各个目录 Android遍历手机内所有文件
Android权限管理之Perm Android教程 使用Ecli Android SDK版本和AD 搭建最新版本的Android开发
SetThreadAffinit 直接显示图片 http://www.33lc. sofasofa.io
http://blog-1003 wave 采集总结 window Zlib库对网页中deflate 设置窗口最大最小大小,窗口变化相
android基础总结 内部存储和外部存储   (54)

研究并了解API的方法,才能根本理解内部存储和外部存储的区别。单纯记忆他们的区别,不如学习学习API,作为开发者,莫要本末倒置,android版本年年更新,不变还是API方法名(你大爷还是你大爷),本篇将从API的角度带大家了解有关存储相关的知识。

开胃菜—android的存储题:
1.SharedPreferences和SQLite的数据存储路径,怎么获取,存在什么位置?
2.今年android主流机型支持双卡,小卡的位置支持扩展第二张SD卡,那么,用代码如何区分内置/扩展SD卡的路径?
3.用Environment操作创建的路径文件,会不会污染SD卡?

一android中存储操作的相关类

关于android存储操作,一般就是路径的操作,比如内部存储中文件路径如何创建获取,外部存储的某个文件如何获取等,相关的类有Environment,Context抽象类(包括众多子类,如Application,Activity等)。还有辅助类StatFs,MemoryInfo。下面依次带大家了解官方API的这些类和方法(API的部分过个眼熟,二–<二>有详细总结):

<一>Environment中File的API详解

在android.os包下,Environment类中可用的11个函数及使用细节:

方法名 路径/解释
(1)getDataDirectory() 返回用户数据目录(返回File): /data
(2)getDownloadCacheDirectory() 返回 /cache content 目录(返回File):/cache
(3)getExternalStorageDirectory() 返回外部存储根目录,(返回File), /mnt/sdcard 或者/storage/emulated/0等
(4)getExternalStorageState() 判断外部存储状态 (返回String),若是外部存储,返回一个mounted的String,对应Environment.MEDIA_MOUNTED常量
(5)getExternalStorageState(File path) 判断文件是否是外部存储,同上
(6) getRootDirectory() 返回手机系统根目录下的system文件目录(返回File) :/system
(7)isExternalStorageEmulated() 判断外部存储设置是否有效(返回boolean),用法:一些apk安装到外部存储,需要先用此方法判断,false则不可安装
(8)isExternalStorageEmulated(File path) 判断path设置是否有效(返回boolean), 用法:一些apk安装到外部存储,需要先用此方法判断,false则不可安装
(9)isExternalStorageRemovable() 如果存储设备可以被拆除(例如SD卡)返回true,如果存储设备不能物理删除(例如内置sd卡)返回false
(10)isExternalStorageRemovable(File file) 如果存储设备可以被拆除(例如SD卡)返回true,如果存储设备不能物理删除(例如内置sd卡)返回false
(11)getExternalStoragePublicDirectory(String type) 获取外部存储的公共目录,共有10个(返回File)用法见下边代码
结合代码,用红米pro真机测试,无外置sd卡,查看输出区别:

//1.返回结果: /data
Log.d(TAG, Environment.getDataDirectory().toString());

//2.返回结果: /cache
Log.d(TAG, Environment.getDownloadCacheDirectory().toString());

//3.返回结果: /storage/emulated/0 外部存储的根目录
Log.d(TAG, Environment.getExternalStorageDirectory().toString());

//4.返回结果:mounted,表示是外部存储
Log.d(TAG, Environment.getExternalStorageState().toString());

//5.返回结果:mounted
Log.d(TAG, Environment.getExternalStorageState(new File(Environment.getExternalStorageDirectory(), "demo.png")).toString());

//6.返回结果:/system
Log.d(TAG, Environment.getRootDirectory().toString());

//7.返回结果:false 表示外部存储不可安装apk
Log.d(TAG, Environment.isExternalStorageEmulated() + "");

//8.返回结果:false 表示外部存储不可安装apk
Log.d(TAG, Environment.isExternalStorageEmulated(new File(Environment.getExternalStorageDirectory(), "demo.png")) + "");//png存不存在不影响结果

//9.返回结果:false 表示是内置内存卡
Log.d(TAG, Environment.isExternalStorageRemovable() + "");

//10.返回结果:false 表示是内置内存卡
Log.d(TAG, Environment.isExternalStorageRemovable(new File(Environment.getExternalStorageDirectory(), "demo.png")) + "");

/**
* 十大公共目录
*/
//11-1.返回结果:storage/emulated/0/Music
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).toString());

//11-2.返回结果:storage/emulated/0/Pictures
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString());

//11-3.返回结果:storage/emulated/0/Alarms
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS).toString());

//11-4.返回结果:storage/emulated/0/DCIM
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString());

//11-5.返回结果:storage/emulated/0/Documents
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString());

//11-6.返回结果:storage/emulated/0/Download
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString());

//11-7.返回结果:storage/emulated/0/Movies
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).toString());

//11-8.返回结果:storage/emulated/0/Notifications
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS).toString());

//11-9.返回结果:storage/emulated/0/Podcasts
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS).toString());

//11-10.返回结果:storage/emulated/0/Ringtones
Log.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES).toString());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<二>Context中和File有关的API详解

这里,调用的文件方法,都是和内部存储和外部的私有目录相关,同Environment的调用有很大区别,根据API的使用注释,私有目录通过context.getExteralxxx的几个方法,其路径在外部存储的/storage/emulated/0/Android/data/程序包名/下,所以对应的,调用了私有目录的方法获取路径时,如果目录不存在,会自动创建,而Environoment的操作的外部存储方法,创建出来需要手动删除.

方法名 路径/解释
(1) getCacheDir() 返回文件系统该应用程序的缓存目录,且是绝对路径 (返回File),app涉及到缓存常用次方法创建
(2) getCodeCacheDir() 为存储缓存代码设计的,回文件系统该应用程序的特殊缓存目录,且是绝对路径(返回File)
(3)getDataDir() 返回系统文件该应用程序所有存储的私有文件的绝对路径(返回File)
(4)getDatabasePath(String name) 返回系统文件上创建的SQLite的绝对路径,使用该方法的前提是已经使用 SQLiteDatabase.openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory)方法创建了SQLite,其中参数就是name (返回File)
(5)getDir(String name, int mode) 检索目录,如果没有,就在应用程序下创建一个name的自定义文件(返回File)
(6)getExternalCacheDir() 获取外部存储的私有缓存目录,绝对路径(返回File)
(7)getExternalCacheDirs() 获取外部存储的私有缓存目录数组,绝对路径(返回File[])
(8)getExternalFilesDir(String type) 获取外部存储的私有文件目录,绝对路径(返回File) type见补充
(9)getExternalFilesDirs(String type) 获取外部存储的私有文件目录数组,绝对路径(返回File[])
(10)getExternalMediaDirs() 获取外部存储的私有的媒体文件目录 (返回File[])
(11)getFileStreamPath(String name) 打开应用程序私有目录的文件,前提是先使用 openFileOutput(String name,int mode)方法创建了私有文件 (返回File)
(12)getFilesDir() 同上(返回File)
(13)getNoBackupFilesDir() 使用同getFilesDir()(返回File)
(14)getObbDir() ) 返回应用程序上obb文件的绝对路径(返回File)
(15)getObbDirs() 返回应用程序上obb文件的绝对路径(返回File[])
(8)的补充:官方文档给的type只有7个,和外部存储的10大公共目录,都是通过Environment.DIRECTORY_xxx调用,而十大公共目录的type有十个,此处缺少的是DIRECTORY_DOWNLOADS,DIRECTORY_DOCUMENT和DIRECTORY_DCIM这三个type,
支持的7个如下:
DIRECTORY_MUSIC,
DIRECTORY_PODCASTS,
DIRECTORY_ALARMS,
DIRECTORY_RINGTONES,
DIRECTORY_NOTIFICATIONS,
DIRECTORY_PICTURES,
DIRECTORY_MOVIES.
通过测试缺少的三个的类型,发现也可以在内部存储中创建,所以type的类型同Environment的type,之所以是7个,是功能需求用不到,不过创建缺少的三个文件,不影响使用。

同样结合代码,使用android studio创建demo,包名是com.storage.demo,查看红米pro的打印结果(三星xplay6结果也相同):

try {

//1.返回结果:/data/user/0/com.storage.demo/cache
Log.d(TAG, "getCacheDir()=" + this.getCacheDir().toString());

//2.返回结果:/data/user/0/com.storage.demo/code_cache
Log.d(TAG, "getCodeCacheDir()=" + this.getCodeCacheDir().toString());

//3.返回结果:没有支持的LEVEL24设备,就不写了
// Log.d(TAG, "getDataDir()=" + this.getDataDir().toString());

//4.返回结果:/data/user/0/com.storage.demo/databases/sjy.db
Log.d(TAG, "getDatabasePath()=" +this.getDatabasePath("sjy.db").toString());

//5.返回结果:/data/user/0/com.storage.demo/app_sjy.db
Log.d(TAG, "getDir()=" + this.getDir("sjy.db", MODE_PRIVATE).toString());

//6.返回结果:/storage/emulated/0/Android/data/com.storage.demo/cache
Log.d(TAG, "getExternalCacheDir()=" + this.getExternalCacheDir().toString());

//7.返回结果:/storage/emulated/0/Android/data/com.storage.demo/cache
File[] CacheDirs = this.getExternalCacheDirs();
StringBuffer CacheDirsbuffer = new StringBuffer();
for (int i = 0; i < CacheDirs.length; i++) {
CacheDirsbuffer.append(CacheDirs[0].toString());
CacheDirsbuffer.append("\n");
}
Log.d(TAG, "getExternalCacheDirs()=" + CacheDirsbuffer.toString());

//8.返回结果: /storage/emulated/0/Android/data/com.storage.demo/files/DCIM
Log.d(TAG, this.getExternalFilesDir(Environment.DIRECTORY_DCIM).toString());

//9.返回结果:/storage/emulated/0/Android/data/com.storage.demo/files/Movies
File[] FilesDirs = this.getExternalFilesDirs(DIRECTORY_MOVIES);
StringBuffer FilesDirsbuffer = new StringBuffer();
for (int i = 0; i < FilesDirs.length; i++) {
FilesDirsbuffer.append(FilesDirs[0].toString());
FilesDirsbuffer.append("\n");
}
Log.d(TAG, "getExternalFilesDirs()=" + FilesDirsbuffer.toString());


//10.返回结果:/storage/emulated/0/Android/media/com.storage.demo
File[] files = this.getExternalMediaDirs();
StringBuffer MediaDirsbuffer = new StringBuffer();
for (int i = 0; i < files.length; i++) {
MediaDirsbuffer.append(files[0].toString());
MediaDirsbuffer.append("\n");
}
Log.d(TAG, "getExternalMediaDirs()=" + MediaDirsbuffer.toString());

//11.返回结果:/data/user/0/com.storage.demo/files/unknown.db
Log.d(TAG, "getFileStreamPath()=" + this.getFileStreamPath("unknown.db").toString());

//12.返回结果:/data/user/0/com.storage.demo/files
Log.d(TAG, "getFilesDir()=" + this.getFilesDir().toString());

//13.返回结果:/data/user/0/com.storage.demo/no_backup
Log.d(TAG, "getNoBackupFilesDir()=" + this.getNoBackupFilesDir().toString());

//14.返回结果:/storage/emulated/0/Android/obb/com.storage.demo
Log.d(TAG, "getObbDir()=" + this.getObbDir().toString());

//15.返回结果:/storage/emulated/0/Android/obb/com.storage.demo
File[] ObbDirs = this.getObbDirs();
StringBuffer ObbDirsbuffer = new StringBuffer();
for (int i = 0; i < ObbDirs.length; i++) {
ObbDirsbuffer.append(ObbDirs[0].toString());
ObbDirsbuffer.append("\n");
}
Log.d(TAG, "getObbDirs()=" + ObbDirsbuffer.toString());


} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, e.toString());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<三>辅助类ActivityManager.MemoryInfo–RAM空间操作**

如下是MemoryInfo的所有方法:

方法名/变量名 详解
(1)availMem 系统可用空间RAM大小(返回值 long)
(2)lowMemory 当系统环境可用空间很低时,该值设为true(返回值 boolean)
(3)threshold 我们认为内存是低的,并且开始杀死后台服务和其他非外部的进程的阈值。(返回值 long)
(4)totalMem 内核可访问的总内存。(返回值 long)
describeContents() 描述这个可分配实例的集合表示中包含的特殊对象的种类。(返回值 int)
readFromParcel(Parcel source)
writeToParcel(Parcel dest, int flags) 把这个物体拉到一个包裹里。
其实Runtime 也有关于JVM(进程)的内存空间操作,这里请看代码打印:
注:测试机红米RAM 3G,xplay6RAM 6G

private void getRamSpace() {
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
long availableLong = memoryInfo.availMem;
long totalLong = memoryInfo.totalMem;
//红米pro: RAM可用空间:885M
//三星xplay6: RAM可用空间:2953M
Log.d(TAG, "RAM可用空间:" + availableLong / 1024 / 1024 + "M");
// RAM总空间:2718M
//三星xplay6: RAM总空间:5696M
Log.d(TAG, "RAM总空间:" + totalLong / 1024 / 1024 + "M");

int memory = activityManager.getMemoryClass();
float maxMemory = (float) (Runtime.getRuntime().maxMemory() * 1.0 / (1024 * 1024));
float totalMemory = (float) (Runtime.getRuntime().totalMemory() * 1.0 / (1024 * 1024));
//剩余内存
float freeMemory = (float) (Runtime.getRuntime().freeMemory() * 1.0 / (1024 * 1024));
//该进程最大分配内存:384M--384.0M
Log.d(TAG, "该进程最大分配内存:" + memory + "M" + "--" + maxMemory + "M");

//该进程总内存:16.027634M
Log.d(TAG, "该进程总内存:" + totalMemory + "M");

//该进程剩余内存:4.1224976M
Log.d(TAG, "该进程剩余内存:" + freeMemory + "M");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<四>辅助类StatFs–操作存储空间的API详解

该类向开发者提供了获取空间大小的三种方法,分别是 :
应用可用空间
系统可用空间
系统总空间。
而且每一种空间的获取对应两种计算方法。如下表,API已经按顺序分别介绍:

方法名 详解
(1)getBlockSizeLong() 系统文件中每一个存储块的大小(返回long) 该值和(2)(3)(4)值分别相乘,获得的结果就是byte值,分别对应(5)(6)(7)的值,见代码打印
(2)getAvailableBlocksLong() 应用程序可用存储块的总数(返回long)
(3)getFreeBlocksLong() 系统可用存储块的总数(返回long),数值比应用程序的可用存储块总数大 (返回long)(对于普通的应用程序是不可用的)
(4)getBlockCountLong() 文件系统上的所有存储块的总数(返回long)
(5) getAvailableBytes() 应用程序可用空间字节数(返回long) ,操作1024,可以获得M,G的剩余空间大小(1)*(2)
(6)getFreeBytes() 文件系统上空闲的字节数(对于普通的应用程序是不可用的) (1)*(3)
(7)getTotalBytes() 系统总字节数 对应(1)*(4)
结合代码的打印,加深理解(注:64G内置存储,无扩展SD卡):


//经测试,StatFs参数不管用context还是Environment,结果没影响
StatFs statFs = new StatFs(this.getExternalCacheDir().getPath());
long blockSize = statFs.getBlockSizeLong();

long availableBlocksLongs = statFs.getAvailableBlocksLong();
long freeBlocksLong = statFs.getFreeBlocksLong();
long BlocksLong = statFs.getBlockCountLong();

//1.返回值:每一个存储块的大小尺寸: 4096
Log.d(TAG, "每一个存储块的大小尺寸:" + blockSize);


//2.返回值:该app外部存储可用的存储块总数:5148216
Log.d(TAG, "该app外部存储可用的存储块总数:" + availableBlocksLongs);

//3.返回值:剩余外部存储可用的存储块总数:5185080
Log.d(TAG, "剩余外部存储可用的存储块总数:" + freeBlocksLong);

//4.返回值: 外部存储总的存储块数量:14059344
Log.d(TAG, "外部存储总的存储块数量" + BlocksLong);


//5-1.返回值:外部存储应用程序可用空间=20110M
Log.d(TAG, "外部存储app可用空间=" + statFs.getAvailableBytes() / 1024 / 1024 + "M");

//5-2.返回值:外部存储应用程序可用空间=20110M
Log.d(TAG, "外部存储app可用空间=" + blockSize * availableBlocksLongs / 1024 / 1024 + "M");


//6-1.返回值: 外部存储系统可用空间20254M
Log.d(TAG, "外部存储系统可用空间" + statFs.getFreeBytes() / 1024 / 1024 + "M");

//6-2.返回值: 外部存储系统可用空间20254M
Log.d(TAG, "外部存储系统可用空间" + blockSize*freeBlocksLong/ 1024 / 1024 + "M");


//7-1.返回值: 外部存储总空间53G
Log.d(TAG, "外部存储总空间" + statFs.getTotalBytes() / 1024 / 1024 / 1024 + "G");

//7-2.返回值: 外部存储总空间53G
Log.d(TAG, "外部存储总空间" + blockSize*BlocksLong/ 1024 / 1024 / 1024 + "G");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
是不是看完此处,就可以写出一个工具类了呢!!!,那么,问题来了,小米标榜的64G,为什么存储总空间只有53G呢?看下面就知道了

二 开发者进阶总结

<一>ROM和RAM,内部存储和外部存储,他们的区别与联系

首先普及一下硬件的知识,现在的手机,已经更倾向于内置SD卡、内置电池的一体智能机。早年的1G+4G+TF到现在的6G+128G的普配。再用RAM和ROM的定义强加给6G+128G已经变得很狭隘了,因为现在的厂商已经将ROM集成到所谓的128G当中去了(准确的说,是android4.4以后),所谓的内置SD卡,也不单单是存储卡,请看下图(修改:外置存储器修改为外部存储):

这里写图片描述
开发者常困惑的地方就在内置存储器这一部分,内置存储器被分为两部分,内部存储(ROM)和外部存储,这也是为什么厂商常说的64G 128G的存储,实际使用时候,只有53G,90多G的原因,因为还需要分配给内部存储(ROM)空间,给系统的文件,所有app的安装等使用。

在可见性方面,那就是ROM区不对用户开放(root除外);而内置外部存储对用户开放,就像手机界面中的文件管理,能看到你的私有目录,公共目录,还有n些其他文件,所说的53G就是对外开放空间,11G的空间不对外开放

在维护性方面,(结合图片来看)内部存储和外部存储的私有目录,都不需要用户操作,系统自动维护,当安装和卸载app时,这两处的目录会自动创建和删除,在内部存储中,/data分区,就是所有安装app的数据区,下面细讲。/system分区是放置系统文件,手机在出厂时的所有系统文件基本都是放着这个区,/cache分区是系统文件的缓存目录,和系统的一些操作有关,在外部存储的私有目录中,其路径/storage/emulated/0/Android/data/程序包名/下,根据API,用户可以在此目录中操作和app相关的文件操作,比如缓存大型临时数据,IO读写,下面细讲,还有需要注意的一点,当大型app涉及过多的缓存时,私有目录下的缓存有可能在app中设置缓存按钮被手动清除,或设置中,手动清楚;外部存储的公共目录和自定义目录,用户需要自己维护,当存储满了,就需要删除这里头的一些文件.所谓公共目录就是由getExternalStoragePublicDirectory(type)方法调用,指向的文件路径,现在API中是10个,之前是9个,还有一个自己定义目录,这一块的路径都是在内置外部存储的根目录上创建的,可以归咎于开发者不按常理创建而出的,当使用者将app删除时,创建的文件目录不会跟随删除,用户不清理永远保存在存储上,占用空间。当然知名IT的app都有在自定义目录中创建文件的习惯。

所以开发中,一般不建议在公共目录中创建和app相关的文件目录,也不建议在外部存储中创建自定义目录,,同理,app涉及到缓存和数据存储时,开发者有两处选择,一处是内置外部存储的私有目录,另一处是内部存储目录.但是科技发展使手机性能大幅提升,就导致开发者在内置存储随意创建目录,但是本着同行,讲原则,最好利人利己为好,虽然ROM空间扩展不少,也经不起这么败家,推荐在私有目录设置大型缓存.

<二>内部存储的存储操作总结

这部分常用的操作就是第二个:应用程序的内部存储路径,所以,这一部分可以说全是Context操作
1内部存储路径:/system /cache /data

内部存储路径 方法(全是 Environment调用 )
系统文件:/system Environment.getRootDirectory()
缓存文件:/cache Environment.getDownloadCacheDirectory()
用户数据:/data Environment.getDownloadCacheDirectory()
2应用程序的内部存储路径:/data/user/0/包名/xxx
常用的方法:

应用程序的内部存储路径 方法(全是Context调用)
内部缓存:/data/user/0/com.storage.demo/cache this.getCacheDir()
SQLite路径:/data/user/0/com.storage.demo/databases/sjy.db this.getDatabasePath(“sjy.db”)
自定义内部存储文件:/data/user/0/com.storage.demo/app_sjy.db this.getDir(“sjy.db”, MODE_PRIVATE
自定义内部存储的file下文件:/data/user/0/com.storage.demo/files/unknown.db this.getFileStreamPath(“unknown.db”)
内部存储的file路径:/data/user/0/com.storage.demo/files this.getFilesDir()
SharedPreferences的存储,也在该路径下,通过root,可以查看其路径:/data/user/0/com.storage.demo/shared_prefs/各种xxx.xml文件,SharedPreferences只是interface类型,其具体操作由getSharedPreferences(name,type)获取实现类SharedPreferencesImpl,这部分也只是对数据对xml的操作,没有涉及到路径,所以不用特意关心路径的获取方法。
通常,SharedPreferences的存储会被制作成工具类,但是写法很简单:

SharedPreferences sp = getSharedPreferences(name, type);
Editor et = sp.edit();
et.putXXX(key,value);
et.commit();
1
2
3
4
<三>外部存储的存储操作总结

外部存储,除了私有目录是Context操作之外,全是用Environment操作
1十大公共目录:

路径 方法
storage/emulated/0/XXX Environment.getExternalStoragePublicDirectory(type)
2常用私有目录

路径 方法(全是Context操作)
缓存目录:/storage/emulated/0/Android/data/com.storage.demo/cache this.getExternalCacheDir()
缓存目录数组:/storage/emulated/0/Android/data/com.storage.demo/cache this.getExternalCacheDirs()
IO文件目录7个:/storage/emulated/0/Android/data/com.storage.demo/files/XXX this.getExternalFilesDir(type)
IO文件目录数组7个/storage/emulated/0/Android/data/com.storage.demo/files/XXX this.getExternalFilesDirs(type)
3常用自定义目录

路径 方法(全是Environment操作)
根路径 :/storage/emulated/0 Environment.getExternalStorageDirectory()
这一块就是创建开发者经常操作的方法,操作这里的方法,创建的文件,会污染外部存储,创建的文件不会随app删除而自动删除。
常见的自定义文件,结合File使用,如下:

String basePath = Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File(basePath, "AAAA/BBB");
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Log.d(TAG, "进入内置外部存储操作");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
输出自然是右图:这里写图片描述

如何正确使用私有目录

上边的方法可以看到,外部存储创建很方便,一个Environment.getExternalStorageState()就可以。但是,私有目录创建需要Context,这个Context,既可以是Activity的this,也可以是Application的getApplication(),也可以是getContext()等等,是不是有点懵~~,是不是有种context的OOM的担忧?那就对了,要怎么创建呢?分析怎么不OOM,最简单的方法就是在Application中一次性创建:使用Application的Context最安全,能够全局调用。
创建一个FileUtils:

/**
* 缓存文件demo
*/

public class FSFileUtils {
public static String basePath = null;// 保存的根目录
public static String savePicPath = null;//图片路径
public static String saveVoicePath = null;//音频路径
public static String registFilePath = null;//注册文件路径(assets下的.lic .model文件路径)

/**
* application中需要初始化
*/
public static void initSavePath(Context context) {
String path;
//文件根路径在 私有目录中
File dataFile = context.getApplicationContext().getExternalFilesDir(null);
if (dataFile != null) {
path = dataFile.getAbsolutePath();
} else {
//正常的手机,这一部分是永远不会执行 这里是内部存储的路径,
dataFile = context.getApplicationContext().getFilesDir();
path = dataFile.getAbsolutePath();
}
basePath = path;
savePicPath = basePath + File.separator + Constants.PIC_FILE;
saveVoicePath = basePath + File.separator + Constants.VOICE_FILE;
registFilePath = path;


mkDir(registFilePath);
mkDir(savePicPath);
mkDir(saveVoicePath);
}

/**
* 创建路径
*/
public static boolean mkDir(String dirPath) {
boolean isExist;
File dirFile = new File(dirPath);
if (!dirFile.exists() || !dirFile.isDirectory()) {
isExist = dirFile.mkdir();
} else {
isExist = true;
}

return isExist;
}

/**
* 清除缓存
*/
public static void clearFiles() {
File baseFile = new File(basePath);
File[] fileList;
if (baseFile.exists() && baseFile.isDirectory()) {
fileList = baseFile.listFiles();
for (int i = 0; i < fileList.length; i++) {
if (fileList[i].isFile()) {
fileList[i].delete();
fileList[i].exists();
}
}
}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
给文件在Application的onCreate中初始化:

@Override
public void onCreate() {
super.onCreate();
//其他操作

//初始化缓存路径
FSFileUtils.initSavePath(getApplicationContext());
}
1
2
3
4
5
6
7
8
最后,就是在任意地方调用FSFileUtils .xxx路径。

<四>被忽略的扩展SD卡操作

扩展内存就是我们插入的外置SD卡,google在底层实现上,更倾向使用内置的外部存储,其底层获取扩展卡路径的方法System.getenv(“SECONDARY_STORAGE”)的SECONDARY_STORAGE,在API level23之后就过时了。还有一种反射方法,代码如下:

/**
* 获取扩展sd卡跟路径
*
* @param mContext
* @return
*/
private static String getExtendedMemoryPath(Context mContext) {

StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
Class storageVolumeClazz = null;
try {
storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isRemovable = storageVolumeClazz.getMethod("isRemovable");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String path = (String) getPath.invoke(storageVolumeElement);
boolean removable = (Boolean) isRemovable.invoke(storageVolumeElement);
if (removable) {
return path;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return "没有扩展sd卡";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
通过反射的方式使用在sdk中被 隐藏 的类 StroageVolume 中的方法getVolumeList(),获取所有的存储空间(Stroage Volume),然后通过参数is_removable控制,来获取内部存储和外部存储(内外sd卡)的路径,参数 is_removable为false时得到的是内置sd卡路径,为true则为外置sd卡路径。

这部分不做多介绍,正常开发,内置外部存储足够使用,所以不建议再使用扩展卡,google也是这么推荐的。

结语:此处只简单介绍了API方法和其具体使用含义,底层逻辑实现并未讲解,但疏通了存储硬件和代码操作的关系,知其然更要知其所以然,真正的存储开发,还涉及更多操作,这只是皮毛,愿小伙伴们自得其乐!
 最热文章
http://www.3 直接显示图片 获取扩展sd卡跟路径 SetThreadAff
搭建最新版本的Andro VC++内存泄漏的检测与 Android 获取系统 Android教程 使用
android基础总结 Android SDK版 Android权限管理之 Android遍历手机内
在Visual C++中 C++中的STL中map vc, c++ stl Zlib库对网页中def
C++ multimap C++中的STL中map VC 获取 程序窗口大小 实时视频传输的关键技术
移动开发 架构设计 编程语言 互联网 开发经验 Web前端
开发杂谈 系统运维 研发管理 数据库 云 计 算 Java开发
 网站联系 QQ: 121756557 E-Mail: 121756557@qq.com ©2018