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(); 
    }
}  
原文地址:https://www.cnblogs.com/pengjingya/p/5504791.html