简体   繁体   中英

Android: Not a file URI: while downloading .apk file

I want to download .apk file and install it. 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

final long downloadId = manager.enqueue(request);

I tried everything from stackoverflow but nothing helped. 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. Prior to that, you have no choice but to use 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. Using common IOStreams and HttpURLConnection everything works fine. Thanks to @CommonsWare for showing me his project. 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

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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