Accidente de FileProvider: npe que intenta invocar XmlResourceParser en una cadena nula

139

Esta es una parte de mi manifiesto:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.asd"
    android:versionCode="118"
    android:versionName="118" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />


    <application
        android:name="com.example.asd.AsdApplication"
        android:allowBackup="true"
        android:allowTaskReparenting="true"
        android:theme="@style/AsdTheme" >
        ...

        <provider
            android:name="com.example.asd.database.hq.ContentProviderDB"
            android:authorities="ourContentProviderAuthorities" >
        </provider>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.asd.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>

       ...
    </application>

</manifest>

Este es el archivo filepaths en raw / xml / filepaths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="media"/>
</paths>

Descargo un video de Internet y lo guardo en el almacenamiento interno de esta manera:

public static boolean saveInputStreamToInternalStorageFile(Context context, String filename, byte[] dataToWrite, Context ctx) {
    FileOutputStream fos;
    try {
        fos = new FileOutputStream(context.getFilesDir() + File.separator + filename);

        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(dataToWrite);
        oos.close();
        return true;
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        return false;
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }
}

Intento usarlo así:

private void playVideoFromDeviceWithWorkaround(String fileName) {

    File newFile = new File(getFilesDir(), fileName);
    Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.example.asd", newFile);

    try {
        vvVideoFullscreen.setVideoURI(contentUri);
        showMediaControls = true;
        playVideo();
    } catch (Exception e) {
        playVideoFromNetwork();
    }

}

En esta línea:

Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.example.asd", newFile); 

Obtuve el siguiente error:

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:560)
at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:534)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:376)
JK
fuente
"Este es el archivo de rutas de archivo": ¿dónde está exactamente en su proyecto este archivo?
CommonsWare
1
raw / xml / filepaths.xml
JK
2
en las autoridades tengo .provider, agregado al paquete de la aplicación, pero cuando llamo a getUriForFile, no agrego .provider. tal vez tthats que, Im probando en este momento
JK
44
Sí, ese era el problema
JK

Respuestas:

264

El problema era que en Manifiesto tenía esta línea:

android:authorities="com.example.asd.fileprovider"

y al llamar a getUriForFile estaba pasando:

Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.example.asd", newFile); 

Así que cambió de "com.example.asd"a "com.example.asd.fileprovider"y funcionó

JK
fuente
1
Gracias funcionó para mí. Primero estaba haciendo así. android: autoridades = "$ {applicationId} .fileprovider Ahora lo cambié a com.memecreator.fileprovider mi nombre real del paquete
Rayyan
2
De acuerdo con @Rayyan, mejor usar android:authorities="${applicationId}"siempre; El código nunca tendrá la culpa.
sud007
Trabajó para mi. Gracias
landrykapela
2
Esta solución no funcionó para mí ... Todavía recibo una excepción de puntero nulo después de verificar para asegurarme de que sea correcto.
wesley franks
44

Puede hacer esto sin codificar el nombre del paquete con un beneficio adicional de ser capaz de ejecutar múltiples variantes en el mismo dispositivo (piensa releasey debugcon applicationIdSuffix, consulte estos temas ):

Residencia en FileProvider.java:560

final ProviderInfo info = context.getPackageManager()
        .resolveContentProvider(authority, PackageManager.GET_META_DATA);
final XmlResourceParser in = info.loadXmlMetaData( //560
        context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);

estabas usando el error authorityy no encontró el ContentProvider( info == null).

Cambie su manifiesto a ( ${applicationId}será reemplazado por Manifest Merger)

android:authorities="${applicationId}.share"

y

Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".share", result);

El .sharesufijo es opcional, en caso de que tenga un real ContentProviderque es mejor tener el nombre del paquete como autoridad.

TWiStErRob
fuente
Trabajó para mí ... He usado las dos últimas líneas y su trabajo perfectamente
Gracias
24

En mi caso, recibí el error porque el

BuildConfig.APPLICATION_ID

estaba siendo importado de

import android.support.v4.BuildConfig;

Entonces, la cadena que devolvió fue en "android.support.v4"lugar del nombre del paquete de mi proyecto. Verifique que el archivo de importación sea de usted import project.Buildconfigy no de otro. Ejemplo:

