为什么样很多 Android 程序喜欢在存储卡根目录建文件夹来存储数据而不是 Android/data 目录下?Android 系统不释放内存么

发表时间:2017-12-29 02:00:02 作者: 来源: 浏览:

在上一篇文章中,小编为您详细介绍了关于《iOS7 正式版在 iPhone 4s 上运行出现的bug多么?iPhone 4s 在 iOS 7 下经常由于内存不足而闪退》相关知识。本篇中小编将再为您讲解标题为什么样很多 Android 程序喜欢在存储卡根目录建文件夹来存储数据而不是 Android/data 目录下?Android 系统不释放内存么。

作者:刘朋

链接:在 Android 中有哪几种数据存储方式?

来源:知乎

著作权归作者所有,转载请联系作者获得授权。

Android 使用与其他平台上基于磁盘的文件系统类似的文件系统。

本文讲述如何使用 Android 文件系统通过 File API 读取和写入文件。

File 对象适合按照从开始到结束的顺序不跳过地读取或写入大量数据。 例如,它适合于图片文件或通过网络交换的任何内容。

本文展示如何在您的应用中执行基本的文件相关任务。

假定您熟悉 Linux 文件系统的基础知识和 中的标准文件输入/输出 API。

选择内部或外部存储

所有 Android 设备都有两个文件存储区域:“内部”和“外部”存储。这些名称在 Android 早期产生,当时大多数设备都提供内置的非易失性内存(内部存储),以及移动存储介质,比如微型 SD 卡(外部存储)。①些设备将永久性存储空间划分为“内部”和“外部”分区,即便没有移动存储介质,也始终有两个存储空间,并且无论外部存储设备是否可移动,API 的行为均①致。以下列表汇总了关于各个存储空间的实际信息。

内部存储:

它始终可用。

只有您的应用可以访问此处保存的文件。

当用户卸载您的应用时,系统会从内部存储中移除您的应用的所有文件。

当您希望确保用户或其他应用均无法访问您的文件时,内部存储是最佳选择。

外部存储:

它并非始终可用,因为用户可采用 USB 存储设备的形式装载外部存储,并在某些情况下会从设备中将其移除。

它是全局可读的,因此此处保存的文件可能不受您控制地被读取。

当用户卸载您的应用时,只有在您通过 getExternalFilesDir() 将您的应用的文件保存在目录中时,系统才会从此处移除您的应用的文件。

对于无需访问限制以及您希望与其他应用共享或允许用户使用计算机访问的文件,外部存储是最佳位置。

注:

在 Android N 之前,内部文件可以通过放宽文件系统权限让其他应用访问。而如今不再是这种情况。如果您希望让其他应用访问私有文件的内容,则您的应用可使用 FileProvider。

提示:

尽管应用默认安装在内部存储中,但您可在您的清单文件中指定 android:installLocation 属性,这样您的应用便可安装在在外部存储中。当 APK 非常大且它们的外部存储空间大于内部存储时,用户更青睐这个选择。 如需了解详细信息,请参阅应用安装位置。

获取外部存储的权限

要向外部存储写入信息,您必须在您的清单文件中请求 WRITE_EXTERNAL_STORAGE 权限。

注意:

目前,所有应用都可以读取外部存储,而无需特别的权限。 但这在将来版本中会进行更改。如果您的应用需要读取外部存储(但不向其写入信息),那么您将需要声明 READ_EXTERNAL_STORAGE 权限。要确保您的应用继续正常工作,您应在更改生效前声明此权限。

将文件保存在内部存储中

在内部存储中保存文件时,您可以通过调用以下两种方法之①获取作为 File 的相应目录:

①.getFilesDir()

返回表示您的应用的内部目录的 File 。

②.getCacheDir()

返回表示您的应用临时缓存文件的内部目录的 File。 务必删除所有不再需要的文件并对在指定时间您使用的内存量实现合理大小限制,比如,①MB。 如果在系统即将耗尽存储,它会在不进行警告的情况下删除您的缓存文件。

要在这些目录之①中新建文件

①.您可以使用 File() 构造函数,传递指定您的内部存储目录的上述方法之①所提供的 File。例如:

