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.返回结果: /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卡,也不单单是存储卡,请看下图(修改:外置存储器修改为外部存储):

这里写图片描述
开发者常困惑的地方就在内置存储器这一部分,内置存储器被分为两部分,内部存储(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调用 )
系统文件:/systemEnvironment.getRootDirectory()
缓存文件:/cacheEnvironment.getDownloadCacheDirectory()
用户数据:/dataEnvironment.getDownloadCacheDirectory()

2应用程序的内部存储路径:/data/user/0/包名/xxx
常用的方法:

应用程序的内部存储路径方法(全是Context调用)
内部缓存:/data/user/0/com.storage.demo/cachethis.getCacheDir()
SQLite路径:/data/user/0/com.storage.demo/databases/sjy.dbthis.getDatabasePath(“sjy.db”)
自定义内部存储文件:/data/user/0/com.storage.demo/app_sjy.dbthis.getDir(“sjy.db”, MODE_PRIVATE
自定义内部存储的file下文件:/data/user/0/com.storage.demo/files/unknown.dbthis.getFileStreamPath(“unknown.db”)
内部存储的file路径:/data/user/0/com.storage.demo/filesthis.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/XXXEnvironment.getExternalStoragePublicDirectory(type)

2常用私有目录

路径方法(全是Context操作)
缓存目录:/storage/emulated/0/Android/data/com.storage.demo/cachethis.getExternalCacheDir()
缓存目录数组:/storage/emulated/0/Android/data/com.storage.demo/cachethis.getExternalCacheDirs()
IO文件目录7个:/storage/emulated/0/Android/data/com.storage.demo/files/XXXthis.getExternalFilesDir(type)
IO文件目录数组7个/storage/emulated/0/Android/data/com.storage.demo/files/XXXthis.getExternalFilesDirs(type)

3常用自定义目录

路径方法(全是Environment操作)
根路径 :/storage/emulated/0Environment.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开发 开篇总结,多多关照

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在这个系列课程中,来自微软的权威技术专家将向您解释Windows操作系统的内部工作原理,从系统架构的大局观出发,逐步展示进程、线程、安全机制、内存管理和存储管理等子系统的工作方式。通过对底层原理的揭示,使您更进一步的理解Windows上各类程序的工作方式和如何进行错误诊断及性能优化。 本次课程的内容编排得到了国内知名技术作家,《Windows Internals》一书的中文译者,潘爱民先生的大力支持,同时TechNet也邀请到了众多微软一线技术专家进行讲解。这是一个为IT专业人员量身定做的Windows内部知识课程,在介绍原理的同时,也紧密地围绕实际案例和常见的故障进行分析点评。这是一个系统的学习Windows底层工作机制的好机会,课程内容深入浅出,精彩纷呈,绝对不容错过。 深入研究Windows内部原理系列之一:Windows的昨天、今天和明天 讲师信息:潘爱民 2007年01月25日 14:00-15:30 Level: 300 著名技术作家、微软亚洲研究院研究员潘爱民老师将在这次课程中跟听众分享Windows的发展历程和技术精萃,描绘操作系统的体系架构、Vista的内核变更以及今后版本Windows的发展趋势。 深入研究Windows内部原理系列之二:Windows体系结构-从操作系统的角度 讲师信息:张银奎 2007年01月26日 14:00-15:30 Level: 400 操作系统是计算机系统的灵魂和管理中心,也是软件系统中最复杂的部分。本讲座将以生动的讲解和丰富的演示带您领略Windows操作系统的核心架构和主要组件,包括HAL、内核、执行体、系统进程(IDLE、SMSS.EXE、WinLogon.EXE)和Windows子系统(CSRSS.EXE、WIN32K.SYS以及子系统DLL)等。并讨论中断管理、对象管理、和异常分发等系统机制和实现这些机制的基本数据结构。 深入研究Windows内部原理系列之三:Windows体系结构-从应用程序的角度 讲师信息:曾震宇 2007年01月29日 14:00-15:30 Level: 400 从服务器软件到Office办公应用,从联网游戏到即时消息,不管这些应用的复杂程度如何,他们都是一个个在操作系统控制和管理之下的可执行程序。本次课程邀请微软全球技术中心专家级工程师,为各位讲解一个程序是如何经历从启动、分配资源、运行、结束这一连串的过程,并且介绍其中的重要概念和排错诊断技巧。 深入研究Windows内部原理系列之四:Windows操作系统中的重要基本概念 讲师信息:高宇 2007年01月30日 14:00-15:30 Level: 400 进程、线程、资源分配、内存管理、Win32 API、服务、安全,这些是工作中常常提及但是又无法深入理解的神秘概念。在这次课程中,讲师将介绍Windows中最常见与最重要的一些基本概念. 使大家能够顺利地参与到本系列之后的讨论中去。 深入研究Windows内部原理系列之五:Windows Sysinternals工具集介绍 讲师信息:彭爱华 2007年01月31日 14:00-15:30 Level: 400 Sysinternals Suite(Windows Sysinternals工具集)包含一系列免费的系统工具,其中有大名鼎鼎的Process Explorer、FileMon、RegMon等(在Windows Vista下,FileMon和RegMon则被Process Monitor所代替),如果把系统管理员比喻成战士的话,那么Sysinternals Suite就是我们手中的良兵利器。熟悉和掌握这些工具,并且对Windows的体系有一定的了解,将大幅度的提高日常的诊断和排错能力。本课程将以任务驱动的模式,介绍几个经典的应用案例,来介绍Sysinternals Suite的强大功能。 深入研究Windows内部原理系列之六:Vista新特性底层揭秘 讲师信息:彭爱华 2007年02月01日 14:00-15:30 Level: 400 Windows Vista绝非仅仅是具有诸如3D切换、毛玻璃等炫目的界面效果,花钱购买了Windows Vista,而仅仅为了使用其界面效果,难免有点“买椟还珠”的感觉。实际上Windows Vista值得称道的是它具有很多全新的安全特性,例如用户帐户控制、IE保护模式、服务隔离和Windows资源保护等等。有了这些全新的安全特性,我们就可以在相当的程度上摆脱恶意软件的滋扰。Windows之父Jim Allchin曾经说过不要满足于只知道How-to、小技巧之类的知识,而是应该深入底层了解其内部原理。只有了解了这些安全特性的内在原理,才
列车长是铁路运输中的一个重要职位,他们负责管理和领导列车运行,确保旅客和货物的安全和顺利运输。在工作中,列车长需要具备丰富的专业知识和技能,同时还需要具备较强的大局意识。下面,本文将从不同方面探讨列车长的大局意识的体现。 一、安全第一 作为列车运输的管理者,列车长必须时刻保持安全第一的意识。在列车长的工作中,他们需要严格遵守铁路交通安全规定,把安全工作放在首位,坚持以人民群众生命财产安全为中心,确保旅客和货物的安全运输。在遇到突发情况时,列车长需要迅速反应,采取果断措施,及时保护旅客和货物的安全,避免事故的发生。 二、紧急情况的处置 列车长在工作中需要具备处理紧急情况的能力,必须能够快速反应,及时处理突发事件,保障旅客和货物的安全。例如,在列车行驶过程中,如果发现火灾、车辆故障等紧急情况,列车长需要迅速采取措施,通知相关部门,组织乘务人员和旅客疏散,及时处理突发事件。 三、对乘务人员的管理 列车长是乘务人员的领导者,需要对乘务人员进行培训和管理,确保他们严格遵守铁路交通安全规定,保障旅客和货物的安全。在工作中,列车长需要关注乘务人员的工作状态和工作效率,及时解决问题,提高工作效率。同时,列车长还需要对乘务人员进行带教和培训,提高他们的专业知识和技能水平。 四、对车辆的管理 列车长需要对车辆进行管理,确保车辆的运行安全和顺畅。在工作中,列车长需要对车辆进行定期检查,发现问题及时处理,保障车辆的正常运行。此外,列车长还需要对车辆进行维护和保养,确保车辆的性能和使用寿命。 五、对旅客的服务 列车长在工作中需要关注旅客的需求和服务,提供优质的服务,确保旅客的出行体验。列车长需要关注旅客的安全和舒适度,关注旅客的饮食和休息等需求,提供周到的服务。此外,列车长还需要关注旅客的意见和建议,及时改进服务,提高旅客的满意度。 六、对铁路运输大局的把握 列车长需要具备较强的大局意识,关注铁路运输的整体发展情况,了解国家铁路运输政策和规划,把握铁路运输的大势和趋势。在工作中,列车长需要根据铁路运输的发展情况,制定合理的运输计划,提高运输效率和运输能力,推动铁路运输的发展。 总之,列车长是铁路运输中的重要职位,他们需要具备丰富的专业知识和技能,同时还需要具备较强的大局意识。只有不断提高自身素质,关注铁路运输的整体发展情况,才能更好地完成工作任务,推动铁路运输的发展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值