从 API 8 开始(参考官方文档:App Install Location | Android Developers),你可以将你的应用安装在外部储存中(例如,安装到设备的 SD 卡上)。这是一个可选的特征,你可以在你的应用的 AndroidManifest.xml 中声明 android:installLocation 属性。如果你没有声明这个属性,你的应用程序将会被安装在内部储存,并且不能被移到外置储存中。
修改 AndroidManifest.xml 文件中 <manifest> 元素下的 android:installLocation 属性,赋值为 “preferExternal” 或 “auto”,即可允许系统将应用安装到外部存储中。代码如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" ... >
如果 android:installLocation 属性声明为 preferExternal,意味着你要求应用程序安装在外置储存,但是系统不保证你的程序一定会被安装在外部储存中。如果外部储存空间已满,程序将会被安装在内部储存。用户也可以在两个储存之间移动你的应用程序。如果声明了 auto,表明你的应用程序可以安装在外部储存,但是你没有一个安装位置的偏好。系统会根据一些因素来决定你的应用程序安装在哪。用户也可以在两个储存之间移动你的程序。
当你的应用被安装在外部储存时:
- 只要外部存储一直挂载在设备上,就不会对应用的性能造成影响。
- .apk 文件会储存在外部储存中,但所有私有的用户数据,数据库,优化的 .dex 文件,和提取的本地的代码都会保存在内部储存中。
- 存储应用的特定容器被一个随机生成的 Key 加密,并且只能被最初安装这个应用的设备解密。这样就可以保证一个安装在 SD 卡上的应用只为唯一的设备服务。
- 用户可以通过系统设置将应用移到内部储存中。
▐ 注意:当用户启用 USB 大容量存储与电脑共享文件或通过系统设置卸载 SD 卡时,外部存储就会从设备卸载同时所有运行在外部存储的应用会被立即杀死。
Backward Compatibility
只有在运行 API 8(Android 2.2)或更高版本的设备上才可以将应用安装到外部存储中,在 API 8 之前建立的应用只能被安装到内部存储中,并且不能移动到外部存储中(即使在 API 8 的设备上)。如果要求应用支持 API < 8 的设备,你可以让运行 API >= 8 的设备使用这个特性,也可通过如下的设置来兼容 API < 8 的设备。
- 在 AndroidManifest.xml 文件 <manifest> 元素中设置的 android:installLocation 属性为 auto 或 perferExternal。
- 保持 android:minSdkVersion 属性不变(一个小于8的数)并保证你应用代码所使用的 API 与之相符。
- 在编译你的应用之前,将 build target 的 API 级别修改为 8。因为低版本的 Android 库不识别 android:installLocation 属性,因此也就不会在该属性存在的情况下编译应用。
当你的应用安装在一个运行 API < 8 的设备上时,android:installLocation 属性会被忽略,并且应用程序会被安装到内部存储中。
▐ 注意:尽管通过 XML 标记可以让低版本的平台忽略这个问题,但仍然要小心的是,当 minSdkVersion < 8 的时候,不要在编程过程中引入级别为 8 的 API,除非你的代码需要向后兼容。
Applications That Should NOT Install on External Storage
当用户启用 USB 大容量存储与计算机共享文件(通过其他方式卸载或移除外部存储),任何安装在外部存储并正在运行的程序都会被杀死。直到大容量存储处于不可用状态,外部存储重新被挂载到设备上,系统才会识别这些应用。除了杀死应用使得它对用户不可用之外,还会通过一些更危险的方式破坏某些类型的应用程序。如果你的应用使用了如下特征,会在外部存储卸载的时出现引用问题,所以你应该禁止你的应用安装在外部存储中。
服务(Services)
正在运行的服务会被杀死,并且在外部存储被重新挂载后也不会重启。但是你可以注册一个 ACTION_EXTERNAL_APPLICATIONS_AVAILABLE 的广播,这样应用程序就可以在外置存储可用时接收到通知并重启服务。
警报服务(Alarm Services)
使用 AlarmManager 注册的 Alarm Services 将被取消。你必须在外置存储被重新挂载的时候重新手动的注册 Alarm Services。
输入法引擎(Input Method Engines)
你的 IME 将会被替换为默认的 IME。当外部存储重新挂载的时候,用户可以在系统设置中激活自己的 IME。
动态壁纸(Live Wallpapers)
动态壁纸会被替换为默认的动态壁纸。外部存储重新挂载的时候,用户可以重新选择自己之前的壁纸。
窗口小部件(App Widgets)
应用程序的 App Widget 会被移除,即使外部存储重新被挂载,用户也无法使用该应用的 App Widget,除非系统重置 Home 应用(通常要等到系统重启)。
账户管理器(Account Managers)
通过 AccountManager 创建的账户会消失,直到外部存储重新挂载后在可用。
同步适配器(Sync Adapters)
AbstractThreadedSyncAdapter 以及它的一切异步方法将停止工作,同样也的等到外部存储重新挂载后才能恢复正常。
设备管理员(Device Administrators)
你的 DeviceAdminReceiver 及其所有管理功能将被禁用,这可能对设备造成不可预见的后果。
监听“启动完成”的广播接收者(Broadcast Receivers listening for "boot completed")
系统会在外部存储挂载到设备之前发出 ACTION_BOOT_COMPLETED 广播。如果你的应用程序安装在外部存储器,它永远不会收到此广播。
如果你的应用程序使用了上面列出的功能,你不应该允许你的应用安装到外部存储。默认情况下,系统将不允许你的应用安装到外部存储,所以你不需要担心你现有的应用程序。然而,如果你非常确定你的应用不应该安装到外部存储,那么你应该声明 android:installLocation 的值为 “internalOnly”。但这并不会改变默认的行为,它只是很明确的提醒你和其他的开发者这个应用只应该被安装在内部存储中。
Applications That Should Install on External Storage
简单来说,只要你的应用没有使用上节中列出的特性,那么安装到外部存储中就是安全的。大型游戏通常会允许安装到外部存储中,因为游戏通常不会在闲置状态提供额外的服务。当外部存储不可用时,游戏进程会被杀死,当外置储存变得可用而且用户重启游戏时,不应该有明显的影响(假设游戏在它正常的 Activity 生命周期适当的保存了状态)。
如果你应用程序的 APK 文件占用很大字节的存储空间,你应该仔细的考虑是否将应用安装到外部存储中,以便为用户节省内部存储空间。