到目前为止,使用以下意图可以轻松地安装APK文件:
final Intent intent=new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
但是,如果你的应用程序中的Android API 24以上(牛轧糖- 7.0),你就可以或更新运行这段代码,你会得到一个异常,如图所示这里,例如:
android.os.FileUriExposedException: file:///storage/emulated/0/sample.apk exposed beyond app through Intent.getData()
所以我被告知:使用支持库的FileProvider类,如下所示:
final Intent intent = new Intent(Intent.ACTION_VIEW)//
.setDataAndType(android.support.v4.content.FileProvider.getUriForFile(context,
context.getPackageName() + ".provider", apkFile),
"application/vnd.android.package-archive").addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
清单:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
res / xml / provider_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--<external-path name="external_files" path="."/>-->
<external-path
name="files_root"
path="Android/data/${applicationId}"/>
<external-path
name="external_storage_root"
path="."/>
</paths>
但是,现在它仅适用于Android Nougat。在Android 5.0上,它将引发异常:ActivityNotFoundException。
我可以只检查Android OS的版本,然后使用这两种方法,但是正如我所读的,应该使用一种方法:FileProvider。
因此,我尝试使用自己的ContentProvider充当FileProvider,但与支持库的FileProvider却遇到了相同的例外。
这是我的代码:
final Intent intent = new Intent(Intent.ACTION_VIEW)
.setDataAndType(OpenFileProvider.prepareSingleFileProviderFile(apkFilePath),
"application/vnd.android.package-archive")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
OpenFileProvider.java
public class OpenFileProvider extends ContentProvider {
private static final String FILE_PROVIDER_AUTHORITY = "open_file_provider";
private static final String[] DEFAULT_PROJECTION = new String[]{MediaColumns.DATA, MediaColumns.DISPLAY_NAME, MediaColumns.SIZE};
public static Uri prepareSingleFileProviderFile(String filePath) {
final String encodedFilePath = new String(Base64.encode(filePath.getBytes(), Base64.URL_SAFE));
final Uri uri = Uri.parse("content://" + FILE_PROVIDER_AUTHORITY + "/" + encodedFilePath);
return uri;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public String getType(@NonNull Uri uri) {
String fileName = getFileName(uri);
if (fileName == null)
return null;
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileName);
}
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
final String fileName = getFileName(uri);
if (fileName == null)
return null;
final File file = new File(fileName);
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
final String filePath = getFileName(uri);
if (filePath == null)
return null;
final String[] columnNames = (projection == null) ? DEFAULT_PROJECTION : projection;
final MatrixCursor ret = new MatrixCursor(columnNames);
final Object[] values = new Object[columnNames.length];
for (int i = 0, count = columnNames.length; i < count; ++i) {
String column = columnNames[i];
switch (column) {
case MediaColumns.DATA:
values[i] = uri.toString();
break;
case MediaColumns.DISPLAY_NAME:
values[i] = extractFileName(uri);
break;
case MediaColumns.SIZE:
File file = new File(filePath);
values[i] = file.length();
break;
}
}
ret.addRow(values);
return ret;
}
private static String getFileName(Uri uri) {
String path = uri.getLastPathSegment();
return path != null ? new String(Base64.decode(path, Base64.URL_SAFE)) : null;
}
private static String extractFileName(Uri uri) {
String path = getFileName(uri);
return path;
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0; // not supported
}
@Override
public int delete(@NonNull Uri uri, String arg1, String[] arg2) {
return 0; // not supported
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
return null; // not supported
}
}
表现
<provider
android:name=".utils.apps_utils.OpenFileProvider"
android:authorities="open_file_provider"
android:exported="true"
android:grantUriPermissions="true"
android:multiprocess="true"/>
为什么会发生?
我创建的自定义提供程序有什么问题吗?是否需要标志?可以创建URI吗?我应该在其中添加当前应用的程序包名称吗?
我是否应该仅检查它是否为Android API 24及更高版本,如果是,则使用提供程序,如果不是,则使用普通的Uri.fromFile调用?如果我使用它,则支持库实际上失去了它的用途,因为它将用于更新的Android版本...
支持库FileProvider是否足以满足所有用例(当然,如果我具有外部存储权限)?
我可以只检查Android OS的版本,然后使用这两种方法,但是正如我所读的,应该使用一种方法:FileProvider。
嗯,俗话说,“探戈需要两个人”。
使用任何特定方案(file
,content
,http
等),你不仅需要提供在该计划中的数据,但收件人需要能够支持接受该方案中的数据。
对于软件包安装程序,content
仅在Android 7.0中添加了对作为方案的支持(然后,可能只是因为我指出了问题)。
为什么会发生?
我创建的自定义提供程序有什么问题吗?
可能不会。
我是否应该仅检查它是否为Android API 24及更高版本,如果是,则使用提供程序,如果不是,则使用普通的Uri.fromFile调用?
是。或者,如果您愿意,抓住ActivityNotFoundException
并对此做出反应,或者使用PackageManager
和resolveActivity()
提前查看给定的Intent
(例如,带有的content
Uri
)是否可以正常工作。
如果使用此功能,则支持库实际上失去了它的作用,因为它将用于更新的Android版本
“支持库”与较旧的Android版本无关。各种Android支持工件中的类中只有一小部分是反向移植或兼容性垫片。它浩大的数量- ,,等-只是类,谷歌希望提供和支持,但希望他们固件外可用。FileProvider
ViewPager
ConstraintLayout
支持库FileProvider是否足以满足所有用例
仅在Android 7.0+上。同样,现有的Android软件包安装程序不支持content
Android 7.0之前的方案。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句