Android 通过代码安装 APK的方法详解
在APK开发中,通过Java代码来打开系统的安装程序以安装APK并不是什么难事,一般的Android系统都有开放这一功能。
- 但随着Android系统版本的迭代,其对于权限的把控越来越严格,或者说是变得越来越注重安全性。这就导致了以前可以通过很简单的几行代码就能实现的功能,现在要复杂很多。
- 对于通过代码打开系统安装程序这一功能的限制,其分水岭在Android7.0,即AndroidN上。通常在AndroidN以上的系统使用一种做法,以下则使用另一种做法。
- 传统的通过代码安装APK的方式
Fileapk=newFile(...); Uriuri=Uri.fromFile(apk); Intentintent=newIntent(); intent.setClassName("com.android.packageinstaller","com.android.packageinstaller.PackageInstallerActivity"); intent.setData(uri); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
这种方法简单粗暴且实用,只要知晓要安装的APK的位置,并拥有访问权限即可。
但现在市面上主流的Android手机系统版本都已经要高于7.0了,这一方法几乎已经没有用了
高版本系统上的通过代码安装APK的方式
Fileapk=newFile(...); Intentintent=newIntent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uriuri=FileProvider.getUriForFile(this,"com.apk.demo.fileprovider",apk); intent.setDataAndType(uri,"application/vnd.android.package-archive"); startActivity(intent);
说到权限问题,在Android版本不断提高的趋势下,系统得安全性也越来越高,很多权限不只是在清单文件里面注册那么简单,内存卡得读写权限属于危险权限,需要我们使用代码动态添加,这里我使用了RxPermiision框架,遇到9.0或者更高版本的系统时获取权限的方法可能会不同。
privatevoidrxPermission(){ RxPermissionsrxPermissions=newRxPermissions(this); rxPermissions.request(Manifest.permission.WRITE_EXTERNAL_STORAGE).subscribe(newConsumer(){ @Override publicvoidaccept(Booleangranted)throwsException{ if(granted){ //权限允许 //在这里可以添加自己的操作 }else{ //权限被拒绝 } } }); }
Android更多权限得查询:https://www.jianshu.com/p/24f79a70025b
上面这段代码安装代码看起来似乎和传统的方式并没有太大的区别是吗?
确实是,但它真正的区别并没有在Java代码上体现出来。
在高版本系统中,APK已经不能直接访问其它APK的私有数据了。
什么是APK的私有数据?
APK在安装过程中于data目录下创建的专属目录自然是其私有数据无疑。另外,只要是在应用程序中封装的File对象,不管这个文件本身是不是由该程序创建的,那这个文件都属于该程序的“私有数据”。举个例子来说,假设我们将手机连接到电脑,通过adbpush的方式往sdcard目录下推了一个APK文件进去。然后我们自行编写了一段代码,将这个sdcard中的安装包传到系统的PackageInstaller中去安装,都会报安全错误,因为这个位于sdcard目录下文件对我们这段代码来说是“私有数据”,不允许直接暴露给PackageInstaller。
下面就来看看在高版本系统中暴露“私有数据”给其它程序的方法。
在高版本中,Android7.0及以上,开放(暴露)私有数据的唯一方式是通过ContentProvider来实现。
具体的步骤大致如下:
- 配置AndroidManifest.xml中的ContentProvider信息;
- 配置要开放的paths信息;
- 在Java代码中通过FileProvider封装文件信息。
1、AndroidManifest.xml配置
前面说过,高版本系统中其实就是将以前的直接开放变成通过ContentProvider来间接开放。因此我们需要在AndroidManifest.xml中添加一个provider标签,示例如下:
- android:name属性填写的是FileProvider类的完整名称。这个类可以填写两个值,一个是位于support(android.support.v4.content.FileProvider)包下的,另一个是位于androidx(androidx.core.content.FileProvider)包下的。这两种都可以填写,本质上没有区别。但是要根据实际情况来决定用哪个,即要看你的工程引的是androidx支援包还是support支援包。关于support与androidx的关系本文就不再赘述了。
- android:authorities属性就是和普通的ContentProvider一样的用于访问文件资源的uri标签头。值内容根据实际需要来填写即可。
- android:exported这个属性表示的是:其他app能否访问这个provider
- android:grantUriPermissions这个属性用于给内容提供器的数据子集授权
- 如果内容提供器的grantUriPermissions属性被设置为true,那么权限能够被授予内容提供器范围内的任何数据。但是,如果grantUriPermission属性被设置为false,那么权限就只能授予这个元素所指定的数据子集。一个内容提供器能够包含任意多个元素。每个都只能指定一个路径(三个可能属性中的一个)。
- meta-data标签中的内容需要关注的是android:resource属性中的内容。这个属性的值引向一个自行配置的xml文件,这份xml文件记载的是设备中的路径信息,简单理解就是你想开放哪些目录中的文件资源给第三方使用的意思。关于这个xml的配置请看第2步的记载。
2、paths配置
- 通常的做法是在工程res目录下新建一个xml目录,并在该xml目录下新建一个xml文件。文件的名称必须与第1步中@xml/属性值中配置的一致。
- 根据第1步中的示例代码,我们需要新建一个file_paths.xml文件。这里我的apk是保留在程序的file文件加下得,该文件的内容如下所示:
其他路径的的配置方式请参考:https://editor.csdn.net/md?articleId=106670247
简单来说,就是将你要开放出去的路径的类型选好,然后填上该类型下的相对路径即可。
我们以示例详细说说:
这表示我们想开放程序内存里面的files目录,然后在files目录下的子路径是/apk,组合成绝对路径就是/data/con.xxx.xxx/files/apk。至于name标签则是用于ContentProvider标识使用的,一般来讲按需要设置成不同的值就可以了,这里我有一个子目录。
3、Java代码配置
Java代码的配置就没什么特别的了,直接以章节首部的代码来用就可以了。关键的代码其实只有一行:
Uriuri=FileProvider.getUriForFile(context,authority,file);
这里的三个参数分别为:
- context:这里表示需要传一个上下文过来
- authority:可以通代码在AndroidManifest.xml里面获得
- file:是你需要的安装的文件
Stringauthority=newStringBuilder(packageName).append(".provider").toString(); //这里的strFile文件的路径+名称;例如:/data/file/apk/xxx.apk Filef=newFile(strFile); Uriuri=FileProvider.getUriForFile(context,authority,file);
通常我们都会兼顾Android高低版本的系统,因此会使用如下所示的“混合型”代码:
publicvoidinstall(){ try{//这里有文件流的读写,需要处理一下异常 Intentintent=newIntent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){ //如果SDK版本>=24,即:Build.VERSION.SDK_INT>=24 StringpackageName=context.getApplicationContext().getPackageName(); Stringauthority=newStringBuilder(packageName).append(".provider").toString(); uri=FileProvider.getUriForFile(context,authority,file); intent.setDataAndType(uri,"application/vnd.android.package-archive"); }else{ uri=Uri.fromFile(file); intent.setDataAndType(uri,"application/vnd.android.package-archive"); } context.startActivity(intent); }catch(Exceptione){ e.printStackTrace(); } }
总结
到此这篇关于Android通过代码安装APK的方法详解的文章就介绍到这了,更多相关android代码安装apk内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!