- 之前在做App版本更新的时候,发现在5.0以上的手机上会出现安装失败崩溃的问题,之前一直好好的,突然出现这个问题,直接是蒙蔽状态,但又必须解决,所以只有对出现的问题进行跟踪,最后解决了,在这里做一下记录
问题1
-
APK在7.0以上手机若不设置FileProvider会直接安装崩溃并抛类似下面的异常,刚才是还以为是安装包路径写错了,后来才发现是在7.0中为了提高应用的安全性,引入私有目录被限制访问和StrictMode API(禁止向你的应用外公开file://url,若有一项包含文件file://url类型的Intent离开你的应用,则会抛出异常)
android.os.FileUriExposedException: file:///XXX exposed beyond app through ClipData.Item.getUri() at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799) at android.net.Uri.checkFileUriExposed(Uri.java:2346) at android.content.ClipData.prepareToLeaveProcess(ClipData.java:832) at android.content.Intent.prepareToLeaveProcess(Intent.java:8909)
-
如何解决该问题了?
-
针对这个问题google提供了FileProvider,使用它可以生成content://url 来替代file://url
- 首先我们在res目录下新建一个xml的目录,然后创建一个file_paths.xml,然后配置你安装包目录,第一个参数可随意指定,第二参数为目录路径,需要准确指定,那它几种标签,可根据需要进行配置
-
<files-path name="name" path="path" /> 物理路径相当于Context.getFilesDir() + /path/
<cache-path name="name" path="path" /> 物理路径相当于Context.getCacheDir() + /path/
<external-path name="name" path="path" /> 物理路径相当于Environment.getExternalStorageDirectory() + /path/
<external-files-path name="name" path="path" /> 物理路径相当于Context.getExternalFilesDir(String) + /path/
<external-cache-path name="name" path="path" /> 物理路径相当于Context.getExternalCacheDir() + /path/
-
在清单文件中进行注册
<provider android:name="android.support.v4.content.FileProvider" android:authorities="你的包名.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
-
代码
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N) {//判读版本是否在7.0以上 Uri apkUri = FileProvider.getUriForFile(this, "你的包名.fileprovider", apkFile);//在AndroidManifest中的android:authorities值 Intent install = new Intent(Intent.ACTION_VIEW); install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//添加这一句表示对目标应用临时授权该Uri所代表的文件 install.setDataAndType(apkUri, "application/vnd.android.package-archive"); startActivity(install); } else{ Intent install = new Intent(Intent.ACTION_VIEW); install.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(install); }
问题2
-
Android为了进一步提高应用的安全性,设置了未知来源应用安装权限,根据用户的需求来是否允许安装不是从应用商店下载的apk,所以刚开始的时候,发现8.0.9.0的手机上即使设置FileProvider同样会安装失败,真是是崩溃,又的找google爸爸了。。。
-
如何解决该问题了?
-
在安装apk的时候我们需要检测我们是否有安装权限,若有我们在进行安装,若没有该权限,我们可以提示用户需要该权限,并且跳转到对应应用的应用权限管理中,让用户开启,之后再应用检测用户是拒绝了还是打开了,若打开了,表示我们有安装权限,此时我们才可以正常安装升级包了
-
1.再清单文件中加入权限
<!--安卓8.0打开apk安装更新--> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
-
检测是否允许安装未知来源的app
@RequiresApi(api = Build.VERSION_CODES.O) public static boolean isHasInstallPermissionWithO(Context context){ if (context == null){ return false; } return context.getPackageManager().canRequestPackageInstalls(); }
-
-
若返回true,直接启动Activity进行安装,反之,跳转到应用权限设置界面让用户去主动打开
/** * 开启设置安装未知来源应用权限界面 * @param context */ @RequiresApi (api = Build.VERSION_CODES.O) public static void startInstallPermissionSettingActivity(Context context) { if (context == null){ return; } Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES); ((Activity)context).startActivityForResult(intent,REQUEST_CODE_APP_INSTALL); }
-
在用户设置后可通过onActivityResult()进行监测,判断是否打开了权限,不知道为什么我在这里获取到的resultCode不是Activity_OK,但权限的确是打开了,可以安装,所以比较建议再次检测一次应用是否有安装权限,若返回true,则开始安装更新包
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == AppUtils.REQUEST_CODE_APP_INSTALL) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val hasInstallPermission = AppUtils.isHasInstallPermissionWithO(mContext) if (!hasInstallPermission) { return } else { instaillApp() } } else { instaillApp() } } } } } else { instaillApp() } } }
-