如何打开所有Android版本的APK文件 [英] How to open an APK file for all Android versions

查看:133
本文介绍了如何打开所有Android版本的APK文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

到目前为止,使用此意图可以轻松地安装APK文件:

So far, there was an easy way to install an APK file, using this intent:

    final Intent intent=new Intent(Intent.ACTION_VIEW)
            .setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");

但是,如果您的应用程序针对的是Android API 24及更高版本(牛轧糖-7.0),并且在其上运行此代码或更高版本,则会出现异常,如

But, if your app targets Android API 24 and above (Nougat - 7.0) , and you run this code on it or newer, you will get an exception, as shown here , for example:

android.os.FileUriExposedException: file:///storage/emulated/0/sample.apk exposed beyond app through Intent.getData()

问题

所以我做了我被告知的事情:像这样使用支持库的FileProvider类:

The problem

So I did what I was told: use the support library's FileProvider class, as such:

    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.

But, now it works only on Android Nougat. On Android 5.0, it throws an exception: ActivityNotFoundException.

我可以添加一个检查Android OS版本的方法,并使用这两种方法,但是正如我已阅读的,应该使用一种方法:FileProvider.

I can just add a check for the version of Android OS, and use either methods, but as I've read, there should be a single method to use: FileProvider.

因此,我尝试使用自己的ContentProvider作为FileProvider,但与支持库的FileProvider却遇到了相同的例外.

So, what I tried is to use my own ContentProvider that acts as FileProvider, but I got the same exception as of the support library's 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. 为什么会发生?

  1. Why does it occur?

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

Is there anything wrong with the custom provider I've created? Is the flag needed? Is the URI creation ok ? Should I add the current app's package name to it?

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

Should I just add a check if it's Android API 24 and above, and if so, use the provider, and if not, use a normal Uri.fromFile call ? If I use this, the support library actually loses its purpose, because it will be used for newer Android versions...

对于所有用例,支持库FileProvider是否足够(当然,如果我具有外部存储权限)?

Will the support library FileProvider be enough for all use cases (given that I do have external storage permission, of course) ?

推荐答案

我可以添加一个检查Android OS版本的方法,并使用这两种方法,但是正如我所读的,应该使用一种方法:FileProvider.

I can just add a check for the version of Android OS, and use either methods, but as I've read, there should be a single method to use: FileProvider.

俗话说,探戈需要两个人".

Well, as the saying goes, "it takes two to tango".

要使用任何特定方案(filecontenthttp等),不仅必须提供该方案中的数据,而且接收者还必须能够支持接受数据在那个方案中.

To use any particular scheme (file, content, http, etc.), not only do you have to provide the data in that scheme, but the recipient needs to be able to support accepting the data in that scheme.

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

In the case of the package installer, support for content as a scheme was only added in Android 7.0 (and then, perhaps only because I pointed out the problem).

为什么会发生?

Why does it occur?

因为Google(请参阅).

Because Google (see this and this).

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

Is there anything wrong with the custom provider I've created?

可能不是.

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

Should I just add a check if it's Android API 24 and above, and if so, use the provider, and if not, use a normal Uri.fromFile call ?

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

Yes. Or, if you prefer, catch the ActivityNotFoundException and react to that, or use PackageManager and resolveActivity() to see ahead of time if a given Intent (e.g., one with a content Uri) will work properly.

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

If I use this, the support library actually loses its purpose, because it will be used for newer Android versions

支持库"与较旧的Android版本无关.各种Android支持工件中的类中只有一小部分是反向移植或兼容性垫片.数量庞大-— FileProviderViewPagerConstraintLayout等.只是Google想要提供和支持的类,但是想使它们在固件之外可用.

The "support library" has little to do with newer-vs.-older Android versions. Only a small percentage of the classes across the various Android Support artifacts are backports or compatibility shims. Vast quantities of it — FileProvider, ViewPager, ConstraintLayout, etc. — are simply classes that Google wanted to provide and support but wanted to make them available outside of the firmware.

对于所有用例,支持库FileProvider是否足够

Will the support library FileProvider be enough for all use cases

仅在Android 7.0及更高版本上.同样,现有的Android软件包安装程序不支持Android 7.0之前的content方案.

Only on Android 7.0+. Again, the stock Android package installer does not support content schemes prior to Android 7.0.

这篇关于如何打开所有Android版本的APK文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