zoukankan      html  css  js  c++  java
  • 02、Android--文件存储

    基本存储

    Android文件的存储有很多种方式,下面会统一进行介绍。

    下面我们再来具体介绍相关知识和内容。

    内部存储

    内部存储,位于data/data/包名/路径下
    是否需要用户权限:否
    是否能被其他应用访问:否
    卸载应用数据是否被删除:是

    内部存储控件不需要用户权限,这意味着我们不需要用户去授权。

    设备为每个安装的App在data/data目录下创建以应用程序包名对应的文件夹,可以直接读写和操作,且不能被其他应用所访问,保证内部存储文件的安全性和隐私性。

    我们可以通过工具查看对应应用的存储文件,它包含如下目录:

    /data/data/应用名/cache :存放的是APP的缓存信息

    /data/data/应用名/code_cache :在运行时存放应用产生的编译或者优化的代码

    /data/data/应用名/files : 存放APP的文件信息

    其中,SharedPrefersences和SQLite数据库都是存储在内部空间上的,可以通过Context来获取和操作:

    getFilesDir().getAbsolutePath() : /data/user/0/package/files

    getCacheDir().getAbsolutePath() :/storage/emulated/0/Android/data/package/cache

    getDir(“myFile”, MODE_PRIVATE).getAbsolutePath() : /data/user/0/package/app_myfile

    getCodeCacheDir().getAbsolutePath() : /data/user/0/package/code_cache (> Android 5.0)

    注意:内部存储空间十分有限,所以在App的开发过程中要尽量避免使用。

    外部存储

    在Android 4.4(API 19)以前,手机自带的存储卡就是内部存储,而拓展的SD卡则是外部存储。

    在Android 4.4以后,将手机机身存储在概念上分为:"内部存储internal" 和 "外部存储external" 。

    其中SD卡也是外部存储,为了区分机身存储和SD卡等外部存储方式,在4.4以后的系统中提供下面的API来遍历手机的外部存储路径:

    File[] files;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        files = getExternalFilesDirs(Environment.MEDIA_MOUNTED);
        for(File file:files){
            Log.e("main",file);
        }
    }
    

    如果手机具备SD卡的话,则会打印两条数据,代表机身存储和SD卡存储的路径:

    /storage/emulated/0/Android/data/packname/files/mounted(机身存储路径)
    /storage/B3E4-1711/Android/data/packname/files/mounted(SD卡存储路径)
    

    一般对于外部存储可以分为两类,外部公有和外部私有(私有的都会随APP卸载而删除,公有的则不会)。

    外部公有

    公共目录必须需要用户授权读写的权限,这意味着我们需要在 AndroidManifest.xml 中注册用户权限。

    <!-- 外部存储写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    

    并且在 Android 6.0 系统之后需要申请用户权限,并获得用户授权,才能读写文件。

    可以通过Environment 对象,访问读写公共目录的文件,在读写时应先判断外部存储的状态,是否能够支持读写操作。

    Environment.getExternalStorageState() 
    /**       {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
     *        {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING},
     *        {@link #MEDIA_NOFS}, {@link #MEDIA_MOUNTED},
     *        {@link #MEDIA_MOUNTED_READ_ONLY}, {@link #MEDIA_SHARED},
     *        {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}
     */
    

    只有在返回值为 MEDIA_MOUNTED 表示当前是可正常读写的。接下来看看相关的API:

    1. Environment.getExternalStorageDirectory() : /storage/emulated/0
    2. Environment.getExternalStoragePublicDirectory(String type): /storage/emulated/0/xxx  
    

    其中type字段的取值如下:

    Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS).getAbsolutePath()  : /storage/emulated/0/Documents
    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath() : /storage/emulated/0/Music
    

    上述的API在Android 10(API 29)基础上已经无法使用,替代方案如下:

    File storageDirectory = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    

    外部私有

    私有目录,在 Android4.4 系统以上。不需要注册和用户授权外部私有存储的读写的权限,就可以在应用的外部私有进行读写文件。并且文件不能被其他应用所访问,具有较好的隐私性和安全性。

    私有目录地址:/storage/emulated/0/Android/data/应用包名

    私有目录相关的API如下所示:

    getExternalCacheDir().getAbsolutePath()  
    // /storage/emulated/0/Android/data/package/cache
    getExternalFilesDir("mytest").getAbsolutePath() 
    // /storage/emulated/0/Android/data/package/files/mytest
    getExternalFilesDir(null).getAbsolutePath()  
    // /storage/emulated/0/Android/data/package/files
    

    Sharedpreferences

    SharedPreferences是轻量级的存储类,它是用xml文件存放数据,文件存放在/data/data//shared_prefs目录下:

    // 存储数据
    String fileName = "legend";
    SharedPreferences sp = getSharedPreferences(fileName, Context.MODE_PRIVATE);
    Editor edit = sp.edit();
    edit.putString("name", "zhangsan");
    edit.commit(); 
    
    // 读取数据
    String fileName = "legend";
    SharedPreferences sp = getSharedPreferences(fileName, Context.MODE_PRIVATE);
    String name = sp.getString(fileName, null);
    

    注意:私有目录下的shared_prefs文件夹里面,路径为/data/data/包名/shared_prefs。

    模式解析

    SharedPreferences有四种操作模式,它们分别是:

    模式 描述
    MODE_APPEND 追加方式存储
    MODE_PRIVATE 私有方式存储,其他应用无法访问
    MODE_WORLD_READABLE 表示当前文件可以被其他应用读取
    MODE_WOELD_WRITEABLE 表示当前文件可以被其他应用写入

    如果想要访问其他应用中的Preference必须满足被访问的应用的Perference创建时指定可读或可写的权限。

    其中两个提交的方法:apply和commit的区别如下:

    apply没有返回值,而commit反馈boolean类型表明修改是否提交成功。

    apply是将修改数据提交到内存,而后异步真正提交到硬件磁盘,而commit是同步提交到硬件磁盘、

    在多个并发提交commit时,会等待正在处理的commit保存到磁盘后再操作,效率低。

    而apply只是的提交到内容,后面调用apply将会直接覆盖前面的内存数据,效率高。

    如果对结果不关心的话,推荐使用apply,关心结果则使用commit。

    操作封装

    在实际的项目开发中,我们一般会将Sharedpreferences进行封装,以此达到快速使用的目的。

    public class SPUtils {
        public static final String FILE_NAME = "share_data";
        /**保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法*/
        public static void put(Context context, String key, Object object) {
            SharedPreferences sp = context.getSharedPreferences(FILE_NAME,Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = sp.edit();
            if (object instanceof String) {
                editor.putString(key, (String) object);
            } else if (object instanceof Integer) {
                editor.putInt(key, (Integer) object);
            } else if (object instanceof Boolean) {
                editor.putBoolean(key, (Boolean) object);
            } else if (object instanceof Float) {
                editor.putFloat(key, (Float) object);
            } else if (object instanceof Long) {
                editor.putLong(key, (Long) object);
            } else {
                editor.putString(key, object.toString());
            }
            SharedPreferencesCompat.apply(editor);
        }
        /**得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值*/
        public static Object get(Context context, String key, Object defaultObject) {
            SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
            if (defaultObject instanceof String) {
                return sp.getString(key, (String) defaultObject);
            } else if (defaultObject instanceof Integer) {
                return sp.getInt(key, (Integer) defaultObject);
            } else if (defaultObject instanceof Boolean) {
                return sp.getBoolean(key, (Boolean) defaultObject);
            } else if (defaultObject instanceof Float) {
                return sp.getFloat(key, (Float) defaultObject);
            } else if (defaultObject instanceof Long) {
                return sp.getLong(key, (Long) defaultObject);
            }
            return null;
        }
        /**移除某个key值已经对应的值*/
        public static void remove(Context context, String key) {
            SharedPreferences sp = context.getSharedPreferences(FILE_NAME,Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = sp.edit();
            editor.remove(key);
            SharedPreferencesCompat.apply(editor);
        }
        /**清除所有数据*/
        public static void clear(Context context) {
            SharedPreferences sp = context.getSharedPreferences(FILE_NAME,Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = sp.edit();
            editor.clear();
            SharedPreferencesCompat.apply(editor);
        }
        /**查询某个key是否已经存在*/
        public static boolean contains(Context context, String key) {
            SharedPreferences sp = context.getSharedPreferences(FILE_NAME,Context.MODE_PRIVATE);
            return sp.contains(key);
        }
        /**返回所有的键值对*/
        public static Map<String, ?> getAll(Context context) {
            SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
            return sp.getAll();
        }
        /**创建一个解决SharedPreferencesCompat.apply方法的一个兼容类*/
        private static class SharedPreferencesCompat {
            private static final Method sApplyMethod = findApplyMethod();
            /**反射查找apply的方法*/
            @SuppressWarnings({ "unchecked", "rawtypes" })
            private static Method findApplyMethod() {
                try {
                    Class clz = SharedPreferences.Editor.class;
                    return clz.getMethod("apply");
                } catch (NoSuchMethodException e) {
                }
                return null;
            }
            /**如果找到则使用apply执行,否则使用commit*/
            public static void apply(SharedPreferences.Editor editor) {
                try {
                    if (sApplyMethod != null) {
                        sApplyMethod.invoke(editor);
                        return;
                    }
                } catch (Exception e) {
                }
                editor.commit();
            }
        }
    }
    

    资源目录

    assets文件夹里面的文件都是保持原始的文件格式,需要用AssetManager以字节流的形式读取文件。

    先在Activity里面调用getAssets() 来获取AssetManager引用。

    再用AssetManager的open(String fileName, int accessMode) 方法则指定读取的文件以及访问模式就能得到输入流InputStream。

    然后就是用已经open file 的inputStream读取文件,读取完成后记得inputStream.close() 。

    调用AssetManager.close() 关闭AssetManager。

    需要注意的是,来自Resources和Assets 中的文件只可以读取而不能进行写的操作以下为raw文件中读取:

    public String getFromRaw(){ 
        try { 
            InputStreamReader inputReader = new InputStreamReader(getResources().openRawResource(R.raw.test1));
            BufferedReader bufReader = new BufferedReader(inputReader);
            String line="";
            String Result="";
            while((line = bufReader.readLine()) != null)
                Result += line;
            return Result;
        } catch (Exception e) { 
            e.printStackTrace(); 
        }             
    }  
    

    以下为assets文件中读取:

    public String getFromAssets(String fileName){ 
        try { 
            InputStreamReader inputReader = new InputStreamReader(getResources().getAssets().open(fileName) ); 
            BufferedReader bufReader = new BufferedReader(inputReader);
            String line="";
            String Result="";
            while((line = bufReader.readLine()) != null)
                Result += line;
            return Result;
        } catch (Exception e) { 
            e.printStackTrace(); 
        }
    }  
    
  • 相关阅读:
    caffe_实战之两个简单的例子(物体分类和人脸检测)
    《Caffe下跑AlxNet之数据处理过程》
    git使用小结
    说好的博客
    C++入门学习
    第一篇 一步一步看透C++
    第一百六十三节,jQuery,基础核心
    第一百六十二节,jQuery入门介绍
    第一百六十一节,封装库--JavaScript,完整封装库文件
    第一百六十节,封装库--JavaScript,ajax注册表单到数据库
  • 原文地址:https://www.cnblogs.com/pengjingya/p/5504791.html
Copyright © 2011-2022 走看看