performclick:Android中的内部存储和外部存储

 2021-07-09 8:11    77  

内部存储和外部存储的概念随着Android版本的更新也在发生不断的变化。最早的内部存储指的是系统自带的ROM存储performclick,外部存储指的是外置的Sdcard或者通过OTG挂在的USB存储。随着硬件价格越来愈低,系统内置的ROM也越来越大,现在即使是一台千元机,一般存储容量也是在16GB以上。所以在Android 4.4以后,系统将内部存储分为了Internal Storage和External Storage。而原来的外置Sdcard或者通过OTG挂载的USB,也都是属于External Storage。

performclick:Android中的内部存储和外部存储

内部存储(Internal File Storage)内部存储指的是应用内部独有的存储,这部分存储的文件performclick、数据,只能被应用自身访问到,其他应用都没有权限访问。

performclick:Android中的内部存储和外部存储

一般情况下,/data开头的路径都是Internal Storageperformclick。而一般应用所能够访问到的就是下面几个路径,称为应用内部私有存储。

performclick:Android中的内部存储和外部存储

应用内部私有存储:

/data/user/0/<包名>

/data/user/0/<包名>/files #存放文件数据

/data/user/0/<包名>/databases #存放Sqlite的数据库文件

/data/user/0/<包名>/shared_prefs #存放SharedPreference的数据

/data/user/0/<包名>/cache #存放缓存文件

需要注意的是,在支持多用户之前,应用内部私有存储的路径是/data/data/<包名>,在Android L系统引入多用户功能之后,每个用户对于每个应用都会有一个独立的存储空间,而这个路径等价于机主用户的应用存储空间也就是/data/user/0/<包名>。其他用户比如访客用户,就会以/data/user/10/<包名>路径进行存储。

由于应用内部私有存储的路径都是与应用包名强相关的,所以在应用内部访问这些路径都是通过Context相关API去访问的。

应用内部私有存储的路径和API对应关系

应用内部私有存储的特点

存储永远都是处于可用状态的只有App自己才能够访问保存的文件一旦App被卸载,系统将会移除内部存储中相关应用的数据文件读写实例

/data/user/0/<包名>/files 下面文件的读写,可以直接通过Context#openFileOutput写入 如下面的代码,在/data/user/0/<包名>/files 目录下,创建myfile文件,可以通过context.getFilesDir获取

String filename = "myfile";String fileContents = "Hello world!";FileOutputStream outputStream;try { outputStream = openFileOutput(filename, Context.MODE_PRIVATE); outputStream.write(fileContents.getBytes()); outputStream.close();} catch (Exception e) { e.printStackTrace();}存储一个内部cache文件

private File getTempFile(Context context, String url) { File file; try { String fileName = Uri.parse(url).getLastPathSegment(); file = File.createTempFile(fileName, null, context.getCacheDir()); } catch (IOException e) { // Error while creating file } return file;}外部存储(External File Storage)外部存储指的是是公共的存储,这部分存储理论上是全局可见的,所有的应用都可以访问这部分数据,一般情况下,路径都是以/storage开头的,比如说/storage/emulated/0就是属于外部存储,这个路径的实际的挂载点是/data/media。又比如外置sdcard的路径为/storage/13FC-0F0B。 相比较内部存储一定会存在,外部存储可能是sdcard或者通过otg挂载的U盘形式,所以可能出现没有挂载的情况,所以所有的外部存储要在使用前通过下面的方式判断是否有被挂载。

检查外部存储是否可读写

/* Checks if external storage is available for read and write */public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { return true; } return false;}检查外部存储是否可读

/* Checks if external storage is available to at least read */public boolean isExternalStorageReadable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { return true; } return false;}外部存储可以分为两个部分,一个是外部应用私有存储,另外一个是外部公共存储。

应用访问外部存储需要通过下面的方式在AndroidManifest.xml中申请动态权限,其中WRITE_EXTERNAL_STORAGE包含了读跟写的权限。不过在Android 4.4之后,访问外部应用私有存储不需要进行权限的申请。

<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ...</manifest>应用外部私有存储这部分存储的所有权属于应用,一般不会主动向外部提供数据,虽然应用可以通过类似绝对路径这样的方式访问到。 这部分数据会在应用卸载时会被清除,主要涉及的路径见下面的表格:

应用外部私有存储与API的对应关系

存储到私有路径实例 可以通过在文件夹目录下添加.nomedia防止该目录下的文件被MediaScanner扫描到

public File getPrivateAlbumStorageDir(Context context, String albumName) { // Get the directory for the app's private pictures directory. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file;}外部公有存储这部分存储一般指的是/storage/emulated/0下面也就是/data/media挂载出来的)除去/storage/emulated/0/Android之外的所有路径,比如storage/emulated/0/Pictures这些文件夹。也包括外置sdcard,通过OTG挂载USB出来的存储,一般路径为/storage/xxxx-xxxx.

外部共有存储的路径跟应用本身没有关系,所以一般都是通过Environment类进行获取。而这些存储的数据也不会随着应用的卸载而丢失。

外部公有存储与API的对应关系

获取图片公有目录,并在内部创建 albumName 文件夹

public File getPublicAlbumStorageDir(String albumName) { // Get the directory for the user's public pictures directory. File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file;}应用清除数据和清除缓存

在设置->应用->应用详情->存储页面下面有”清除数据“与”清除缓存”两个菜单,点击之后分别删除了哪些文件呢?

清除数据这个操作将会清除/data/user/0/<包名>/ /storage/emulated/0/Android/data/<包名>/ 权限授予数据清除缓存 点击清除缓存之后,将会对下面路径下的缓存文件进行清除/data/user/0/<包名>/cache /storage/emulated/0/Android/data/<包名>/cache关于外置Sdcard

外置Sdcard由于传输速率等原因,虽然到目前还支持,但也逐渐开始被Google放弃,很多厂商也开始不在支持外置Sdcard。

外置SDcard在Android M之后支持两种挂载方式

Portable Storage

这种挂载方式是Android最早支持的外置sdcard的方式,sdcard存粹作为可移动存储的方式进行存储,一般用来存储照片、音乐这样的多媒体文件。外置存储将会以/mnt/media_rw/的形式挂载到系统中

Filesystem Size Used Avail Use% Mounted on

… …

/data/media 24G 3.9G 20G 17% /mnt/runtime/default/emulated

/dev/block/vold/public:179,129 941M 64K 941M 1% /mnt/media_rw/35B6-0CFA

/mnt/media_rw/35B6-0CFA 941M 64K 941M 1% /mnt/runtime/default/35B6-0CFA

如果通过File接口直接在外置sdcard路径下去创建文件,会报下面的错误。

08-10 20:51:04.033 24575 24575 W System.err: java.io.IOException: Permission denied

08-10 20:51:04.033 24575 24575 W System.err: at java.io.UnixFileSystem.createFileExclusively0(Native Method)

08-10 20:51:04.034 24575 24575 W System.err: at java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:281)

08-10 20:51:04.034 24575 24575 W System.err: at java.io.File.createNewFile(File.java:1008)

08-10 20:51:04.034 24575 24575 W System.err: at com.ryan.demostore.storagedemo.StorageDemoFragment$17.onClick(StorageDemoFragment.java:337)

08-10 20:51:04.034 24575 24575 W System.err: at android.view.View.performClick(View.java:6597)

08-10 20:51:04.034 24575 24575 W System.err: at android.view.View.performClickInternal(View.java:6574)

08-10 20:51:04.034 24575 24575 W System.err: at android.view.View.access$3100(View.java:778) 08-10 20:51:04.034 24575 24575 W System.err: at android.view.View$PerformClick.run(View.java:25885)

08-10 20:51:04.034 24575 24575 W System.err: at android.os.Handler.handleCallback(Handler.java:873)

08-10 20:51:04.035 24575 24575 W System.err: at android.os.Handler.dispatchMessage(Handler.java:99)

08-10 20:51:04.035 24575 24575 W System.err: at android.os.Looper.loop(Looper.java:193) 08-10 20:51:04.035 24575 24575 W System.err: at android.app.ActivityThread.main(ActivityThread.java:6669)

08-10 20:51:04.035 24575 24575 W System.err: at java.lang.reflect.Method.invoke(Native Method) 08-10 20:51:04.035 24575 24575 W System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)

08-10 20:51:04.036 24575 24575 W System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

在Android P之前,带有平台签名的应用在申请android.permission.WRITE_MEDIA_STORAGE权限之后,可以读写外置sdcard文件,而在Android P之后,由于权限策略的修改,即使是平台签名应用也无法读写外置sdcard。所有应用都需要通过DocumentFile的方式去读写。

Adoptable storage

这种挂载方式会讲外置sdcard格式化为加密的存储,替代原先ROM中挂载出来的/data/media,这种sdcard的数据无法被其他机器识别。如下面所示/mnt/expand/735f55e1-4a68-44aa-885c-e04c18cb8534/media将会代替原先/data/media成为系统中的Primary外置存储。

/dev/block/dm-2 24G 3.9G 20G 17% /data

/dev/block/dm-3 895M 1.6M 893M 1% /mnt/expand/735f55e1-4a68-44aa-885c-e04c18cb8534

/mnt/expand/735f55e1-4a68-44aa-885c-e04c18cb8534/media 895M 1.6M 893M 1% /mnt/runtime/default/emulated

所有的存储在data/media应用数据将会迁徙到sdcard上,同时原先ROM中的外部存储空间将会被废弃掉,只能被用户以应用的数据进行填充,无法被用户作为存储如MTP这样的形式使用。这种方式的体验并不好,因为在一般情况下,外置sdcard的读写速度是比不上ROM的存储。即使是EMMC的Flash,读写速度也要比一般的外置sdcard快的多,如果使用外置sdcard作为Primary存储,很容易造成系统的卡顿。

尾记

随着Android版本的更新,存储权限的控制越来越精细,就像在Android P上,应用无法通过file:///这的uri直接共享文件,否则会爆出FileUriExposedException的错误。

据说在Android Q平台,Google对于应用存储的权限做了比较大的变更。传统的 READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE 读写权限已经被更加细化的权限替代了,READ_MEDIA_IMAGES 和 READ_MEDIA_VIDEO 分别对应读取图片和视频,授权是会作为一组权限同时授予,而 READ_MEDIA_AUDIO 读取音频权限为单独一组授予,它们会控制应用通过 MediaStore 读取媒体的能力。(此段摘自 ://feng.moe/archives/47/)

注:本文同步自本人博客/2019/08/10/storage/

参考链接

://developer.android.com/guide/topics/data/data-storage

://feng.moe/archives/47/

本文标签:存储

原文链接:https://www.xgfox.com/alpx/692.html

本文版权:如无特别标注,本站文章均为原创。