如何打开所有Android版本的APK文件

android开发人员

背景

到目前为止,使用以下意图可以轻松地安装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"/>

问题

  1. 为什么会发生?

  2. 我创建的自定义提供程序有什么问题吗?是否需要标志?可以创建URI吗?我应该在其中添加当前应用的程序包名称吗?

  3. 我是否应该仅检查它是否为Android API 24及更高版本,如果是,则使用提供程序,如果不是,则使用普通的Uri.fromFile调用?如果我使用它,则支持库实际上失去了它的用途,因为它将用于更新的Android版本...

  4. 支持库FileProvider是否足以满足所有用例(当然,如果我具有外部存储权限)?

常用软件

我可以只检查Android OS的版本,然后使用这两种方法,但是正如我所读的,应该使用一种方法:FileProvider。

嗯,俗话说,“探戈需要两个人”。

使用任何特定方案(filecontenthttp等),你不仅需要提供在该计划中的数据,但收件人需要能够支持接受该方案中的数据。

对于软件包安装程序,content仅在Android 7.0中添加了作为方案的支持(然后,可能只是因为我指出了问题)。

为什么会发生?

由于谷歌(见)。

我创建的自定义提供程序有什么问题吗?

可能不会。

我是否应该仅检查它是否为Android API 24及更高版本,如果是,则使用提供程序,如果不是,则使用普通的Uri.fromFile调用?

是。或者,如果您愿意,抓住ActivityNotFoundException并对此做出反应,或者使用PackageManagerresolveActivity()提前查看给定的Intent(例如,带有的content Uri)是否可以正常工作。

如果使用此功能,则支持库实际上失去了它的作用,因为它将用于更新的Android版本

“支持库”与较旧的Android版本无关。各种Android支持工件中的类中只有一小部分是反向移植或兼容性垫片。它浩大的数量- ,等-只是类,谷歌希望提供和支持,但希望他们固件外可用。FileProviderViewPagerConstraintLayout

支持库FileProvider是否足以满足所有用例

仅在Android 7.0+上。同样,现有的Android软件包安装程序不支持contentAndroid 7.0之前的方案。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在Android 10中打开APK文件

如何打开APK文件并安装?

列出我的Android应用程序打开的所有文件

如何使用Android Gradle插件的0.14+版本向APK文件添加版本号?

如何使用“ KeepRevisionForever”属性保留所有文件版本

Firebase实时数据库未在Android调试APK版本上返回所有值

如何用git打开所有修改过的文件?

如何找到在给定目录中打开的所有文件?

如何打开Java中以特定前缀开头的所有文件?

[Golang] [Linux]-如何获取当前用户的所有打开文件

如何在PhpStorm中打开所有修改的文件

如何使FileZilla一键打开所有必需的文件

如何在python中打开目录中的所有文件?

如何打开提交 VS CODE 的所有文件

如何打开ls命令产生的所有文件?

从命令行打开访问文件-涵盖所有版本

Android Studio具有2个用于同一活动的布局活动文件。有必要吗?如何为所有较新的SDK版本保留1个文件?

如何使用vim打开当前目录和所有子目录中的所有文件?

如何打开所有标志?

如何为具有不同cpu架构的相同apk文件创建不同的版本号

如何遍历所有 CSV 文件,打开每个文件并对每个文件执行一些操作?

如何签署Android APK文件

如何在 android studio 中制作一个在所有手机上运行的 android apk 文件,仅用于测试目的

androidTestUtil 配置中并非所有文件都是 APK 导致的 Android Lint Infrastructure 错误

在Android中以编程方式将所有安装的APK文件备份到sdcard中

从android apk中的.smali文件中提取所有API调用

文件所有版本的FileNet属性

Cygwin:cygstart *不打开所有文件

打开目录中的所有文件 - Python