Arresto anomalo di FileProvider: npe tenta di richiamare XmlResourceParser su una stringa null


139

Questa è una parte del mio manifest:

<?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>

Questo è il file filepaths in 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>

Scarico un video da Internet e lo salvo nella memoria interna in questo modo:

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

Provo ad usarlo così:

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

}

A questa linea:

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

Ottengo il seguente errore:

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)

"Questo è il file filepaths" - dove si trova esattamente questo file nel tuo progetto?
CommonsWare,

1
raw / xml / filepaths.xml
JK,

2
nelle autorità ho .provider, aggiunto al pacchetto dell'app, ma quando chiamo getUriForFile, non aggiungo .provider. forse è così, sto testando proprio ora
JK,

4
Sì, quello era il problema
JK,

Risposte:


264

Il problema era che in Manifest avevo questa linea:

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

e quando chiamavo getUriForFile stavo passando:

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

Quindi è cambiato da "com.example.asd"a "com.example.asd.fileprovider"e ha funzionato


1
Grazie ha funzionato per me. Prima stavo facendo così. android: authority = "$ {applicationId} .fileprovider Ora l'ho cambiato in com.memecreator.fileprovider il mio attuale nome del pacchetto
Rayyan,

2
Concordo con @Rayyan, migliore da usare android:authorities="${applicationId}"sempre; il codice non sarà mai in errore.
sud007,

Ha funzionato per me. Grazie
landrykapela il

2
Questa soluzione non ha funzionato per me ... Ricevo ancora l'eccezione del puntatore null dopo aver verificato che sia corretto.
Wesley Frank,

44

Puoi farlo senza codificare il nome del pacchetto con un ulteriore vantaggio di poter eseguire più varianti sullo stesso dispositivo (pensa releasee debugcon applicationIdSuffix, vedi questi problemi ):

Basato su 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);

stavi usando il torto authoritye non hai trovato il ContentProvider( info == null).

Cambia il tuo manifest in ( ${applicationId}sarà sostituito da Manifest Fusione)

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

e

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

Il .sharesuffisso è facoltativo, nel caso in cui tu abbia un reale ContentProviderche è meglio avere il nome del pacchetto come autorità.


Ha funzionato per me ... Ho usato le ultime due righe e ha funzionato perfettamente..Grazie
Ravindra Kushwaha,

24

Nel mio caso, ho ricevuto l'errore perché il

BuildConfig.APPLICATION_ID

veniva importato da

import android.support.v4.BuildConfig;

Quindi la stringa che ha restituito era "android.support.v4"invece del nome del mio pacchetto di progetto. Controlla che il file di importazione proviene dal tuo import project.Buildconfige non da un altro. Esempio:

import com.example.yourProjectName.BuildConfig;

Infine, nel <provider>tag in Manifest devo android:authorities="${applicationId}"sempre ottenere il nome del mio pacchetto di progetto come autorità

<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>

1
cos'è android: resource = "@ xml / ruta_fileprovider"
Ananta Prasad

1
@AnantaPrasad <? Xml version = "1.0" encoding = "utf-8"?> <percorsi> <nome-percorso esterno = "file_esterno" percorso = "." /> <files-path name = "files" path = "." /> <cache-path name = "cache" path = "." /> </paths>
Prince,

5

Innanzitutto, assicurati che il tuo provider android:authoritiesnon sia in conflitto con gli altri provider. Inoltre puoi scegliere qualsiasi nome per l'ultima parte del suo nome: "provider", "fileprovider" ecc., Ma l'app si arresta in modo anomalo quando ce ne sono più di uno android:authoritieselencato, mentre la documentazione afferma che consente più valori elencati.

file://non è ora possibile associare lo schema con Intent su targetSdkVersion> = 24 (Android N 7.0), content://viene sempre passato solo per tutti i dispositivi (Android 5, 6 e 7). Ma abbiamo riscontrato che Xiaomi rompe questa convenzione di Google e invia file://, quindi data.getData().getAuthority()dà stringa vuota.

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
}

Inoltre, il bug quando si verificava un FileProvider.getUriForFile()arresto anomalo java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example/files/attachments/image.jpgveniva corretto nella Libreria di supporto Android v24.2.0. Il problema era che FileProvider.java non vedeva le cartelle del percorso esterno.


1

Se stai costruendo AUTHORITY in fase di esecuzione utilizzando BuildConfigassicurati di utilizzare il nome completo della classe incluso il nome del pacchetto.

Male:

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

Buona:

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


0

Il seguito ha funzionato per me.

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

0

Ecco cosa ho fatto per risolvere il problema. Ho dato un nome completo in Android: nome. Funziona in 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>

puoi per favore condividere il tuo codice per condividere file usando il provider di file dalla directory interna?
Vishal Patoliya ツ

0

Dovresti provarlo:

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

0

Questo è quello che hai bisogno di fare:

Uri fileURI = FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".fileprovider", file);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.