Falha no FileProvider - npe tentando chamar XmlResourceParser em uma String nula


139

Esta é uma parte do meu manifesto:

<?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 é o arquivo de caminhos de arquivo em 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>

Faço o download de um vídeo da Internet e o salvo no armazenamento interno desta maneira:

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

Eu tento usá-lo assim:

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

}

Nesta linha:

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

Estou tendo o erro a seguir:

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)

"Este é o arquivo de caminhos de arquivo" - onde exatamente está no seu projeto esse arquivo?
— CommonsWare

1
raw / xml / filepaths.xml
— JK

2
nas autoridades eu tenho .provider, anexado ao pacote de aplicativos, mas, ao chamar getUriForFile, não anexo .provider. talvez tthats-lo, Im testando agora
— JK

4
Sim, isso era o problema
— JK

Respostas:


264

O problema era que no Manifest eu tinha essa linha:

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

e ao chamar getUriForFile eu estava passando:

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

Então mudou de "com.example.asd"para "com.example.asd.fileprovider"e funcionou


1
Obrigado trabalhou para mim. Primeiro eu estava fazendo assim. android: autoridades = "$ {applicationId} .fileprovider Agora eu mudei para com.memecreator.fileprovider meu nome atual do pacote
— Rayyan

2
Concorde com @Rayyan, melhor para usar android:authorities="${applicationId}"sempre; código nunca estará com defeito.
— sud007

Trabalhou para mim. Graças
— landrykapela

2
Esta solução não funcionou para mim ... Estou recebendo exceção de ponteiro nulo ainda após verificar para garantir que isso esteja correto.
— wesley franqueia

44

Você pode fazer isso sem codificar o nome do pacote com um benefício adicional de poder executar várias variantes no mesmo dispositivo (pense releasee debugcom applicationIdSuffix, veja estes problemas ):

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

você estava usando o errado authoritye ele não encontrou o ContentProvider( info == null).

Alterar seu manifesto para ( ${applicationId}será substituído por Manifest Merger)

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

e

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

O .sharesufixo é opcional, caso você tenha um real, ContentProvideré melhor ter o nome do pacote como autoridade.


Trabalhou para mim ... Eu tenho usado as duas últimas linhas e seus perfectly..Thanks trabalho
— Ravindra Kushwaha

24

No meu caso, recebi o erro porque o

BuildConfig.APPLICATION_ID

estava sendo importado de

import android.support.v4.BuildConfig;

Portanto, a string retornada foi "android.support.v4"substituída pelo nome do pacote do meu projeto. Verifique se o arquivo de importação é do seu import project.Buildconfige não de outro. Exemplo:

import com.example.yourProjectName.BuildConfig;

Por fim, na <provider>tag Manifest, android:authorities="${applicationId}"sempre tenho que obter o nome do pacote do meu projeto como autoridade

<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
o que é android: resource = "@ xml / ruta_fileprovider"
— Ananta Prasad

1
@AnantaPrasad <? Xml version = "1.0" encoding = "utf-8"?> <caminhos> <external-path name = "external_files" path = "." /> <nome do caminho dos arquivos = "arquivos" caminho = "." /> <nome do caminho do cache = "cache" path = "." /> </paths>
— Prince

5

Primeiro, verifique se o seu provedor android:authoritiesnão entra em conflito com seus outros fornecedores. Além disso, você pode escolher qualquer nome para a última parte do nome: "provider", "fileprovider" etc. etc., mas o aplicativo trava quando há mais de um android:authoritieslistado, enquanto a documentação afirma que permite vários valores listados.

file://Agora, o esquema não pode ser anexado ao Intent no targetSdkVersion> = 24 (Android N 7.0), mas content://sempre é passado para todos os dispositivos (Android 5, 6 e 7). Mas descobrimos que a Xiaomi quebra essa convenção do Google e envia file://, portanto, data.getData().getAuthority()fornece uma string vazia.

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
}

Além disso, o bug quando FileProvider.getUriForFile()resultou em falha java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example/files/attachments/image.jpgfoi corrigido na Android Support Library v24.2.0. O problema foi que o FileProvider.java não viu as pastas de caminho externo.


1

Se você estiver construindo sua AUTHORITY em tempo de execução, BuildConfiguse o nome completo da classe, incluindo o nome do pacote.

Ruim:

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

Boa:

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


0

A seguir funcionou para mim.

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

0

Aqui está o que eu fiz para corrigir o problema. Eu dei o nome completo no android: name. Funciona no 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>

você pode compartilhar seu código para compartilhar arquivos usando o provedor de arquivos do diretório interno?
— Vishal Patoliya ツ

0

Você deveria tentar:

  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

Isso é o que você precisa fazer:

Uri fileURI = FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".fileprovider", file);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.