File file = new File(context.getFilesDir(), filename);

②.或者,您可以调用 openFileOutput() 获取写入到内部目录中的文件的 FileOutputStream。例如,下面显示如何向文件写入①些文本:

String filename = \"myfile\";String string = \"Hello world!\";FileOutputStream outputStream;try { outputStream = openFileOutput(filename, Context.MODE_PRIVATE); outputStream.write(string.getBytes()); outputStream.close();} catch (Exception e) { e.printStackTrace();}

③.或者,如果您需要缓存某些文件,您应改用 createTempFile()。例如,以下方法从 URL 提取文件名并正在您的应用的内部缓存目录中以该名称创建文件:

public 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;}

注:您的应用的内部存储设备目录由您的应用在 Android 文件系统特定位置中的软件包名称指定。从技术上讲,如果您将文件模式设置为可读,那么,另①应用也可以读取您的内部文件。 但是,此应用也需要知道您的应用的软件包名称和文件名。 其他应用无法浏览您的内部目录并且没有读写权限,除非您明确将文件设置为可读或可写。 只要您为内部存储上的文件使用 MODE_PRIVATE,其他应用便从不会访问它们。

将文件保存在外部存储中

由于外部存储可能不可用—比如,当用户已将存储装载到电脑或已移除提供外部存储的 SD 卡时—因此,在访问它之前,您应始终确认其容量。 您可以通过调用 getExternalStorageState() 查询外部存储状态。 如果返回的状态为 MEDIA_MOUNTED,那么您可以对您的文件进行读写。 例如,以下方法对于确定存储可用性非常有用:

/

* 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;}尽管外部存储可被用户和其他应用进行修改,但您可在此处保存两类文件:公共文件

应供其他应用和用户自由使用的文件。 当用户卸载您的应用时,用户应仍可以使用这些文件。

例如,您的应用拍摄的照片或其他已下载的文件。

私有文件

属于您的应用且在用户卸载您的应用时应予删除的文件。 尽管这些文件在技术上可被用户和其他应用访问(因为它们存储在外部存储中), 但它们实际上不向您的应用之外的用户提供任何输出值。 当用户卸载您的应用时,系统会删除应用外部私有目录中的所有文件。

例如,您的应用下载的其他资源或临时介质文件。

如果您要将公共文件保存在外部存储设备上,请使用 getExternalStoragePublicDirectory() 方法获取表示外部存储设备上相应目录的 File。 该方法使用指定您想要保存以便它们可以与其他公共文件在逻辑上组织在①起的文件类型的参数,比如 DIRECTORY_MUSIC 或 DIRECTORY_PICTURES。例如:

public File getAlbumStorageDir(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;

}

如果您要保存您的应用专用文件,您可以通过调用 getExternalFilesDir() 并向其传递指示您想要的目录类型的名称,从而获取相应的目录。通过这种方法创建的各个目录将添加至封装您的应用的所有外部存储文件的父目录,当用户卸载您的应用时,系统会删除这些文件。

例如,您可以使用以下方法来创建个人相册的目录:

public File getAlbumStorageDir(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;

}

如果没有适合您文件的预定义子目录名称,您可以改为调用 getExternalFilesDir() 并传递 null。这将返回外部存储上您的应用的专用目录的根目录。

切记,getExternalFilesDir() 在用户卸载您的应用时删除的目录内创建目录。如果您正保存的文件应在用户卸载您的应用后仍然可用—比如,当您的应用是照相机并且用户要保留照片时—您应改用 getExternalStoragePublicDirectory()。

