Kerusakan FileProvider - npe mencoba memohon XmlResourceParser pada String nol

139

Ini adalah bagian dari manifes saya:

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

Ini adalah file filepaths di 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>

Saya mengunduh video dari internet dan menyimpannya ke penyimpanan internal dengan cara ini:

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

Saya mencoba menggunakannya seperti ini:

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

}

Di baris ini:

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

Saya mendapatkan kesalahan berikut:

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
sumber
"Ini adalah file filepaths" - di mana tepatnya dalam proyek Anda file ini?
CommonsWare
1
raw / xml / filepaths.xml
JK
2
di otoritas saya punya .provider, ditambahkan ke paket aplikasi, tetapi ketika memanggil getUriForFile, saya tidak menambahkan .provider. mungkin itu saja, saya sedang menguji sekarang
JK
4
Ya itu masalahnya
JK

Jawaban:

264

Masalahnya adalah bahwa dalam Manifest saya memiliki baris ini:

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

dan ketika memanggil getUriForFile saya melewati:

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

Jadi berubah dari "com.example.asd"ke "com.example.asd.fileprovider"dan berhasil

JK
sumber
1
Terima kasih berhasil untuk saya. Pertama saya melakukan seperti ini. android: otoritas = "$ {applicationId} .fileprovider Sekarang saya mengubahnya ke com.memecreator.fileprovider nama paket saya yang sebenarnya
Rayyan
2
Setuju dengan @Rayyan, Best to use android:authorities="${applicationId}"always; kode tidak akan pernah salah.
sud007
Bekerja untukku. Terima kasih
landrykapela
2
Solusi ini tidak berfungsi untuk saya ... Saya masih mendapatkan pengecualian pointer nol setelah memeriksa untuk memastikan ini benar.
wesley franks
44

Anda dapat melakukan ini tanpa hardcoding nama paket dengan manfaat tambahan untuk dapat menjalankan beberapa varian pada perangkat yang sama (pikirkan releasedan debugdengan applicationIdSuffix, lihat masalah ini ):

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

Anda menggunakan yang salah authoritydan tidak menemukan ContentProvider( info == null).

Ubah manifes Anda menjadi ( ${applicationId}akan digantikan oleh Manifest Merger)

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

dan

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

The .shareakhiran adalah opsional, jika anda memiliki nyata ContentProvideryang lebih baik untuk memiliki nama paket sebagai otoritas.

TWiStErRob
sumber
Bekerja untuk saya ... Saya telah menggunakan dua baris terakhir dan bekerja dengan sempurna..Terima kasih
Ravindra Kushwaha
24

Dalam kasus saya, saya mendapat kesalahan karena

BuildConfig.APPLICATION_ID

sedang diimpor dari

import android.support.v4.BuildConfig;

Jadi string yang dikembalikannya "android.support.v4"bukan nama paket proyek saya. Lihat file impor dari Anda import project.Buildconfigdan bukan yang lain. Contoh:

import com.example.yourProjectName.BuildConfig;

Akhirnya, dalam <provider>tag di Manifest saya harus android:authorities="${applicationId}"selalu mendapatkan nama paket proyek saya sebagai otoritas

<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
sumber
1
apa itu 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>
Pangeran
5

Pertama, pastikan penyedia android:authoritiesAnda tidak bertentangan dengan penyedia lainnya. Selain itu, Anda dapat memilih nama untuk bagian terakhir namanya: "provider", "fileprovider" dll., Tetapi aplikasi macet ketika ada lebih dari satu yang android:authoritiesterdaftar, sementara dokumentasi menyatakan bahwa itu memungkinkan beberapa nilai terdaftar.

file://Skema sekarang tidak diizinkan untuk dilampirkan dengan Intent on targetSdkVersion> = 24 (Android N 7.0), hanya content://selalu dilewati untuk semua perangkat (Android 5, 6 dan 7). Tetapi kami menemukan bahwa Xiaomi melanggar konvensi dan pengiriman Google ini file://, karenanya data.getData().getAuthority()memberikan string kosong.

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
}

Juga, bug saat FileProvider.getUriForFile()mengakibatkan kerusakan java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example/files/attachments/image.jpgdiperbaiki di Android Support Library v24.2.0. Masalahnya adalah bahwa FileProvider.java tidak melihat folder jalur eksternal.

soshial
sumber
1

Jika Anda membangun OTORITAS saat runtime menggunakan, BuildConfigpastikan Anda menggunakan nama kelas lengkap termasuk nama paket Anda.

Buruk:

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

Baik:

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

Vahid Amiri
sumber
0

Mengikuti berhasil untuk saya.

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

Inilah yang saya lakukan untuk memperbaiki masalah ini. Saya memberikan nama yang sepenuhnya memenuhi syarat di android: name. Ini bekerja di 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
sumber
dapatkah Anda membagikan kode Anda untuk berbagi file menggunakan penyedia file dari direktori internal?
Vishal Patoliya ツ
0

Kamu harus mencobanya:

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

Ini adalah hal yang perlu kamu lakukan:

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