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.