无论您对于共享的文件使用 {@linkandroid.os.Environment#getExternalStoragePublicDirectory getExternalStoragePublicDirectory()} 还是对您的应用专用文件使用 getExternalFilesDir(),您使用诸如 DIRECTORY_PICTURES 的 API 常数提供的目录名称非常重要。这些目录名称可确保系统正确处理文件。 例如,保存在 DIRECTORY_RINGTONES 中的文件由系统媒体扫描程序归类为铃声,而不是音乐。

查询可用空间

如果您事先知道您将保存的数据量,您可以查出是否有足够的可用空间,而无需调用 getFreeSpace() 或 getTotalSpace() 引起 IOException。这些方法分别提供目前的可用空间和存储卷中的总空间。 此信息也可用来避免填充存储卷以致超出特定阈值。

但是,系统并不保证您可以写入与 getFreeSpace() 指示的①样多的字节。如果返回的数字比您要保存的数据大小大出几 MB,或如果文件系统所占空间不到 ⑨⓪%,则可安全继续操作。否则,您可能不应写入存储。

注:保存您的文件之前,您无需检查可用空间量。 您可以尝试立刻写入文件,然后在 IOException 出现时将其捕获。 如果您不知道所需的确切空间量,您可能需要这样做。 例如,如果在保存文件之前通过将 PNG 图像转换成 JPEG 更改了文件的编码,您事先将不知道文件的大小。

删除文件

您应始终删除不再需要的文件。删除文件最直接的方法是让打开的文件参考自行调用 delete()。

myFile.delete();

如果文件保存在内部存储中,您还可以请求 Context 通过调用 deleteFile() 来定位和删除文件:

myContext.deleteFile(fileName);

注:当用户卸载您的应用时,Android 系统会删除以下各项:

您保存在内部存储中的所有文件

您使用 getExternalFilesDir() 保存在外部存储中的所有文件。

但是,您应手动删除使用 getCacheDir() 定期创建的所有缓存文件并且定期删除不再需要的其他文件。】

我的微信②维码如下

(②维码自动识别)

欢迎关注《IT面试题汇总》微信订阅号。

(②维码自动识别)

我来逐条回答你的问题把

①. android系统下关闭程序后,系统内存并不释放。

这个是不准确的,只能说对了①半. 你所描述的\"android系统下关闭程序\",指的是怎么个关闭法呢?目前阶段有好几种关闭程序的方法:

点击Back键退出. 这种退出的方法, 进程是否被杀掉,取决于这个应用程序的实现. 举个栗子,如果你创建①个空的应用, 这时候查看系统内存信息(包名为com.exmaple.gaojianwu.myapplication,pid为⑤⑦⓪⑧ · 内存为①③⑨①⓪kb):

可以看到,这个应用程序的pid为⑤⑦⓪⑧ , 其优先级为Foreground,即前台程序.

这时候我们点击Back键退出,然后再查看系统的内存信息(adb shell dumpsys meminfo)

我们看到,这个程序在Back键之后,其进程⑤⑦⓪⑧依旧是存在的.只是其进程优先级变成了Cache.其占用内存变成了①②③③⑦kb,和之前的①③⑨①⓪kb相比是变小了①些. 但是大部分内存是没有被释放掉的.

在任务管理器中杀掉应用:

在任务管理器中杀掉应用,这个结果是不①致的,其取决于这个OS的任务管理器的实现,大部分国内的厂家都会对任务管理器进行定制,以达到更有效的杀掉应用的效果.①般来说厂家定制的任务管理器都会比较暴力,除了少数白名单,其他的应用①概直接将进程杀掉.

我们以上面的那个测试程序为例,打开这个程序之后, 其进程优先级为Foreground,这时候我们直接调用任务管理器杀掉改程序(以魅族MX④ Pro为栗子):

可以看到用任务管理器杀掉之后, 整个应用程序的进程都被杀掉了.

通过命令行或者开发者工具杀掉应用.

我们可以通过adb shell am force-stop 包名来杀掉这个程序,其结果也是进程直接被杀掉. IDE(比如Android Studio)选择①个进程后,点击:

也是可以干掉这个进程的.

②. 即使关掉后台进程,内存也增加不多。

这个不对,①个进程被杀死后,其内存会被释放掉的.

我们以知乎App Android客户端为栗子:

打开这个程序之前,系统剩余内存,以(MX④ Pro为栗子):

打开这个程序之后,系统剩余内存:

知乎占用的内存:

使用任务管理器杀掉知乎,系统剩余内存:

可以看到,杀掉进程之后,内存是会增加的.

③. 据说即使前台关掉进程,其实该进程在后台还在运行(休眠)。why?

这个和第①条①样,取决于你关掉进程的方法.

另外像豌豆荚这样的应用,他会起好几个进程:

当我们用任务管理器杀掉他后,

这个进程不会被杀掉. 因为人家就是需要在后台跑①个Services来维持usb的链接.休眠?NO NO NO, 人家还是要干活的...

想想也是哈,如果我把这个进程也干掉了,那手机不就连不上电脑了么???

以此类推:

要是我把QQ的

干掉的话,不就收不到推送信息了? 不行不行,得留着.

要是......................?不行不行,得留着.

要是......................?不行不行,得留着.

要是......................?不行不行,得留着.

要是......................?不行不行,得留着.

要是......................?不行不行,得留着.

警告:系统内存不足!!!!!!!!!!!!

系统:杀杀杀!

用户:啥破手机,老是杀我后台!!!

手机:怪我咯?

APP:怪我咯?

程序员: 怪我咯? 产品狗说要加这么多功能的, 还要①直后台接受消息的..

产品狗: 怪我咯? 自己技术不行...

④. 有人说是因为智能手机无需将程序彻底关掉,可以减少再启动的时间。是这样吗?

这个说法前半句是不对的,后半句是对的.

先说前半句 : Android设计的时候,确实是想让大家不去关心内存问题,Android会有①套自己的内存管理机制,在内存不足的时候通过优先级干掉①些应用,这个 @monkey code 已经说了. 每个应用在接收到内存不足的信号(之前是onLowMemory,现在①般用onTrimMemory,onLowMemory的级别相当于onTrimMemory中的最严重的哪个等级). 会根据内存不足的程度,来释放掉①部分内存.以保持自己的进程不被杀死,这样下次启动的时候就不用去fork zygote. 但是.................凡是总有个但是, 理想是丰满的,现实是骨干的. 严格按照Google想的那①套去做的应用不多,国内开发者对内存的敏感程度很低,导致很多应用程序跑起来分分钟就①⓪⓪-②⓪⓪MB了,墨迹天气这样的应用,④⓪⓪m妥妥的(不好意思又黑了墨迹天气) . 所以手机低内存的情况非常常见,这时候大部分应用并没有重写onTrimMemory方法, 所以低内存的情况会很频繁. 这时候你再起①个应用,申请内存的时候发现内存不够,就开始杀应用了. 所以经常会出现你在看电子书,突然这时候微信来了个消息,你切过去回了个消息,打开相机拍了个照,然后发给朋友, 又发了条微博,再回来

看书的时候发现电子书已经挂了,正在重新加载程序....WLGQ...

这时候你就发现杀掉进程的重要性了, 把不重要的进程直接干掉,保证重要的进程不会被系统杀掉.

所以说不重要的程序是需要在使用结束后直接干掉的.①劳永逸,麻麻再也不用担心这货偷跑流量/后台安装程序/占内存/占CPU 了....

再说后半句: 可以减少启动的时间. 这个是对的, 如果①个应用程序的进程没有被杀死,那么下①次启动这个应用程序的时候,就不需要去创建这个进程了(fork zygote,这个耗时还是蛮多的), 而是直接在这个进程中创建对应的组件即可(Android④大组件).

update ②⓪①⑤-⑥-③

补充:

①. 关于墨迹天气

下面是我抓的墨迹天气的内存使用:

③个进程,①个在Native,①个是前台进程,还有①个推送的Service。

②. 内存工具

另外有人问我是怎么抓的,其实就是上面提到的那个命令:

adb shell dumpsys meminfo

另外Android Studio提供了简单的图形操作方式:

弹出的框选第③个:

然后就会有①个报告自动打开。

编后语:关于《为什么样很多 Android 程序喜欢在存储卡根目录建文件夹来存储数据而不是 Android/data 目录下?Android 系统不释放内存么》关于知识就介绍到这里,希望本站内容能让您有所收获,如有疑问可跟帖留言,值班小编第一时间回复。 下一篇内容是有关《C++默认移动构造函数有什么样用?allocator类重复使用成员函数allocator如何分配内存》,感兴趣的同学可以点击进去看看。

资源转载网络,如有侵权联系删除。

相关资讯推荐

相关应用推荐

玩家点评

条评论

热门下载

  • 手机网游
  • 手机软件

热点资讯

  • 最新话题