除了发送彩信需要文件提供器,安装应用也需要FileProvider。不单单彩信的附件图片能到媒体库中查询,应用的APK安装包也可在媒体库找到。查找安装包依然借助于内容解析器,具体的实现过程和查询图片类似,比如事先声明如下的对象变量:
private List<ApkInfo> mApkList = new ArrayList<ApkInfo>(); // 安装包列表
private Uri mFilesUri = MediaStore.Files.getContentUri("external"); // 存储卡的Uri
private String[] mFilesColumn = new String[]{ // 媒体库的字段名称数组
MediaStore.Files.FileColumns._ID, // 编号
MediaStore.Files.FileColumns.TITLE, // 标题
MediaStore.Files.FileColumns.SIZE, // 文件大小
MediaStore.Files.FileColumns.DATA, // 文件路径
MediaStore.Files.FileColumns.MIME_TYPE}; // 媒体类型
再如通过内容解析器到媒体库查找安装包列表,具体的加载代码示例如下:
// 加载安装包列表
private void loadApkList() {
mApkList.clear(); // 清空安装包列表
// 查找存储卡上所有的apk文件,其中mime_type指定了APK的文件类型,或者判断文件路径是否以.apk结尾
Cursor cursor = getContentResolver().query(mFilesUri, mFilesColumn,
"mime_type='application/vnd.android.package-archive' or _data like '%.apk'", null, null);
if (cursor != null) {
// 下面遍历结果集,并逐个添加到安装包列表。简单起见只挑选前十个文件
for (int i=0; i<10 && cursor.moveToNext(); i++) {
ApkInfo apk = new ApkInfo(); // 创建一个安装包信息对象
apk.setId(cursor.getLong(0)); // 设置安装包编号
apk.setName(cursor.getString(1)); // 设置安装包名称
apk.setSize(cursor.getLong(2)); // 设置安装包的文件大小
apk.setPath(cursor.getString(3)); // 设置安装包的文件路径
mApkList.add(apk); // 添加至安装包列表
}
cursor.close(); // 关闭数据库游标
}
}
找到安装包之后,通常还要获取它的包名、版本名称、版本号等信息,此时可调用应用包管理器的getPackageArchiveInfo方法,从安装包文件中提取PackageInfo包信息。包信息对象的packageName属性值为应用包名,versionName属性值为版本名称,versionCode属性值为版本号。下面是利用弹窗展示包信息的代码例子:
// 显示安装apk的提示对话框
private void showAlert(final ApkInfo apkInfo) {
PackageManager pm = getPackageManager(); // 获取应用包管理器
// 获取apk文件的包信息
PackageInfo pi = pm.getPackageArchiveInfo(apkInfo.getPath(), PackageManager.GET_ACTIVITIES);
if (pi != null) { // 能找到包信息
String desc = String.format("应用包名:%s\n版本名称:%s\n版本编码:%s\n文件路径:%s",
pi.packageName, pi.versionName, pi.versionCode, apkInfo.getPath());
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("是否安装该应用?"); // 设置提醒对话框的标题
builder.setMessage(desc); // 设置提醒对话框的消息内容
builder.setPositiveButton("是", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
installApk(apkInfo.getPath()); // 安装指定路径的APK
}
});
builder.setNegativeButton("否", null);
builder.create().show(); // 显示提醒对话框
} else { // 未找到包信息
ToastUtil.show(this, "该安装包已经损坏,请选择其他安装包");
}
}
有了安装包的文件路径,就能打开系统的安装程序执行安装操作了,此时一样要把安装包的Uri对象传过去。应用安装的详细调用代码如下所示:
// 安装指定路径的APK
private void installApk(String path) {
Uri uri = Uri.parse(path); // 根据指定路径创建一个Uri对象
// 兼容Android7.0,把访问文件的Uri方式改为FileProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 通过FileProvider获得安装包文件的Uri访问方式
uri = FileProvider.getUriForFile(this,
BuildConfig.APPLICATION_ID + ".fileProvider", new File(path));
}
Intent intent = new Intent(Intent.ACTION_VIEW); // 创建一个浏览动作的意图
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 另外开启新页面
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 需要读权限
// 设置Uri的数据类型为APK文件
intent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(intent); // 启动系统自带的应用安装程序
}
注意,Android8.0开始安装应用需要申请权限REQUEST_INSTALL_PACKAGES,于是打开AndroidManifest.xml,补充下面的权限申请配置:
<!-- 安装应用请求,Android8.0需要 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
这下大功告成,编译运行App,打开测试页面自动加载安装包列表的界面如下图所示。
点击某项安装包,弹出如下图所示的确认对话框。
点击确认对话框的“是”按钮,便跳到了如下图所示的应用安装页面,剩下的安装操作就交给系统了。
点此查看Android开发笔记的完整目录