Android7.0版本影响开发的改进分析
本文总结分析了Android7.0版本影响开发的改进。分享给大家供大家参考,具体如下:
低电耗模式
会对闹铃、GPS 和 Wi-Fi 扫描 产生限制.
可参考Optimizing for Doze and App Standby
使用GCM来发送和接受消息
后台优化
Android N 删除了三项隐式广播,隐式广播会在后台频繁启动已注册侦听这些广播的应用。 删除这些广播可以显著提升设备性能和用户体验.
侦听网络变化的主线程广播改为: CONNECTIVITY_CHANGE。
对所有应用都无法 发送和接受 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO .
可以使用JobScheduler API ,更多参考后台优化
系统权限更改
为了提高私有文件的安全性,面向 Android 7.0或更高版本的App私有目录被限制访问(0700)。此设置可防止私有文件的元数据泄漏,如它们的大小或是否存在(状态)。此权限策略的更改有多重副作用:
私有文件的文件权限不应再由所有者放宽,为使用MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE而进行的此类尝试将触发SecurityException,会导致App崩溃的。
注:迄今为止,这种限制还不能完全执行。App仍可能使用原生API或File API来修改它们的私有目录权限。但是Google强烈反对放宽私有目录的权限。
传递软件包网域外的 file://URI可能给接收器留下无法访问的路径。因此传递file://URI会触发 FileUriExposedException。分享私有文件内容的推荐方法是使用FileProvider。
DownloadManager不再按文件名分享私人存储的文件。老的App在访问COLUMN_LOCAL_FILENAME时可能出现无法访问的路径。针对Android 7.0或更高版本开发的应用在尝试访问COLUMN_LOCAL_FILENAME时会触发 SecurityException。通过使用DownloadManager.Request.setDestinationInExternalFilesDir())或DownloadManager.Request.setDestinationInExternalPublicDir())将下载位置设置为公共位置的老App仍可以访问COLUMN_LOCAL_FILENAME中的路径,但是Google还是强烈反对使用这种方法。访问由DownloadManager公开的文件的首选方式是使用ContentResolver.openFileDescriptor())。
应用间共享文件
对于针对Android 7.0的应用,Android framework执行的StrictMode API禁止向你的App外公开file://URI。如果一个包含文件URI的Intent发送到你的应用之外,App会发生FileUriExposedException异常。
若要在应用间共享文件,您应发送一项content://URI,并授予URI临时访问权限。进行此授权的最简单方式是使用FileProvider类。如需有关权限和共享文件的更多信息,请参阅共享文件。
解决 Android N 上 安装Apk时报错:android.os.FileUriExposedException:
在AndroidManifest.xml中添加如下代码
<provider android:name="android.support.v4.content.FileProvider" android:authorities="app的包名.fileProvider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
注意:
authorities:app的包名.fileProvider
grantUriPermissions:必须是true,表示授予 URI 临时访问权限
exported:必须是false
resource:中的@xml/file_paths是我们接下来要添加的文件
在res目录下新建一个xml文件夹,并且新建一个file_paths的xml文件(如下图)
输入以下内容
<?xml version="1.0" encoding="utf-8"?> <paths> <external-path path="Android/data/app的包名/" name="files_root" /> <external-path path="." name="external_storage_root" /> </paths>
path:需要临时授权访问的路径(.代表所有路径)
name:就是你给这个访问路径起个名字
最后修改代码:
Intent intent = new Intent(Intent.ACTION_VIEW); //判断是否是AndroidN以及更高的版本 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile); intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); } else { intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } startActivity(intent); //以前我们直接 Uri.fromFile(apkFile)构建出一个Uri,现在我们使用FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", apkFile); //BuildConfig.APPLICATION_ID直接是应用的包名
屏幕缩放
Android 7.0支持用户设置显示尺寸,以放大或缩小屏幕上的所有元素,从而提升设备对视力不佳用户的可访问性。用户无法将屏幕缩放至低于最小屏幕宽度sw320dp,该宽度是Nexus 4的宽度,也是常规中等大小手机的宽度。
当设备密度发生更改时,系统会以如下方式通知正在运行的应用:
1. 如果是面向API leve 23或更低版本系统的应用,系统会自动终止其所有后台进程。也就是说如果用户切换后离开你的App,打开“Settings”更改Display size设置,则系统会像处理内存不足的情况一样终止该应用。如果应用具有任何前台进程,则系统会如处理运行时变更中所述将配置变更通知给这些进程,就像对待设备屏幕方向变更一样,具体大家可以再看看这个超链接。
2. 如果是针对Android 7.0的App,则其所有进程(前台和后台)都会收到有关配置变更的通知,如处理运行时变更中所讲的那样。 大多数App并不需要进行任何更改即可支持此功能,不过前提是这些应用遵循Android最佳实践。具体要检查的事项:
① 在屏幕宽度为 sw320dp 的设备上测试你的App,并确保其正常运行。
② 当设备Config发生变更时,更新任何与密度相关的缓存信息,例如缓存位图或从网络加载的资源。当应用从暂停状态恢复运行时,检查Config的变化。
注:如果你要缓存与配置相关的数据,则最好也包括相关元数据,例如该数据对应的屏幕尺寸或像素密度。保存这些元数据便于你在Config变更后决定是否需要刷新缓存数据。
③ 避免用像素单位指定尺寸,因为像素不会随屏幕密度缩放。应改为使用dp等单位。
用户可以在设置-显示-显示大小修改屏幕宽度,也可以在设置-开发人员选项-最小宽度随意设置指定宽度,开发人员特别需要注意适配
NDK平台库
Android N 做了一些命名空间更改,阻止加载非公开API,会出现一些常见错误
如,UnsatisfiedLinkError
典型修复方法:
1. 使用标准 JNI 函数来替代使用 libandroid_runtime.so 中的 getJavaVM 和 getJNIEnv
2. 使用公开 alternative __system_property_get 来替代使用 libcutils.so 中的 property_get 符号
3. 使用应用本地版本来替代使用 libcrypto.so 中的 SSL_ctrl 符号
注解保留
Android 7.0在注解可见性被忽略时修复错误。这种问题将启用本不应被允许的运行时访问注解。 这些注解包括:
VISIBILITY_BUILD:仅应编译时可见。
VISIBILITY_SYSTEM:运行时应可见,但仅限基本系统。 如果你的App依赖这种行为,请在注解中添加一项运行时必须可用的保留政策。你可通过使用@Retention(RetentionPolicy.RUNTIME)
这样做。
其他重要说明
1. 如果一个针对较低API级别开发的App在Android 7.0上运行,那么在用户更改显示尺寸时,系统将终止此App进程。App必须能够正常处理此情景。否则,当用户从最近使用记录中恢复运行App时,App将会出现崩溃现象。您应测试应用以确保不会发生此行为。要进行此测试,您可以通过DDMS手动终止应用,可以造成相同的崩溃现象。在屏幕密度发生更改时,系统不会自动终止针对Android 7.0及更高版本开发的App;不过这些App仍可能对配置变更做出不良响应。
2. Android 7.0上的应用应能够正常处理配置变更,并且在后续启动时不会出现崩溃现象。你可以通过更改字体大小 (Setting > Display > Font size) 并随后从最近使用记录中恢复运行应用,来验证App行为。
3. 由于之前的Android版本中的一项错误,系统没有对主线程上的一个TCP Socket的写入操作严格检查。Android 7.0修复了这个系统错误。之前有这种行为的App将会引发android.os.NetworkOnMainThreadException
。一般情况下,不建议在主线程上执行网络操作,因为这些操作通常都有可能导致ANR和卡顿,这个应该是中所周知的,大家一般不会犯。
4. Debug.startMethodTracing()
方法族现在默认在你的共享的存储空间上的软件包特定目录中存储输出,而非 SD卡顶级。这意味着应用不再需要请求WRITE_EXTERNAL_STORAGE权限就可以使用这些API。
5. 许多平台API现在开始检查在Binder事务间发送的大负载,系统现在会将TransactionTooLargeExceptions再次作为RuntimeExceptions引发,而不再只是默默记录或不抛出这个错误。一个常见例子是在Activity.onSaveInstanceState())
上存储过多数据,导致ActivityThread.StopInfo在你的App面向 Android 7.0时引发RuntimeException。
6. 如果应用向View post Runnable任务,并且View未附加到窗口,系统会用View为Runnable任务排队;在 View附加到窗口之前,Runnable任务不会执行。 此行为会修复以下错误:
① 如果一个App是从并非预期Window UI线程的其他线程发布到View,则Runnable可能会因此运行错误。
② 如果Runnable任务是从并非looper thread的其他线程发布,则应用可能会曝光Runnable任务。
7. 如果Android 7.0上有DELETE_PACKAGES权限的应用尝试删除一个软件包,但另一项应用已经安装了这个软件包,则系统可能要求用户确认。在这种情况下,应用在调用PackageInstaller.uninstall())
时的返回状态应为STATUS_PENDING_USER_ACTION。
更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》
希望本文所述对大家Android程序设计有所帮助。