Aplikacja ulega awarii, gdy próbuję otworzyć plik. Działa poniżej Androida Nougat, ale na Androidzie Nougat ulega awarii. Występuje awaria tylko wtedy, gdy próbuję otworzyć plik z karty SD, a nie z partycji systemowej. Masz problem z pozwoleniem?
Przykładowy kod:
File file = new File("/storage/emulated/0/test.txt");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "text/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); // Crashes on this line
Log:
android.os.FileUriExposedException: file: ///storage/emulated/0/test.txt udostępniony poza aplikacją poprzez Intent.getData ()
Edytować:
Podczas kierowania na Android Nougat file://
identyfikatory URI nie są już dozwolone. content://
Zamiast tego powinniśmy używać identyfikatorów URI. Jednak moja aplikacja musi otwierać pliki w katalogach głównych. Jakieś pomysły?
android
android-file
android-7.0-nougat
Thomas Vos
źródło
źródło
Odpowiedzi:
Jeśli tak
targetSdkVersion >= 24
, musimy użyćFileProvider
klasy, aby dać dostęp do określonego pliku lub folderu, aby były dostępne dla innych aplikacji. Tworzymy własne dziedziczenie klasFileProvider
, aby nasz FileProvider nie powodował konfliktu z FileProvider zadeklarowanymi w importowanych zależnościach, jak opisano tutaj .Kroki zastępowania
file://
identyfikatoracontent://
URI identyfikatorem URI:Dodaj rozszerzenie klasy
FileProvider
Dodaj
<provider>
tag FileProviderAndroidManifest.xml
pod<application>
tagiem. Podaj unikatowe uprawnienia doandroid:authorities
atrybutu, aby uniknąć konfliktów, można określić importowane zależności${applicationId}.provider
i inne często używane uprawnienia.provider_paths.xml
plik wres/xml
folderze. Folder może być potrzebny do utworzenia, jeśli nie istnieje. Zawartość pliku pokazano poniżej. Opisuje, że chcielibyśmy udostępnić dostęp do pamięci zewnętrznej w folderze głównym(path=".")
o nazwie pliki_zewnętrzne .Ostatnim krokiem jest zmiana linii kodu poniżej w
do
Edycja: jeśli używasz zamiaru, aby system otworzył plik, może być konieczne dodanie następującego wiersza kodu:
Proszę zapoznać się, pełny kod i rozwiązanie zostało wyjaśnione tutaj.
źródło
(Build.VERSION.SDK_INT > M)
warunek jest bezużyteczny.FileProvider
należy rozszerzyć tylko wtedy, gdy chcesz zastąpić domyślne zachowanie, w przeciwnym razie użyjandroid:name="android.support.v4.content.FileProvider"
. Zobacz developer.android.com/reference/android/support/v4/content/…Oprócz rozwiązania wykorzystującego
FileProvider
, istnieje inny sposób obejścia tego. Po prostuw
Application.onCreate()
. W ten sposób maszyna wirtualna ignorujeURI
ekspozycję pliku .metoda
włącza sprawdzanie ekspozycji pliku, co jest również domyślnym zachowaniem, jeśli nie skonfigurujemy VmPolicy.
Napotkałem problem polegający na tym, że jeśli używam
content://
URI
do przesłania czegoś, niektóre aplikacje po prostu go nie rozumieją. Obniżenietarget SDK
wersji nie jest dozwolone. W takim przypadku moje rozwiązanie jest przydatne.Aktualizacja:
Jak wspomniano w komentarzu, StrictMode jest narzędziem diagnostycznym i nie powinno być używane w przypadku tego problemu. Kiedy opublikowałem tę odpowiedź rok temu, wiele aplikacji może odbierać tylko pliki uris. Po prostu zawieszają się, gdy próbuję wysłać im identyfikator URI FileProvider. Zostało to już naprawione w większości aplikacji, dlatego powinniśmy przejść do rozwiązania FileProvider.
źródło
VmPolicy
.StrictMode.enableDefaults();
, który uruchamiam tylko na moich kompilacjach programistycznych, nie dochodzi do awarii - więc mam teraz aplikację produkcyjną, która ulega awarii, ale nie ulega awarii podczas programowania. Zasadniczo włączenie narzędzia diagnostycznego ukrywa tutaj poważny problem. Dzięki @hqzxzwb za pomoc w wyjaśnieniu tego.Jeśli
targetSdkVersion
jest wyższa niż 24 , wówczas do udzielenia dostępu służy FileProvider .Utwórz plik xml (ścieżka: res \ xml) ścieżka_ dostawcy. Xml
Dodaj dostawcę w AndroidManifest.xml
Jeśli używasz Androidax , ścieżka FileProvider powinna być:
i zamień
do
Edycja: Podczas
Intent
dołączania identyfikatora URI pamiętaj, aby dodać poniższy wiersz:i jesteś gotowy iść. Mam nadzieję, że to pomoże.
źródło
Jeśli twoja aplikacja jest ukierunkowana na API 24+ i nadal chcesz / musisz użyć file: // intents, możesz użyć zhackowanego sposobu, aby wyłączyć sprawdzanie czasu wykonywania:
Metoda
StrictMode.disableDeathOnFileUriExposure
jest ukryta i udokumentowana jako:Problem polega na tym, że moja aplikacja nie jest kiepska, ale raczej nie chce zostać kaleką za pomocą content: // intencji, które nie są zrozumiałe dla wielu aplikacji. Na przykład otwarcie pliku mp3 ze schematem content: // oferuje znacznie mniej aplikacji niż przy otwieraniu tego samego schematu over file: //. Nie chcę płacić za błędy projektowe Google, ograniczając funkcjonalność mojej aplikacji.
Google chce, aby programiści korzystali ze schematu treści, ale system nie jest na to przygotowany, przez lata aplikacje były tworzone z wykorzystaniem plików, które nie są „treściami”, pliki można edytować i zapisywać z powrotem, a plików obsługiwanych przez schemat treści nie można (można one?).
źródło
ContentResolver
ma zarównoopenInputStream()
iopenOutputStream()
. Mniej hackującym sposobem jest po prostu samodzielne skonfigurowanie reguł maszyn wirtualnych i nie włączaniefile
Uri
reguły.Jeśli masz
targetSdkVersion
24 lata lub więcej, nie możesz używaćfile:
Uri
wartościIntents
na urządzeniach z Androidem 7.0+ .Do wyboru są:
Upuść swój
targetSdkVersion
do 23 lub mniej, lubUmieść swoje treści w pamięci wewnętrznej, a następnie użyj,
FileProvider
aby je selektywnie udostępnić innym aplikacjomNa przykład:
(z tego przykładowego projektu )
źródło
/system
partycji? Każda aplikacja powinna mieć dostęp do tej partycji bez rootowania./system
będzie czytelne dla świata. Biorąc to pod uwagę, domyślam się, że nadal będziesz otrzymywać ten wyjątek. Podejrzewam, że sprawdzają tylko schemat i nie próbują ustalić, czy plik jest naprawdę czytelny dla całego świata. Jednak ciFileProvider
to nie pomoże, ponieważ nie możesz nauczyć go służyć/system
. Możesz stworzyć niestandardową strategię dla mnieStreamProvider
lub rzucić własnąContentProvider
, aby obejść problem./data
,/system
), z powodu tej „dobrej zmiany”.Najpierw musisz dodać dostawcę do swojego AndroidManifest
teraz utwórz plik w folderze zasobów xml (jeśli używasz Android Studio, możesz nacisnąć Alt + Enter po podświetleniu ścieżki_pliku i wybrać opcję utworzenia zasobu xml)
Następnie w pliku file_paths wpisz
Ten przykład dotyczy ścieżki zewnętrznej, do której możesz się odwoływać tutaj więcej opcji. Umożliwi to udostępnianie plików znajdujących się w tym folderze i jego podfolderze.
Teraz pozostało tylko stworzyć zamiar w następujący sposób:
EDYTOWAĆ : Dodałem folder główny karty SD w ścieżkach do pliku. Przetestowałem ten kod i działa.
źródło
String extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString());
Polecam również każdemu, kto szuka odpowiedzi, aby najpierw przeczytać przez FileProvider i zrozumieć, co masz do czynienia z uprawnieniami do plików w systemie Android N i nowszym. Dostępne są opcje pamięci wewnętrznej vs. pamięci zewnętrznej, a także zwykłej ścieżki plików vs. ścieżek pamięci podręcznej.java.lang.IllegalArgumentException: Failed to find configured root ...
jedyną rzeczą, która działała, był<files-path path="." name="files_root" />
plik xml zamiast<external-path ...
. Mój plik został zapisany w pamięci wewnętrznej.@palash k odpowiedź jest poprawna i działała w przypadku plików pamięci wewnętrznej, ale w moim przypadku chcę również otwierać pliki z pamięci zewnętrznej, moja aplikacja uległa awarii podczas otwierania pliku z pamięci zewnętrznej, takiej jak sdcard i usb, ale udało mi się rozwiązać problem, modyfikując dostawca_paths.xml z zaakceptowanej odpowiedzi
zmień ścieżkę dostawca.xml jak poniżej
i w klasie java (bez zmian jako zaakceptowana odpowiedź tylko niewielka edycja)
Pomoże mi to naprawić awarię plików z zewnętrznych pamięci. Mam nadzieję, że pomoże to komuś, kto ma taki sam problem jak mój :)
źródło
<root-path
proszę? To działa.<external-path path="Android/data/${applicationId}/" name="files_root" />
nie miało wpływu na otwarte pliki z pamięci zewnętrznej.Android/data/${applicationId}/
w karcie SD.Moim rozwiązaniem było użycie „Uri.parse” ścieżki pliku jako ciągu, zamiast używania Uri.fromFile ().
Wydaje się, że fromFile () używa wskaźnika pliku, który, jak przypuszczam, może być niepewny, gdy adresy pamięci są widoczne dla wszystkich aplikacji. Ale Ciąg ścieżki do pliku nigdy nikomu nie zaszkodzi, więc działa bez zgłaszania wyjątku FileUriExposedException.
Testowane na poziomach API od 9 do 27! Pomyślnie otwiera plik tekstowy do edycji w innej aplikacji. W ogóle nie wymaga FileProvider ani biblioteki obsługi systemu Android.
źródło
Po prostu wklej poniższy kod w działaniu onCreate ()
Zignoruje ekspozycję URI
źródło
Po prostu wklej poniższy kod w aktywności
onCreate()
.Zignoruje ekspozycję URI.
Miłego kodowania :-)
źródło
Korzystanie z fileProvider jest właściwą drogą. Możesz jednak użyć tego prostego obejścia:
zastąpić:
przez
źródło
Użyłem odpowiedzi Palasha podanej powyżej, ale była ona nieco niekompletna, musiałem udzielić takiego pozwolenia
źródło
Po prostu wklej poniższy kod w działaniu onCreate ()
Zignoruje ekspozycję URI
źródło
dodaj tę linię w onCreate
Metoda udostępniania
źródło
Oto moje rozwiązanie:
w pliku Manifest.xml
w res / xml / provider_paths.xml
w moim fragmencie mam następny kod:
To wszystko, czego potrzebujesz.
Również nie trzeba tworzyć
Testuję na Androidzie 5.0, 6.0 i Androidzie 9.0 i to się udało.
źródło
Aby pobrać pdf z serwera, dodaj poniższy kod w swojej klasie usług. Mam nadzieję, że jest to dla ciebie pomocne.
I tak, nie zapomnij dodać uprawnień i dostawcy w swoim manifeście.
źródło
@xml/provider_paths
?Nie wiem dlaczego, zrobiłem wszystko dokładnie tak samo jak Pkosta ( https://stackoverflow.com/a/38858040 ), ale ciągle pojawiał się błąd:
java.lang.SecurityException: Permission Denial: opening provider redacted from ProcessRecord{redacted} (redacted) that is not exported from uid redacted
Zmarnowałem godziny na ten temat. Sprawca? Kotlin.
intent
faktycznie ustawieniegetIntent().addFlags
zamiast działa na moim nowo zadeklarowanej playIntent.źródło
Umieściłem tę metodę, aby ścieżka imageuri łatwo wchodziła w treść.
źródło
Istnieją 3 główne kroki tutaj, jak wspomniano poniżej
Krok 1: Wpis manifestacyjny
Krok 2: Utwórz plik XML res / xml / provider_paths.xml
Krok 3: Zmiany kodu
źródło
Wiem, że to dość stare pytanie, ale ta odpowiedź jest dla przyszłych widzów. Zetknąłem się z podobnym problemem i po przeprowadzeniu badań znalazłem alternatywę dla tego podejścia.
Twój zamiar tutaj na przykład: Aby wyświetlić obraz ze swojej ścieżki w Kotlin
Główna funkcja poniżej
Podobnie, zamiast obrazu, możesz użyć dowolnego innego formatu pliku, takiego jak pdf, aw moim przypadku działało dobrze
źródło
Spędziłem prawie dzień, próbując dowiedzieć się, dlaczego dostaję ten wyjątek. Po wielu zmaganiach ta konfiguracja działała idealnie ( Kotlin ):
AndroidManifest.xml
ścieżka_pliku.xml
Sama intencja
Tutaj wyjaśniam cały proces .
źródło
https://stackoverflow.com/a/38858040/395097 ta odpowiedź jest kompletna.
Ta odpowiedź jest na - masz już aplikację, która była kierowana poniżej 24, a teraz uaktualniasz do targetSDKVersion> = 24.
W Androidzie N zmieniany jest tylko plik uri udostępniony aplikacji innej firmy. (Nie tak jak wcześniej go używaliśmy). Dlatego zmieniaj tylko miejsca, w których udostępniasz ścieżkę za pomocą aplikacji innej firmy (w moim przypadku Aparat)
W naszej aplikacji wysyłaliśmy uri do aplikacji Camera, w tym miejscu spodziewamy się, że aplikacja camera zapisze przechwycony obraz.
Teraz mamy 2 różne URI dla tego samego pliku. # 1 jest współdzielony z aplikacją Camera. Jeśli zamierzeniem kamery jest sukces, możemy uzyskać dostęp do obrazu z punktu 2.
Mam nadzieję że to pomoże.
źródło
Xamarin.Android
Uwaga: Ścieżka xml / dostawca_paths.xml (.axml) nie mogła zostać rozwiązana, nawet po utworzeniu folderu xml w obszarze Zasoby (być może można go umieścić w istniejącej lokalizacji, takiej jak Wartości , nie próbowałem), więc uciekłem się to działa na teraz. Testy wykazały, że należy go wywoływać tylko raz na uruchomienie aplikacji (co ma sens, ponieważ zmienia stan operacyjny maszyny wirtualnej hosta).
Uwaga: xml musi być pisany wielkimi literami, więc Resources / Xml / provider_paths.xml
źródło
Odpowiedź @Pkosta jest jednym ze sposobów na zrobienie tego.
Oprócz używania
FileProvider
możesz także wstawić plikMediaStore
(szczególnie w przypadku plików obrazów i wideo), ponieważ pliki w MediaStore są dostępne dla każdej aplikacji:Na przykład możesz wstawić plik wideo do MediaStore w następujący sposób:
contentUri
jest jakcontent://media/external/video/media/183473
, który można przekazać bezpośrednio doIntent.putExtra
:To działa dla mnie i oszczędza kłopotów z używaniem
FileProvider
.źródło
Po prostu pozwól mu zignorować ekspozycję URI ... Dodaj go po utworzeniu
źródło
Wypróbuj to rozwiązanie
PRZEKAZAJ TE ZEZWOLENIA NA MANIFEST
ZAMIERZANIE ZDJĘĆ
UZYSKAJ UCHWYT OBRAZU W WYNIKU NIEAKTYWNOŚCI
METODA UZYSKANIA URI OBRAZU
źródło
W moim przypadku pozbyłem się wyjątku, zastępując
SetDataAndType
go justSetData
.źródło