本文主要是介绍android基础总结-内部存储和外部存储的大局观,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
研究并了解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.返回结果: /dataLog.d(TAG, Environment.getDataDirectory().toString());//2.返回结果: /cacheLog.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.返回结果:/systemLog.d(TAG, Environment.getRootDirectory().toString());//7.返回结果:false 表示外部存储不可安装apkLog.d(TAG, Environment.isExternalStorageEmulated() + "");//8.返回结果:false 表示外部存储不可安装apkLog.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/MusicLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).toString());//11-2.返回结果:storage/emulated/0/PicturesLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString());//11-3.返回结果:storage/emulated/0/AlarmsLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS).toString());//11-4.返回结果:storage/emulated/0/DCIMLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString());//11-5.返回结果:storage/emulated/0/DocumentsLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString());//11-6.返回结果:storage/emulated/0/DownloadLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString());//11-7.返回结果:storage/emulated/0/MoviesLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).toString());//11-8.返回结果:storage/emulated/0/NotificationsLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS).toString());//11-9.返回结果:storage/emulated/0/PodcastsLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS).toString());//11-10.返回结果:storage/emulated/0/RingtonesLog.d(TAG, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES).toString());
<二>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/cacheLog.d(TAG, "getCacheDir()=" + this.getCacheDir().toString());//2.返回结果:/data/user/0/com.storage.demo/code_cacheLog.d(TAG, "getCodeCacheDir()=" + this.getCodeCacheDir().toString());//3.返回结果:没有支持的LEVEL24设备,就不写了// Log.d(TAG, "getDataDir()=" + this.getDataDir().toString());//4.返回结果:/data/user/0/com.storage.demo/databases/sjy.dbLog.d(TAG, "getDatabasePath()=" +this.getDatabasePath("sjy.db").toString());//5.返回结果:/data/user/0/com.storage.demo/app_sjy.dbLog.d(TAG, "getDir()=" + this.getDir("sjy.db", MODE_PRIVATE).toString());//6.返回结果:/storage/emulated/0/Android/data/com.storage.demo/cacheLog.d(TAG, "getExternalCacheDir()=" + this.getExternalCacheDir().toString());//7.返回结果:/storage/emulated/0/Android/data/com.storage.demo/cacheFile[] 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/DCIMLog.d(TAG, this.getExternalFilesDir(Environment.DIRECTORY_DCIM).toString());//9.返回结果:/storage/emulated/0/Android/data/com.storage.demo/files/MoviesFile[] 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.demoFile[] 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.dbLog.d(TAG, "getFileStreamPath()=" + this.getFileStreamPath("unknown.db").toString());//12.返回结果:/data/user/0/com.storage.demo/filesLog.d(TAG, "getFilesDir()=" + this.getFilesDir().toString());//13.返回结果:/data/user/0/com.storage.demo/no_backupLog.d(TAG, "getNoBackupFilesDir()=" + this.getNoBackupFilesDir().toString());//14.返回结果:/storage/emulated/0/Android/obb/com.storage.demoLog.d(TAG, "getObbDir()=" + this.getObbDir().toString());//15.返回结果:/storage/emulated/0/Android/obb/com.storage.demoFile[] 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());}
<三>辅助类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可用空间:2953MLog.d(TAG, "RAM可用空间:" + availableLong / 1024 / 1024 + "M");// RAM总空间:2718M//三星xplay6: RAM总空间:5696MLog.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.0MLog.d(TAG, "该进程最大分配内存:" + memory + "M" + "--" + maxMemory + "M");//该进程总内存:16.027634MLog.d(TAG, "该进程总内存:" + totalMemory + "M");//该进程剩余内存:4.1224976MLog.d(TAG, "该进程剩余内存:" + freeMemory + "M");}
<四>辅助类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.返回值:每一个存储块的大小尺寸: 4096Log.d(TAG, "每一个存储块的大小尺寸:" + blockSize);//2.返回值:该app外部存储可用的存储块总数:5148216Log.d(TAG, "该app外部存储可用的存储块总数:" + availableBlocksLongs);//3.返回值:剩余外部存储可用的存储块总数:5185080Log.d(TAG, "剩余外部存储可用的存储块总数:" + freeBlocksLong);//4.返回值: 外部存储总的存储块数量:14059344Log.d(TAG, "外部存储总的存储块数量" + BlocksLong);//5-1.返回值:外部存储应用程序可用空间=20110MLog.d(TAG, "外部存储app可用空间=" + statFs.getAvailableBytes() / 1024 / 1024 + "M");//5-2.返回值:外部存储应用程序可用空间=20110MLog.d(TAG, "外部存储app可用空间=" + blockSize * availableBlocksLongs / 1024 / 1024 + "M");//6-1.返回值: 外部存储系统可用空间20254MLog.d(TAG, "外部存储系统可用空间" + statFs.getFreeBytes() / 1024 / 1024 + "M");//6-2.返回值: 外部存储系统可用空间20254MLog.d(TAG, "外部存储系统可用空间" + blockSize*freeBlocksLong/ 1024 / 1024 + "M");//7-1.返回值: 外部存储总空间53GLog.d(TAG, "外部存储总空间" + statFs.getTotalBytes() / 1024 / 1024 / 1024 + "G");//7-2.返回值: 外部存储总空间53GLog.d(TAG, "外部存储总空间" + blockSize*BlocksLong/ 1024 / 1024 / 1024 + "G");
是不是看完此处,就可以写出一个工具类了呢!!!,那么,问题来了,小米标榜的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();
<三>外部存储的存储操作总结
外部存储,除了私有目录是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();}}}
输出自然是右图:
如何正确使用私有目录
上边的方法可以看到,外部存储创建很方便,一个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();}}}}}
给文件在Application的onCreate中初始化:
@Overridepublic void onCreate() {super.onCreate();//其他操作//初始化缓存路径FSFileUtils.initSavePath(getApplicationContext());}
最后,就是在任意地方调用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卡";}
通过反射的方式使用在sdk中被 隐藏 的类 StroageVolume 中的方法getVolumeList(),获取所有的存储空间(Stroage Volume),然后通过参数is_removable控制,来获取内部存储和外部存储(内外sd卡)的路径,参数 is_removable为false时得到的是内置sd卡路径,为true则为外置sd卡路径。
这部分不做多介绍,正常开发,内置外部存储足够使用,所以不建议再使用扩展卡,google也是这么推荐的。
结语:此处只简单介绍了API方法和其具体使用含义,底层逻辑实现并未讲解,但疏通了存储硬件和代码操作的关系,知其然更要知其所以然,真正的存储开发,还涉及更多操作,这只是皮毛,愿小伙伴们自得其乐!
github示例 点击跳转
android开发 开篇总结,多多关照
这篇关于android基础总结-内部存储和外部存储的大局观的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!