2016-10-06 3 views
15

Ich habe eine Ausnahme, die nur auf Huawei-Geräte in meiner app passiert, wenn FileProvider.getUriForFile mit:FileProvider Fehler onHuawei Geräte

Exception: java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/<card name>/Android/data/<app package>/files/.export/2016-10-06 13-22-33.pdf 
    at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(SourceFile:711) 
    at android.support.v4.content.FileProvider.getUriForFile(SourceFile:400) 

Hier ist die Definition meiner Datei-Provider in meinem Manifest:

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

Die Ressourcendatei mit konfiguriert Pfade:

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

Irgendeine Idee über die Ursache dieses Problems und warum es nur auf Huawei-Geräten passiert? Wie würde ich dies debuggen, da ich kein Huawei-Gerät habe?

UPDATE:

Ich habe mehr Protokolle in meine App und ich habe einige inkonsistente Ergebnisse, wenn beide ContextCompat.getExternalFilesDirs und context.getExternalFilesDir auf diesen Geräten drucken:

ContextCompat.getExternalFilesDirs: 
/storage/emulated/0/Android/data/<package>/files 
/storage/sdcard1/Android/data/<package>/files 

context.getExternalFilesDir: 
/storage/sdcard1/Android/data/<package>/files 

Dies ist inkonsistent mit der Dokumentation von ContextCompat.getExternalFilesDirs, die besagt, dass The first path returned is the same as getExternalFilesDir(String)

Das erklärt das Problem seit ich context.getExternalFilesDir in verwenden Mein Code und FileProvider verwendet ContextCompat.getExternalFilesDirs.

+0

Wo/wie bekommen Sie diese 'Datei'? '/ storage/' sieht nicht korrekt aus. – CommonsWare

+0

Bitte teilen Sie mit, was getExternalStorageDirectory() auf diesem Gerät liefert. – greenapps

+0

Ich erhalte die Datei mit 'Context.getExternalFileDir (null)'. Von den Protokollen, die ich auf diesen Geräten habe, kann es Speicher zurückbringen/sdcard1 /,/storage/3565-3131 /,/storage/73A8-8626 /,/storage/864A-F3ED ... –

Antwort

7

Update für Android N (die ursprüngliche Antwort unten verlassen und haben diesen neuen Ansatz bestätigt arbeitet in der Produktion):

Wie Sie in Ihrem Update erwähnt, viele Huawei Gerätemodelle (zB KIW-L24, ALE- L21, ALE-L02, PLK-L01 und eine Vielzahl anderer) brechen den Android-Vertrag für Anrufe auf ContextCompat#getExternalFilesDirs(String). Anstatt Context#getExternalFilesDir(String) (dh den Standardeintrag) als erstes Objekt im Array zurückzugeben, geben sie stattdessen das erste Objekt als Pfad zur externen SD-Karte zurück, falls eine vorhanden ist.

Durch diese Reihenfolge Vertrag zu brechen, diese Huawei-Geräte mit externen SD-Karten werden mit einem IllegalArgumentException auf Anrufe zu FileProvider#getUriForFile(Context, String, File) für external-files-path Wurzeln abstürzen. Es gibt zwar eine Vielzahl von Lösungen, die Sie verfolgen können, um mit diesem Problem umzugehen (z.

  • Pre-N: Zurück Uri#fromFile(File), die mit Android N wird nicht funktionieren und vor aufgrund FileUriExposedException
  • eine benutzerdefinierte FileProvider Implementierung) zu schreiben, habe ich die einfachste Ansatz ist zu fangen dieses Problem und fand N: Kopieren Sie die Datei auf Ihre cache-path (Anmerkung: dieser ANRs vorstellen kann, wenn auf dem UI-Thread getan) und dann FileProvider#getUriForFile(Context, String, File) für die kopierte Datei zurück

-Code (dh die Fehler ganz zu vermeiden), dies zu erreichen kann gefunden werden unten:

public class ContentUriProvider { 

    private static final String HUAWEI_MANUFACTURER = "Huawei"; 

