简体   繁体   English

Android:不是文件 URI:在下载 .apk 文件时

[英]Android: Not a file URI: while downloading .apk file

I want to download .apk file and install it.我想下载 .apk 文件并安装它。 When I'm not using FileProvider, everything is going well, but when I create uri from file using FileProvider, I've got IllegalArgumentException: Not a file URI: content://pl.rasztabiga.klasa1a.provider/external_storage_root/klasa1a.apk on line当我不使用 FileProvider 时,一切都很顺利,但是当我使用 FileProvider 从文件创建 uri 时,我得到了 IllegalArgumentException: Not a file URI: content://pl.rasztabiga.klasa1a.provider/external_storage_root/klasa1a。 apk上线

final long downloadId = manager.enqueue(request);

I tried everything from stackoverflow but nothing helped.我尝试了 stackoverflow 中的所有内容,但没有任何帮助。 Here is my code:这是我的代码:

File file = new File(Environment.getExternalStorageDirectory(), "klasa1a.apk");
final Uri uri = FileProvider.getUriForFile(MainActivity.this, getApplicationContext().getPackageName() + ".provider", file);

        //Delete update file if exists
        //File file = new File(destination);
        if (file.exists())
            file.delete();

        //get url of app on server
        String url = "http://rasztabiga.ct8.pl/klasa1a.apk";

        //set downloadmanager
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
        request.setDescription("Downloading new version");
        request.setTitle(MainActivity.this.getString(R.string.app_name));

        //set destination
        request.setDestinationUri(uri);

        // get download service and enqueue file
        final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        final long downloadId = manager.enqueue(request);

        //set BroadcastReceiver to install app when .apk is downloaded
        BroadcastReceiver onComplete = new BroadcastReceiver() {
            public void onReceive(Context ctxt, Intent intent) {
                Intent install = new Intent(Intent.ACTION_VIEW);
                install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                install.setDataAndType(uri,
                        manager.getMimeTypeForDownloadedFile(downloadId));
                startActivity(install);

                unregisterReceiver(this);
                finish();
            }
        };
        //register receiver for when .apk download is compete
        registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

ACTION_VIEW and ACTION_INSTALL_PACKAGE only support the content scheme as of Android 7.0. ACTION_VIEWACTION_INSTALL_PACKAGE仅支持 Android 7.0 以后的content方案。 Prior to that, you have no choice but to use file .在此之前,您别无选择,只能使用file So, change:所以,改变:

final Uri uri = FileProvider.getUriForFile(MainActivity.this, getApplicationContext().getPackageName() + ".provider", file);

to:到:

final Uri uri = (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N) ?
    FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", file) :
    Uri.fromFile(file);

The problem was in DownloadManager.问题出在下载管理器中。 It cannot parse uri's as "content://", only as "file://" so since sdk24, we cannot use it.它无法将 uri 解析为“content://”,只能解析为“file://”,因此从 sdk24 开始,我们无法使用它。 Using common IOStreams and HttpURLConnection everything works fine.使用常见的 IOStreams 和 HttpURLConnection 一切正常。 Thanks to @CommonsWare for showing me his project.感谢@CommonsWare 向我展示了他的项目。 That's how it looks like now:这就是现在的样子:

        File file = new File(Environment.getExternalStorageDirectory(), "klasa1a.apk");
        final Uri uri = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) ?
                FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", file) :
                Uri.fromFile(file);

        //Delete update file if exists
        //File file = new File(destination);
        if (file.exists())
            //file.delete() - test this, I think sometimes it doesnt work
            file.delete();

        //get url of app on server
        String url = "http://rasztabiga.ct8.pl/klasa1a.apk";

        InputStream input = null;
        OutputStream output = null;
        HttpURLConnection connection = null;
        try {
            URL sUrl = new URL(url);
            connection = (HttpURLConnection) sUrl.openConnection();
            connection.connect();

            // download the file
            input = connection.getInputStream();
            output = new FileOutputStream(file);

            byte data[] = new byte[4096];
            int count;
            while ((count = input.read(data)) != -1) {
                // allow canceling with back button
                if (isCancelled()) {
                    input.close();
                    return null;
                }

                output.write(data, 0, count);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (output != null)
                    output.close();
                if (input != null)
                    input.close();
            } catch (IOException ignored) {
            }

            if (connection != null)
                connection.disconnect();
        }

        Intent install = new Intent(Intent.ACTION_INSTALL_PACKAGE)
                .setData(uri)
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivity(install);

        return null;
    }

For those that still want to use DownloadManager and find that the answer by CommonsWare doesn't solve the problem, you need to include an exception for APK file对于那些仍然想使用DownloadManager并发现CommonsWare的答案没有解决问题的人,您需要为APK文件包含一个例外

final Uri uri = (!file.getAbsolutePath().endsWith(".apk") && Build.VERSION.SDK_INT>=Build.VERSION_CODES.N) ?
    FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", file) :
    Uri.fromFile(file);
request.setDestinationUri(uri);

Which is a shorter version of哪个是更短的版本

if (!file.getAbsolutePath().endsWith(".apk") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    request.setDestinationUri(FileProvider.getUriForFile(MainActivity.this,  BuildConfig.APPLICATION_ID + ".provider", download));
} else {
    request.setDestinationUri(Uri.fromFile(file));
}

that may be easier to follow.这可能更容易遵循。

The short answer is that apk files should be handled with Uri.fromFile , but this does not have to come at the cost of changing the entire process.简短的回答是 apk 文件应该用Uri.fromFile处理,但这不必以改变整个过程为代价。

If you want to be thorough, though, you can also catch the IllegalArgumentException for Not a file URI and use that to attempt Uri.fromFile但是,如果您想要彻底,您还可以捕获IllegalArgumentException for Not a file URI并使用它来尝试Uri.fromFile

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM