研究并了解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());

<二>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());        }

<三>辅助类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");    }

<四>辅助类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");

是不是看完此处,就可以写出一个工具类了呢!!!,那么,问题来了,小米标榜的64G,为什么存储总空间只有53G呢?看下面就知道了

二 开发者进阶总结

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

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

android基础总结-内部存储和外部存储的大局观_第1张图片
开发者常困惑的地方就在内置存储器这一部分,内置存储器被分为两部分,内部存储(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中初始化:

 @Override    public 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开发 开篇总结,多多关照

更多相关文章

  1. Android中访问sdcard路径的几种方式
  2. 使用Android内置的Pull解析器解析XML文件
  3. android moudle 资源文件重命名
  4. CrossWalk - android 动态加载so库文件实践
  5. Android中使用pull解析器操作xml文件的解决办法
  6. rdp文件和vnc软件
  7. Android so 文件进阶 从dlsym()源码看android 动态链接过程
  8. Android使用JNI生成.so文件并调用(使用传统生成.h的方法)

随机推荐

  1. Android(安卓)对话框 (二)ProgressDialog
  2. Android自定义相机实现自动对焦和手动对
  3. android 上传文件到服务器代码实例
  4. 一份关于 Java、Kotlin 与 Android(安卓)
  5. android 搭建开发环境
  6. 移动3g为什么这么坑爹
  7. 二、Android(安卓)NDK编程预备之Java jni
  8. ListView 使用详解
  9. android Service与BroadcastReceiver
  10. android 电话状态的监听(来电和去电)