Android存储:内部,外部,可移动。 第1/3部分

对于所有设法做出正确选择的人。

这是CommonsWare的Mark Murphy撰写的一系列文章的翻译,该文章在stackoverflow上广为人知,并且是“ Android开发的繁忙编码指南”,“ Android的体系结构组件”一书的作者。 有些术语没有特别翻译。


内部储存


关于Android存储模型存在很多困惑。 随着存储模型中Android 4.4的更改,这种困惑已大大增加,此后情况一直没有改善。 关于Stack Overflow和类似资源,存在无数问题,人们显然并不完全了解各种Android存储模型。


用户如何看待内部存储


根据您设备的型号,用户最终会进入设备上的“设置”->“存储”或等效位置,并可以看到描述“内部存储”的屏幕。


用户认为整个内置闪存驱动器是“内部存储”。 幸运的是,Google开始使用Android 8.0更改此术语,改为使用“通用存储”而不是“内部存储”。


但是,通过USB连接设备时,用户仍可以在Windows资源管理器窗口等位置看到“内部存储”。


Google对内部存储的看法


users,用户看到的内容与Android SDK认为“内部存储”的内容不同,这会引起一些混乱。 如果您阅读内部存储库上的Android文档 ,则此描述...至少含糊不清(自撰写本文以来,注释文本已更改):


您可以将文件直接保存到设备的内存中。 默认情况下,内部存储器中存储的文件是您的应用程序专用的,其他应用程序(以及用户)无法访问它们。 当用户卸载应用程序时,这些文件将被删除。

实际上,Android SDK将“内部存储”定义为应用程序唯一的特殊目录,您的应用程序可以在其中托管文件。 如文档中所建议,这些文件默认情况下供您的应用程序读取和写入,禁止用于其他任何应用程序(例外:使用具有根用户设备超级用户权限的文件管理器的用户可以访问所有内容)。


上下文具有一些基本方法,可让您访问内部存储,包括:


  • getCacheDir()
  • getDir()
  • getDatabasePath()
  • getFilesDir()
  • openFileInput()
  • openFileOutput()

其他方法将依赖于它们,例如openOrCreateDatabase() 。 其他类也将依赖它们,例如SQLiteOpenHelperSharedPreferences


内部存储存储在哪里...有时


如果查看2012年或更早版本中发布的各种博客文章,StackOverflow响应和书籍,系统会通知您应用程序的“内部存储”位于/data/data/your.application.package.name


当您使用某些Context方法时,内部将有一些Android自动创建的目录。 例如, getFilesDir()返回一个File对象,该对象指向应用程序内部存储中的files/目录。


内部存储器存储在哪里...其余时间


但是,应用程序的内部存储并不总是位于指定的位置。 对于开发人员,有一条规则必须从这一系列博客文章中学习:


绝不困难的路径


我不时看到开发人员在做这样的事情:


File f=new File("/data/data/their.app.package.name/files/foo.txt");


这不是犯罪,更糟糕的是,这是一个错误。


正确的举动,少写点:


File f=new File(getFilesDir(), "foo.txt");


更重要的是, 内部存储并不总是位于同一位置 。 值得注意的是,我们拥有独立用户个人资料(独立用户个人资料)的概念, 平板电脑的Android 4.2和手机的Android 5.0开始。 每个用户都有自己的“内部存储”。 尽管以上目录仍用于主要用户,但不能保证将其用于辅助帐户。


探索内部存储


Android Studio 3.0+中的设备文件资源管理器工具可以查看模拟器上的所有内部存储,以及生产设备上调试后的应用程序的内部存储。


在命令行中,可以将adbrun-as选项一起使用。


例如,要将数据库从主要用户的内部存储上传到开发计算机,可以使用:


adb shell 'run-as your.application.package.name cp /data/data/your.application.package.name/databases/dbname.db /sdcard'


请注意:


  • 您需要将目标位置更改为外部存储在设备上的安装位置(此处显示为/sdcard/ ,并非在所有设备上都相同)
  • 您可能需要在较旧的设备上使用cat而不是cp
  • 将文件放置在外部存储上之后,您可以使用adb pull其下载到计算机上,或以其他常用方式(例如,通过将设备安装为磁盘)进行访问。

内部存储限制


