好记性不如烂笔头
前期准备 要安装的apk放到assets文件,三款手机 系统4.4 6.0 7.0
关于assets文件知识可以参考这里
步骤1 copy assets 文件到 存储目录
1.1 清单权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
1
1.2 调用copyAssets方法
private void copyAssets(Context context, String filename) {
AssetManager assetManager = context.getAssets();
InputStream in;
OutputStream out;
try {
in = assetManager.open(filename);
String outFileName = Environment.getExternalStorageDirectory().
getAbsolutePath();//保存到外部存储,大部分设备是sd卡根目录
String copyName = System.currentTimeMillis() + "qq.apk";//copy后具体名称
File outFile = new File(outFileName, copyName);
out = new FileOutputStream(outFile);
copyFile(in, out);
in.close();
out.flush();
out.close();
} catch (IOException e) {
System.out.println("");
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
copyAssets() 传入两个参数,一个是Context 另一个是 assets 中的文件名(这里传“qq.apk”)
1.3 运行结果(仅限6.0以下) 这里虽然调用.getExternalStorageDirectory()但是系统默认是集成的sd卡,也就是常见的 /storage/emulated/0 俗称 内部存储
1.4 安装
修改 copyAssets 方法如下
private void copyAssets(Context context, String filename) {
AssetManager assetManager = context.getAssets();
InputStream in;
OutputStream out;
try {
in = assetManager.open(filename);
String outFileName = Environment.getExternalStorageDirectory()
.getAbsolutePath();//保存到外部存储,大部分设备是sd卡根目录
String copyName = System.currentTimeMillis() + "qq.apk";//copy后具体名称
File outFile = new File(outFileName, copyName);
out = new FileOutputStream(outFile);
copyFile(in, out);
in.close();
out.flush();
out.close();
//copy 完毕,直接安装
File file = new File(outFileName, copyName);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
context.startActivity(intent);
} catch (IOException e) {
System.out.println("");
}
}
1.5 运行结果
步骤2 >6.0 系统权限问题
当以上代码在 >6.0 系统运行时,会遇到权限问题 /storage/emulated/0/1493862005311qq.apk: open failed: EACCES (Permission denied)
这是因为 6.0 系统引入了动态权限管理,针对此问题我们可以用三方的PermissionsDispatcher当然了,这里不对三方过多依赖,我们自己请求权限, 首先代码动态请求,然后在系统回调中处理逻辑
//请求权限
private static final int READ_CONTACTS_REQUEST = 1;
public void getPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
//发起请求获得用户许可,可以在此请求多个权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
READ_CONTACTS_REQUEST);
} else {
//权限许可过,直接拷贝
copyAssets(MainActivity.this, "qq.apk");
}
}
//系统方法,从requestPermissions()方法回调结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
//确保是我们的请求
if (requestCode == READ_CONTACTS_REQUEST) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "写文件权限被授予", Toast.LENGTH_SHORT).show();
copyAssets(MainActivity.this, "qq.apk");
} else if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "写文件权限被拒绝", Toast.LENGTH_SHORT).show();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
这时我们不直接调用copyAssets 而是调用getPermission 方法,其流程大概如下
开 始
查看权限是否赋予了?
直接拷贝
结束
请求权限
onRequestPermissionsResult结果
用户是否赋予权限?
直接拷贝
结束
用户没有给予权限逻辑
yes
no
yes
no
具体情况如图,第一次运行会动态提示
步骤3 7.0 的文件共享问题
以上代码运行在 7.0 设备上会报错 ,报错位置就是在安装时
File file = new File(outFileName, copyName);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
context.startActivity(intent);
报错内容如下
file:///storage/emulated/0/1493799271025qq.apk exposed beyond app through Intent.getData()
针对这个问题最常用的就是 FileProvider ,这里也一样
3.1 FileProvider 清单配置
provider 作为四大组件之一,要在application节点下进行配置,就跟 activity 一样
<provider
android:name="android.support.v4.content.FileProvider"
//这里是一个口令字符串,等会儿获取文件时要用,内容可以随意但前后必须一致
android:authorities="com.nf.hp.assets.provider"
android:grantUriPermissions="true"
android:exported="false">
<!--元数据-->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
//这里配置共享文件路径,provider_paths只是文件名,可任意取
android:resource="@xml/provider_paths" />
</provider>
3.2 创建路径配置文件
在 res 下创建 xml 文件夹 里面新建 provider_paths文件 注意与清单中名称一致
provider_paths 内容如下
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path path="." name="." />
</paths>
其中 <external-path path="." name="." /> 对应getExternalStorageDirectory()下所有文件及其子文件可以对外共享
3.3 再次修改 copyAssets 中安装方法 如下
//copy 完毕,直接安装
File file = new File(outFileName, copyName);
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//>7.0时 用 provider 共享
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, "com.nf.hp.assets.provider",//这里与provider清单中一致
new File(outFileName, copyName));
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
}
ontext.startActivity(intent);
备注
针对 >6.0 时权限问题,可能有些同学发现并没有文章中错误,其实除了手机系统,还与工程的配置有关 .若 compileSdkVersion 和 targetSdkVersion 都比较小 比如小于6.0(API23),那么即使系统是 6.0 也不会出现权限问题
---------------------
作者:hepann44
来源:CSDN
原文:https://blog.csdn.net/hepann44/article/details/71135688
版权声明:本文为博主原创文章,转载请附上博文链接!