    public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file) { 
     if (HUAWEI_MANUFACTURER.equalsIgnoreCase(Build.MANUFACTURER)) { 
      Log.w(ContentUriProvider.class.getSimpleName(), "Using a Huawei device Increased likelihood of failure..."); 
      try { 
       return FileProvider.getUriForFile(context, authority, file); 
      } catch (IllegalArgumentException e) { 
       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { 
        Log.w(ContentUriProvider.class.getSimpleName(), "Returning Uri.fromFile to avoid Huawei 'external-files-path' bug for pre-N devices", e); 
        return Uri.fromFile(file); 
       } else { 
        Log.w(ContentUriProvider.class.getSimpleName(), "ANR Risk -- Copying the file the location cache to avoid Huawei 'external-files-path' bug for N+ devices", e); 
        // Note: Periodically clear this cache 
        final File cacheFolder = new File(context.getCacheDir(), HUAWEI_MANUFACTURER); 
        final File cacheLocation = new File(cacheFolder, file.getName()); 
        InputStream in = null; 
        OutputStream out = null; 
        try { 
         in = new FileInputStream(file); 
         out = new FileOutputStream(cacheLocation); // appending output stream 
         IOUtils.copy(in, out); 
         Log.i(ContentUriProvider.class.getSimpleName(), "Completed Android N+ Huawei file copy. Attempting to return the cached file"); 
         return FileProvider.getUriForFile(context, authority, cacheLocation); 
        } catch (IOException e1) { 
         Log.e(ContentUriProvider.class.getSimpleName(), "Failed to copy the Huawei file. Re-throwing exception", e1); 
         throw new IllegalArgumentException("Huawei devices are unsupported for Android N", e1); 
        } finally { 
         IOUtils.closeQuietly(in); 
         IOUtils.closeQuietly(out); 
        } 
       } 
      } 
     } else { 
      return FileProvider.getUriForFile(context, authority, file); 
     } 
    } 

} 

Zusammen mit dem file_provider_paths.xml:

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

Sobald Sie eine Klasse wie folgt erstellt haben, ersetzen Sie Ihre Anrufe an:

FileProvider.getUriForFile(Context, String, File) 

mit:

ContentUriProvider.getUriForFile(Context, String, File) 

Ehrlich gesagt, Ich denke nicht, dass dies eine besonders anmutige Lösung ist, aber es erlaubt uns, dafür zu verwenden mally dokumentiert Android-Verhalten ohne etwas zu drastisch (z. Schreiben einer benutzerdefinierten FileProvider Implementierung). Ich habe das in der Produktion getestet, also kann ich bestätigen, dass es diese Huawei Abstürze behebt. Für mich war das der beste Ansatz, da ich nicht zu viel Zeit damit verbringen wollte, das zu adressieren, was ganz offensichtlich ein Herstellerdefekt ist.

Update von vor Huawei-Geräten mit diesem Fehler zu Android N aktualisiert:

Dies wird nicht mit Android N arbeiten und oben aufgrund FileUriExposedException, aber ich habe noch ein Huawei-Gerät mit diesem Fehl begegnen Konfiguration auf Android N.

public class ContentUriProvider { 

    private static final String HUAWEI_MANUFACTURER = "Huawei"; 

    public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file) { 
     if (HUAWEI_MANUFACTURER.equalsIgnoreCase(Build.MANUFACTURER) && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { 
      Log.w(ContentUriProvider.class.getSimpleName(), "Using a Huawei device on pre-N. Increased likelihood of failure..."); 
      try { 
       return FileProvider.getUriForFile(context, authority, file); 
      } catch (IllegalArgumentException e) { 
       Log.w(ContentUriProvider.class.getSimpleName(), "Returning Uri.fromFile to avoid Huawei 'external-files-path' bug", e); 
       return Uri.fromFile(file); 
      } 
     } else { 
      return FileProvider.getUriForFile(context, authority, file); 
     } 
    } 
} 
+0

Wir sehen das passiert auf Huawei Geräte mit Android N jetzt. –

+0

Ich kann bestätigen, dass ich mehrere Abstürze mit Huawei-Geräten habe, auf denen Android N läuft, mit dieser Fehlkonfiguration. –

+0

Ich fing an, dieses Problem auch zu sehen. Hat oben eine aktualisierte Antwort gegeben, die diesen Fehler allgemeiner angehen sollte – wrb

1

Meine Lösung jetzt zu diesem Thema, auch wenn es nicht perfekt ist, ist meiner zu erklären FileProvider mit dem folgenden Pfad (kann dazu dienen, alle Dateien auf dem Gerät):

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

Dies ist nicht offiziell dokumentiert und könnte mit einer zukünftigen Version der v4 Unterstützung Bibliothek brechen, aber ich kann keine andere Lösung sieht Sie eine Datei in dem sekundären externen Speicher (oft die SD-Karte) unter Verwendung der vorhandenen FileProvider zu dienen .

3

Ich hatte das gleiche Problem und meine Lösung am Ende war, immer den ContextCompat.getExternalFilesDirs Aufruf zu verwenden, um die File zu bauen, die als Parameter für FileProvider verwendet wird. Auf diese Weise müssen Sie keine der oben genannten Problemumgehungen verwenden.

Mit anderen Worten. Wenn Sie die Kontrolle über den File Parameter haben, den Sie verwenden, um FileProvider aufzurufen und/oder es Ihnen egal ist, dass die Datei möglicherweise außerhalb des klassischen Ordners /storage/emulated/0/Android/data/ gespeichert wird (was vollkommen in Ordnung sein sollte, da es alles nur derselbe SD ist Ich schlage vor, zu tun, was ich getan habe.

Wenn es nicht Ihr Fall ist, dann empfehle ich, die obige Antwort mit benutzerdefinierten getUriForFile Implementierung zu verwenden.

Verwandte Themen