Quiero descargar el archivo .apk e instalarlo. Cuando no estoy usando FileProvider, todo va bien, pero cuando creo uri desde un archivo usando FileProvider, tengo IllegalArgumentException: No es un URI de archivo: contenido: //pl.rasztabiga.klasa1a.provider/external_storage_root/klasa1a. apk en línea

final long downloadId = manager.enqueue(request);

Intenté todo, desde stackoverflow, pero nada me ayudó. Aquí está mi código:

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));
3
Bartłomiej Rasztabiga 24 dic. 2016 a las 00:00

3 respuestas

La mejor respuesta

ACTION_VIEW y ACTION_INSTALL_PACKAGE solo admiten el esquema content a partir de Android 7.0. Antes de eso, no tienes más remedio que usar file. Entonces, cambie:

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

Para:

final Uri uri = (Build.VERSION.SDK_INT>=Build.VERSION_CODES.N) ?
    FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", file) :
    Uri.fromFile(file);
4
CommonsWare 23 dic. 2016 a las 21:05

Para aquellos que todavía quieren usar DownloadManager y encuentran que la respuesta de CommonsWare no resuelve el problema, debe incluir una excepción para el archivo 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);

¿Cuál es una versión más corta de

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));
}

Eso puede ser más fácil de seguir.

La respuesta corta es que los archivos apk deben manejarse con Uri.fromFile, pero esto no tiene que tener el costo de cambiar todo el proceso.

Sin embargo, si quieres ser minucioso, también puedes atrapar el IllegalArgumentException para Not a file URI y usarlo para intentar Uri.fromFile

1
Abandoned Cart 1 dic. 2019 a las 01:35

El problema estaba en DownloadManager. No puede analizar uri como "contenido: //", solo como "archivo: //", por lo que desde sdk24, no podemos usarlo. Usando IOStreams y HttpURLConnection comunes, todo funciona bien. Gracias a @CommonsWare por mostrarme su proyecto. Así es como se ve ahora:

        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;
    }
2
Bartłomiej Rasztabiga 24 dic. 2016 a las 10:33