在较旧的Android 1.x和2.x设备上,内部存储通常位于文件系统的专用部分中,并且该部分通常很小。 最初的Android设备HTC Dream(又名T-Mobile G1)具有70 MB的巨大内部内存,供所有应用程序使用(这不是错字,当时我们以兆字节为单位测量内存)。


到出现2.3台设备时,内部存储器的大小可能已达到1 GB。


随着内部存储变得越来越大,Android 3.0改变了存储模式。 对于宣传为具有4 GB,8 GB,16 GB等的设备。 存储空间,通常所有这些内容(减去现有内容)可用于内部存储。 我们将在以下有关外部存储的文章中介绍Android 3.0中发生的变化及其对存储模型的影响。


对于Android 1.x和2.x,内部存储仅对小文件有效,而其他所有内容都必须使用外部存储。 Android 3.0+意味着对于大多数设备和大多数用户来说,内部存储非常适合那些不适合其他应用程序正常使用的文件,或者对于用户而言无论您使用什么应用程序都可以访问的文件。 但是,一些有经验的用户发现,即使是板载闪存也不足以存储他们想要存储的内容,因此他们切换到了可移动存储设备……这是一罐蠕虫(请注意λμινς) ,这是许多无法预料和复杂问题的根源。


内部存储常见问题解答


我应该在世界可读取或世界可写入内部存储器中制作文件吗?


哦,天哪,不。 使用FileProvider并通过ContentProvider的实现提供此内容。 之后,至少您有机会使用Android许可系统来控制对这些文件的访问,这与您的版本不同,因为系统中的任何应用程序都可能破坏这些文件。


好吧,关于android:sharedUserId呢?


我不建议。


android: sharedUserId是一个属性,您可以将其放在清单中,该清单指示将用于您的应用程序的用户的逻辑标识符。 从安全角度来看,安装的其他所有使用相同签名密钥签名并请求相同android:sharedUserId都将使用同一Linux用户。 结果是这两个应用程序可以互不干扰地使用彼此的文件,因为这些文件属于同一Linux用户。


此属性确实适用于预安装的应用程序,例如由设备制造商,移动运营商或开发的ROM固件的开发人员预加载的软件套件。 特别是,一旦安装了应用程序,便无法毫不费力地更改android:sharedUserIdandroid:sharedUserId而不阻止用户访问任何现有文件...因为更改Linux用户帐户时Android不会更改所有者对现有文件的权利,运行该应用程序。


当多个进程访问文件时,存在各种风险。 一些子系统(例如SQLite)具有内置逻辑来解决此问题。 但是,如果您自己组织对文件的访问(例如,通过File和Java I / O),则需要同时进行一些访问,这很困难。


您还需要通过删除另一个应用程序使用的文件来处理一个应用程序正在卸载的情况。 例如,在中心辐射型模型中,使用一个应用程序和一组插件,这并不是那么危险。 在其他应用程序更公平的模型中,仅由于用户决定删除某些单独的应用程序,您就无法承受丢失应用程序数据的负担。


最后,您不知道未来会带来什么。 现在,您可以将应用程序套件视为紧密耦合的套件。 购买这些应用程序或收购您的公司的某人可能希望以其他方式进行。 使用ContentProvider连接的数据共享功能(例如ContentProvider )可以为您提供更大的灵活性。 在理想的情况下,您的应用程序应像对待自己的Web服务一样,将其他应用程序视为相当可靠但并非始终可访问的资源。


如何防止有根设备的用户访问内部存储中的文件?


只是不要将文件放在内部存储中。 拥有根权限的设备的用户可以访问他们在设备上需要的内容,因此阻止他们访问您数据的唯一方法是不在设备上拥有它。


一些开发人员将尝试使用硬编码密码来加密他们的文件,以使有根设备的用户无法使用这些文件。 这将在短时间内产生减速带的效果。 所需要的只是一个对您的应用程序进行反向工程感兴趣的人员,该人员已确定如何解密这些文件,然后在博客或论坛上写了有关如何执行此操作的消息。


一般而言,拥有植根设备的人相对较少-我对它们的评价不到1%。 恕我直言,通过将工程工作集中在编写更好的应用程序上,而不是花费时间来保护root用户的设备,您将获得更大的成功。

Source: https://habr.com/ru/post/zh-CN429338/


All Articles