安卓内部外部文件存储
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓内部外部文件存储相关的知识,希望对你有一定的参考价值。
参考技术A 总述觉得十分有必要搞清楚内存,内部存储和外部存储的区别,还有我们在开发中真正将数据存在了手机的哪儿。
先提一个问题:手机设置的应用管理中,每个App下都有清除数据和清除缓存,清除的分别是哪里的数据?
一 内存,内部存储和外部存储
整个存储空间分为内部存储和外部存储两部分,内部存储中又包含RAM和ROM等部分。
内部存储,即InternalStorage,也常说内置存储卡,这是手机内置的存储空间,出厂时就被确定,是手机的一个硬件指标。类比电脑的内存。
外部存储,即ExternalStorage,也常说外置存储卡,手机出厂时并不存在,是由用户自由扩展的存储空间,常见的就是SD卡。类比电脑的外接移动硬盘。
RAM,即常说的运行时内存,是手机运行时存储数据和指令的地方,注意是运行时内存。类比电脑的内存条。
ROM,用来存放一些系统文件,应用配置和其他数据的地方,是内部存储中主要存储区域。类比于Windows电脑的C盘。
上图中运行内存即RAM,共2G,机身存储即内部存储,共8G。
打开手机的文件管理,再点击手机一项,可以看到下图:
上图中箭头所指的地方,可以进行内部存储与外部存储的切换,当前页为内部存储下的展示,这只是Android系统存储数据的一部分而已,大部分数据都对用户隐藏。至于红框部分下面继续讲。
二 详说存储
一般来说,App软件大都安装在内部存储的ROM区,当然现在也有办法安装在SD卡上,但这需要App自身支持及SD卡分区等一系列操作,下面讨论的App是安装在ROM区的。
以Genymotion模拟器设备为例来分析,型号是三星GalaxyS5。直接使用AndroidStudio的DDMS,打开File Explorer,我们可以看到下图:
这张图是手机根目录下的完整目录结构图,内外部存储均包含在内。不过有些文件内容需要root权限才能看到。
上面展示的内部存储图就是其中一部分子目录。
选其中几个重要的文件夹介绍。
2.1 /data包:主要存储手机应用的相关数据。
如上图的二级目录中,/data/app文件夹下存放三方应用的apk文件;/data/data文件夹下存放系统应用和三方应用的包私有数据,每个应用都有独属于自己的包。
选一个三方应用包——com.X.main,来分析三级目录——/data/data/com.X.main下都有什么数据:
由包名不难看出:cache包存放缓存数据,databases包存放使用SQLite存储的数据,files包存放普通数据(log数据,json型数据等),shared_prefs包存放使用SharedPreference存放的数据。这些包都是由系统创建的。
2.2 /mnt/sdcard/storage包:这三个包,与手机的部分内置存储卡数据和外置存储卡数据有关。
上面的完整目录结构图中有其子目录信息展示。
以内置存储卡来说,通常用sdcard0表示:Android4.1上,首先挂载到目录/storage/sdcard0上面,/sdcard和/mnt/sdcard都只是指向/storage/sdcard0的软链接;Android4.2上,首先挂载到目录/storage/emulated/0(0就表示内置存储)上面,为兼容之前版本,又挂载到/storage/emulated/legency上面,/storage/sdcard0、/sdcard和/mnt/sdcard都只是指向/storage/emulated/legency的软链接。(挂载相当于真正位置,软链接相当于指针)。
/mnt/sdcard是Android2.2及之上版本使用,/sdcard是Android2.1及之下版本使用。
在手机的文件管理中看到的内置存储卡文件,如上面文件管理页面的图,就是/storage/emulated /0包的子目录,Android包的路径就是:/storage/emulated/0/Android。
2.3 其他
/dev包:Linux系统的常规文件夹。
/system包:系统配置的文件夹,比如Android系统框架(framework)、底层类库(lib)、字体(font)等。
三 存储相关操作
在Android开发中,我们常打交道的存储空间有三部分。
一是根目录下路径为 /data/data/包名/XX 的文件。开发中SQLite数据、SharedPreference数据均保存在这里,虽说我们可以读写操作,但这部分空间由系统维护。
二是在外置存储卡上做存储。暂时不讲。
三是在内置存储卡中做存储。在/storage/emulated/0/Android/data包下或与/storage/emulated/0/Android包同级目录上,建立App包存储数据,这部分空间均由开发者维护。区别在于/storage/emulated/0/Android/data包下的数据为私有目录数据,会随App卸载被清除,与/storage/emulated/0/Android包同级的数据(如系统目录DCIM包,DOWNLOWN包和bluetooth包,还有下图中的baidu包)属于公有目录数据,不会随App卸载被清除,这就会造成数据的卸载残留。
Google官方建议开发者将App的数据存储在私有目录即/storage/emulated/0/Android/data包下,这样卸载App时数据会随之被系统清除,不会造成数据残留。
对存储空间进行操作,首先要获取存储空间的存储路径,对此Android提供了Environment类和Context类来获取路径。
就上面对存储空间的划分,第一部分空间对用户不可见,是在具体包名下的,和特定的App有关,所以对这些数据的访问需调用Context类中的方法;第三部分空间对用户可见,私有目录数据仍与特定App有关,需调用Context类中的方法,而公有目录数据与App无关,应调用Environment类中的方法。
总结如下图:
写了个小程序对Environment类和Context类相关方法测试,如下图:(冒号前为方法名,冒号后为输出结果)
四 回答最初提出的问题
手机设置的应用管理中,每个App下都有清除数据和清除缓存,清除的分别是哪里的数据?
还是用上面的Genymotion模拟器设备来分析,主要测试三部分数据:
内部数据:/data/data/包名/XXX
外部私有数据:/storage/emulated/0/Android/data/包名/XXX
外部公有数据:/storage/emulated/0/包名/XXX
测试结果图就不上了,直接上结论:
清除缓存:将外部私有数据下的cache包(/storage/emulated/0/Android/data/包名/cache)清除,将内部数据下的cache包下的内容(/data/data/包名/cache/XXX)清除 。
清楚数据:将外部私有数据包(/storage/emulated/0/Android/data/包名)清除,将内部数据下的所有内容(/data/data/包名/XXX)清除;
而两种操作对外部公有数据均无影响。
如何在 Flutter 中从内部和外部存储中获取所有 PDF 文件?
【中文标题】如何在 Flutter 中从内部和外部存储中获取所有 PDF 文件?【英文标题】:How to get all PDF files from internal as well as external storage in Flutter? 【发布时间】:2018-08-30 04:43:14 【问题描述】:我想显示所有存在于内部和外部存储中的 pdf 文件,所以在点击该特定文件时,我想在全屏对话框中打开该文件。
【问题讨论】:
在 Android 和 iOS 上都可以吗?您正在使用“内部”和“外部”关键字,所以我猜您正在考虑“Android 平台”。您是否打算仅在 Android Platorm 上使用您的应用程序?在 iOS 上,您只能访问您的应用程序沙箱“内部”存储。那么你的具体情况是什么? 暂时只寻找Android平台,但如果你有两个解决方案,那就太好了! :) 【参考方案1】:我将此代码用于列表文件和目录
Future<List<FileSystemEntity>> dirContents(Directory dir)
var files = <FileSystemEntity>[];
var completer = Completer<List<FileSystemEntity>>();
var lister = dir.list(recursive: false);
lister.listen((file) async
FileStat f = file.statSync();
if (f.type == FileSystemEntityType.directory)
await dirContents(Directory(file.uri.toFilePath()));
else if (f.type == FileSystemEntityType.file && file.path.endsWith('.pdf'))
_files.add(file);
, onDone: ()
completer.complete(files);
setState(()
//
);
);
return completer.future;
Directory dir = Directory('/storage/emulated/0');
var files = await dirContents(dir);
print(files);
【讨论】:
【参考方案2】:因此,为了做到这一点,您需要:
授予对 PDF 文件所在目录中外部存储的访问权限。我们称该文件夹为<external storage>/pdf
。
列出该目录的所有文件并向用户显示。
使用可以可视化 PDF 的应用程序打开所选文件。
为了做所有我认为我建议你使用那些颤振包:
path_provider simple_permission通过path_provider可以获取Android设备的外部存储目录。
Directory extDir = await getExternalStorageDirectory();
String pdfPath = extDir + "/pdf/";
为了访问外部存储,您需要在ApplicationManifest.xml
中设置此权限请求:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
您也可以只使用READ_EXTERNAL_STORAGE
,但simple_permission 插件将不起作用。
使用 simple_permission 插件,您可以请求用户授予外部存储访问权限:
bool externalStoragePermissionOkay = false;
_checkPermissions() async
if (Platform.isAndroid)
SimplePermissions
.checkPermission(Permission.WriteExternalStorage)
.then((checkOkay)
if (!checkOkay)
SimplePermissions
.requestPermission(Permission.WriteExternalStorage)
.then((okDone)
if (okDone)
debugPrint("$okDone");
setState(()
externalStoragePermissionOkay = okDone;
debugPrint('Refresh UI');
);
);
else
setState(()
externalStoragePermissionOkay = checkOkay;
);
);
一旦我们被授予外部存储访问权限,我们就会列出我们的 PDF 目录:
List<FileSystemEntity> _files;
_files = dir.listSync(recursive: true, followLinks: false);
并在 ListView 中显示它们:
return new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: _files.length,
itemBuilder: (context, i)
return _buildRow(_files.elementAt(i).path);
);
当用户点击它们时,您必须使用查看器打开它们。
要做到这一点并不容易,因为在 Android 中,我们需要构建一个 ContentUri 并将对该 URI 的访问权限提供给外部应用程序查看器。
所以我们在 Android 中这样做,我们使用flutter platform channels 来调用 Android 原生代码。
飞镖:
static const platform =
const MethodChannel('it.versionestabile.flutterapp000001/pdfViewer');
var args = 'url': fileName;
platform.invokeMethod('viewPdf', args);
本机 Java 代码:
public class MainActivity extends FlutterActivity
private static final String CHANNEL = "it.versionestabile.flutterapp000001/pdfViewer";
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler()
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result)
if (call.method.equals("viewPdf"))
if (call.hasArgument("url"))
String url = call.argument("url");
File file = new File(url);
//*
Uri photoURI = FileProvider.getUriForFile(MainActivity.this,
BuildConfig.APPLICATION_ID + ".provider",
file);
//*/
Intent target = new Intent(Intent.ACTION_VIEW);
target.setDataAndType(photoURI,"application/pdf");
target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
target.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(target);
result.success(null);
else
result.notImplemented();
);
毕竟我们可以拥有我们的 PDF 列表并在 Android 上查看。
你有很多东西要学。我希望这对你来说是一个有用的游乐场。
这是用于外部存储,但您也可以获取内部和临时目录,并与此处类似。
如果你想在 iOS 上做同样的事情,你需要在 iOS 项目上创建相同的 Native Code pdfViewer
。请始终参考flutter platform channels 以执行此操作。请记住,iOS 设备上不存在外部存储。因此,您可以只使用应用程序沙箱文件夹或临时文件夹。
GitHub repo.
编码愉快。
【讨论】:
以上是关于安卓内部外部文件存储的主要内容,如果未能解决你的问题,请参考以下文章