import com.example.yourProjectName.BuildConfig;

Finalmente, en la <provider>etiqueta en Manifiesto, android:authorities="${applicationId}"siempre tengo que obtener el nombre del paquete de mi proyecto como autoridad

<manifest>
   ..
   ..
   <application>
    ..
    ..
       <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/ruta_fileprovider" />
        </provider>

    </application>

</manifest>
Gian Gomen
fuente
1
¿Qué es Android: resource = "@ xml / ruta_fileprovider"
Ananta Prasad
1
@AnantaPrasad <? Xml version = "1.0" encoding = "utf-8"?> <paths> <external-path name = "external_files" path = "." /> <files-path name = "files" path = "." /> <cache-path name = "cache" path = "." /> </paths>
Príncipe
5

Primero, asegúrese de que su proveedor android:authoritiesno entre en conflicto con sus otros proveedores. Además de eso, puede elegir cualquier nombre para la última parte de su nombre: "proveedor", "proveedor de archivos", etc., pero la aplicación se bloquea cuando hay más de uno en la android:authoritieslista, mientras que la documentación indica que permite múltiples valores en la lista.

file://ahora no se permite adjuntar el esquema con Intent en targetSdkVersion> = 24 (Android N 7.0), solo content://se pasa siempre para todos los dispositivos (Android 5, 6 y 7). Pero descubrimos que Xiaomi rompe esta convención de Google y envía file://, por lo tanto, data.getData().getAuthority()da una cadena vacía.

final String uriScheme = currentUri.getScheme();
if ("content".equals(uriScheme)) {
    // getting full file path (works with some providers, i.e. Gallery)
    path = FileUtils.getPath(getContext(), currentUri);
    if (path != null) {
         currentFile = new File(path);
    }
} else if ("file".equals(uriScheme)) {
    // in a rare case we received file:// in currentUri, we need to:
    // 1. create new File variable from currentUri that looks like "file:///storage/emulated/0/download/50044382b.jpg"
    // 2. generate a proper content:// Uri for it
    currentFile = new File(currentUri.getPath());
    String authority = data.getData().getAuthority();
    if (authority != null && !authority.isEmpty()) {
        currentUri = FileProvider.getUriForFile(getActivity(), authority, currentFile);
    }
} else {
    // throw exception
}

Además, el error cuando se FileProvider.getUriForFile()produjo un bloqueo java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example/files/attachments/image.jpgse corrigió en Android Support Library v24.2.0. El problema era que FileProvider.java no veía carpetas de ruta externa.

soshial
fuente
1

Si está creando su AUTORIDAD en tiempo de ejecución utilizando, BuildConfigasegúrese de utilizar el nombre completo de la clase, incluido el nombre del paquete.

Malo:

final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";

Bueno:

final String AUTHORITY = com.mycompany.myapp.BuildConfig.APPLICATION_ID + ".provider";

Vahid Amiri
fuente
0

Lo siguiente funcionó para mí.

mUri = FileProvider.getUriForFile(this,
                    BuildConfig.APPLICATION_ID + ".provider",
                    fileObject);
Jose
fuente
0

Esto es lo que hice para solucionar el problema. Di nombre completo en Android: nombre. Funciona en android 6,7,8

    <provider android:authorities="${applicationId}.opener.provider" 
        android:exported="false" android:grantUriPermissions="true" 
        android:name="io.github.pwlin.cordova.plugins.fileopener2.FileProvider">
        <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
    </provider>
Arup Nayak
fuente
¿puede compartir su código para compartir archivos utilizando el proveedor de archivos del directorio interno?
Vishal Patoliya ツ
0

Deberías probarlo:

  Context context = PostAdapter.this.activity;
                    StringBuilder stringBuilder2 = new StringBuilder();
                    stringBuilder2.append(PostAdapter.this.activity.getPackageName());
                    stringBuilder2.append(".provider");
                    Uri uri;
                    uri = FileProvider.getUriForFile(context,stringBuilder2.toString(), newFile);
Chirag Patel
fuente
0

Esto es lo que necesitas hacer:

Uri fileURI = FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".fileprovider", file);
MrGuy
fuente