1. Fundo
O capítulo anterior [Android Framework Series] Capítulo 16 Storage Access Framework (SAF) analisou principalmente a estrutura de acesso ao armazenamento (SAF) introduzida no Android 4.4 存储访问框架(SAF)
. Neste capítulo analisamos Android10(Q)
o armazenamento relacionado e entendemos seus métodos de armazenamento restritos.
Google
Para dar aos usuários mais controle sobre seus arquivos e limitar a confusão de arquivos, a forma como você acessa o armazenamento do seu dispositivo Android Q
mudou .App
Antes de 1.1 Android Q
Contanto que o programa obtenha READ_EXTERNAL_STORAGE
permissão, ele poderá ler livremente o diretório público de armazenamento externo;
desde que o programa obtenha WRITE_EXTERNAL_STORAGE
permissão, ele poderá criar livremente novos arquivos ou pastas no diretório público gravados no armazenamento externo.
1.2 Android Q e posterior
Então o Google propôs isso no Android Q 分区存储
, pretendendo limitar o uso de diretórios públicos em armazenamento externo por programas .
O armazenamento particionado não tem impacto nos diretórios privados de armazenamento interno e nos diretórios privados de armazenamento externo.
2 Modo sandbox
2.1 O Android Q estipula que o aplicativo tenha duas visualizações de modo de espaço de armazenamento: Visualização herdada e Visualização filtrada.
2.1.1 Visualização herdada (modo de compatibilidade)
Quando o projeto targetSdkVersion <= 28, o modo de compatibilidade é usado por padrão no dispositivo AndroidQ, o método de armazenamento é o mesmo de Android Q
antes App
e o acesso Sdcard
é o mesmo, com direitos de acesso completos.
2.1.2 Visualização Filtrada (modo sandbox)
Quando o projeto targetSdkVersion > 28, o dispositivo AndroidQ App
só pode acessar diretamente App-specific
arquivos de diretório e não tem permissão para acessar App-specific
arquivos externos. O acesso a outros diretórios só pode ser fornecido por meio de MediaStore
, SAF
ou outros aplicativos ContentProvider
.
2.2 O armazenamento com escopo divide o espaço de armazenamento em duas partes
2.2.1 Diretório público do cartão SD: Downloads, Documentos, Imagens, DCIM, Filmes, Música, Toques
- Os arquivos no diretório público
App
não serão excluídos após a desinstalação - Pode ser acessado através
SAF
daMediaStore
interface
2.2…2 diretório privado de dados/dados (diretório específico do aplicativo)
- Para
Filtered View App
,App-specific
o diretório só pode ser acessado diretamente por você App
Desinstale e os dados serão apagados.
2.3 Impacto de compatibilidade
Scoped Storage
Tem um grande impacto sobre App访问存储方式
, App数据存放
e App间数据共享
.
2.4 Adaptação
Para adaptação específica, consulte a documentação oficial.
2.4.1 Visualização em execução do aplicativo
O sistema determina o modo de execução do aplicativo através do seguinte:
App TargetSDK > 28
,padrãoFiltered View(沙箱模式)
App TargetSDK <= 28
, declara a permissão READ_EXTERNAL_STORAGE ou WRITE_EXTERNAL_STORAGE, padrãoLegacy View(兼容模式)
- O aplicativo pode definir requestLegacyExternalStorage por meio de AndroidManifest.xml e selecionar o método correspondente:
declaradoREAD_EXTERNAL_STORAGE
,WRITE_EXTERNAL_STORAGE
permissão (ignorado se não declarado):
requestLegacyExternalStorage=true
significaLegacy View(兼容模式)
requestLegacyExternalStorage=false
Filtered View(沙箱模式)
(注意:该方式Android11后失效了)
- Os aplicativos do sistema podem solicitar
android.permission.WRITE_MEDIA_STORAGE
permissões do sistema e também ter permissões de espaço de armazenamento total e acessar todos os arquivos.No entanto, no teste CTS, apenas aplicativos que não têm interação do usuário e são visíveis podem se inscrever. Referência específica《Android Bootcamp 2019 - Privacy Overview.pdf》
.
- O aplicativo é propriedade quando as seguintes condições são atendidas
: ① DeclaraçãoINSTALL_PACKAGES
ou动态申请INSTALL_PACKAGES
permissão
② PropriedadeWRITE_EXTERNAL_STORAGE权限
③ Propriedade do aplicativo外置存储空间Read、Write权限
.
Porém,Environment.isExternalStorageLegacy
a julgar pela interface, o retorno não é necessariamente uma Visualização Legada.
2.4.2 Determine o modo de execução atual do aplicativo
Para determinar em qual modo o aplicativo atual está sendo executado, você pode usar esta API para determinar:
Environment.isExternalStorageLegacy();
2.5 Lendo e escrevendo diretórios públicos
Depois que o aplicativo é iniciado Filtered View
, ele só pode acessar a si mesmo diretamente App-specific目录
, portanto, Android Q
são fornecidos dois métodos de acesso ao diretório público:
2.5.1 Uri definido pelo MediaStore
MediaStore
Os seguintes tipos de acesso são fornecidos Uri
, e o objetivo do acesso é alcançado através da pesquisa dos dados Uri correspondentes.
Cada um dos seguintes tipos é dividido em três tipos Uri
, Internal
, External
, 可移动存储
:
Áudio
- Interno: MediaStore.Audio.Media.INTERNAL_CONTENT_URI
content://media/internal/audio/media。- Externo: MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
content://media/external/audio/media。- Armazenamento removível: MediaStore.Audio.Media.getContentUri
content://media//audio/media.
Vídeo
- Interno: MediaStore.Video.Media.INTERNAL_CONTENT_URI
content://media/internal/video/media。- Externo: MediaStore.Video.Media.EXTERNAL_CONTENT_URI
content://media/external/video/media。- Armazenamento removível: MediaStore.Video.Media.getContentUri
content://media//video/media.
Imagem
- Interno: MediaStore.Images.Media.INTERNAL_CONTENT_URI
content://media/internal/images/media。- Externo: MediaStore.Images.Media.EXTERNAL_CONTENT_URI
content://media/external/images/media。- Armazenamento removível: MediaStore.Images.Media.getContentUri
content://media//images/media.
Arquivo
- MediaStore. Files.Media.getContentUri
content://media//file。
Transferências
- Interno: MediaStore.Downloads.INTERNAL_CONTENT_URI
content://media/internal/downloads。- Externo: MediaStore.Downloads.EXTERNAL_CONTENT_URI
content://media/external/downloads。- Armazenamento removível: MediaStore.Downloads.getContentUri
content://media//downloads.
2.5.1.1 Obtenha todos os volumes
Para o Uri descrito anteriormente, getContentUri
como obter todos eles pode ser feito da seguinte maneira:
for(String volume:MediaStore.getExternalVolumeNames(this)){
MediaStore.Audio.Media.getContentUri(volume);
}
2.5.1.2 Relacionamento entre Uri e diretório público
MediaProvider
Para que o App seja armazenado em um arquivo de diretório público, ele é determinado pelo Uri no método. O caminho relativo é ContentResolver insert
mostrado na tabela a seguir , e o arquivo completo é: content://media//<Uri path>.<Uri路径>
2.5.1.3 Permissões
MediaStore
Através de diferentes métodos Uri
, os usuários podem adicionar, excluir (se os arquivos não puderem ser excluídos por meio do File Uri, será necessário usar a interface SAF) e modificar.
As permissões correspondentes do aplicativo são as seguintes:
2.5.1.4 Consultar arquivos
Consultando ContentResolver
conteúdos diferentes com base em Uri diferentes:
try (Cursor c = getContentResolver()
.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION, null, null, null)) {
while (c.moveToNext()) {
Uri contentUri =
ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, c.getLong(0));
}
}
PS: Durante MediaStore.Files
o processo Query
, apenas fotos, vídeos e arquivos de áudio serão exibidos.
2.5.1.5 Lendo arquivos
Através ContentResolver query
da interface, a leitura do arquivo após localizá-lo pode ser feita da seguinte forma:
- Através
ContentResolver openFileDescriptor
da interface, selecione o método de abertura correspondente,
por exemplo, "r" significa leitura, "w" significa escrita e oParcelFileDescriptor
tipo de retornoFD
. - Acessado
Thumbnail
atravésContentResolver loadThumbnail
da interface.
Ao passar o tamanho,MediaProvider
o tamanho especificado é retornadoThumbnail
. - O código nativo acessa arquivos.
Se o código nativo precisar acessar arquivos, você pode consultar os seguintes métodos:
①openFileDescriptor
RetornandoParcelFileDescriptor
②ParcelFileDescriptor.detachFd()
LendoFD
③FD
Passando paraNative
o código da camada
④App
Precisa ser responsável pelo fechamento atravésclose
da interfaceFD
ParcelFileDescriptor parcelFd = resolver.openFileDescriptor(uri, file0penMode);
if (parcelFd != null) {
int fd = parcelFd.detachFd();
// Pass the integer value "fd" into your native code. Remember to call
// close(2) on the file descriptor when you're done using it.
}
2.5.1.6 Criar novo arquivo
Se precisar armazenar um novo arquivo em um diretório público, você precisará ContentResolver insert
usar uma interface diferente Uri
e optar por armazená-lo em um diretório diferente.
2.5.1.7 Modificar arquivos
Se precisar modificar um arquivo multimídia, você precisa ContentResolver query
encontrar o Uri do arquivo correspondente através da interface.
Se você não criou um novo arquivo, você precisa prestar atenção à descrição em 2.5.1.3 Permissões. Você precisa aplicar WRITE_EXTERNAL_STORAGE权限
ou catch RecoverableSecurityException
uma caixa pop-up dará ao usuário uma escolha.
Através da interface a seguir, obtenha o arquivo que precisa ser modificado FD
ou OutputStream
:
- getContentResolver().openOutputStream(contentUri)
obtém o OutputStream do arquivo correspondente. - getContentResolver().openFile ou getContentResolver().openFileDescriptor
abre o arquivo através de openFile ou openFileDescriptor. Você precisa selecionar Mode como "w", que indica permissão de gravação. Essas interfaces retornam um ParcelFileDescriptor.
getContentResolver().openFileDescriptor(contentUri,“w”);
getContentResolver().openFile(contentUri,“w”,null);
2.5.1.8 Excluir arquivos
Exclua arquivos por meio da interface ContentResolver e o Uri é o Uri da consulta:
getContentResolver().delete(contentUri,null,null);
2.5.2 Através da interface SAF
2.5.2.1 Introdução ao SAF
SAF
, isto é , fornecendo aos usuários a capacidade de abrir e navegar por arquivos Storage Access Framework
selecionando diferentes arquivos . DocumentsProvider
Podemos olhar para o capítulo anterior
O Android fornece o seguinte por padrão DocumentsProvider
:
MediaDocumentsProvider
, ExternalStorageProvider
, DownloadStorageProvider
.
A diferença entre eles é:
Nesta imagem, existem três áreas, a saber:
- MediaDocumentsProvide
- DownloadStorageProvider
- Provedor de armazenamento externo
- Fornecedor de documentos de terceiros
2.5.2.2 Como usar
Consulte a documentação oficial para obter detalhes
O método geral é o seguinte:
- Selecione um único arquivo
- Selecione o diretório,
o programa de gerenciamento de arquivos e o programa de limpeza. Você pode usar este método para obter todas as permissões de gerenciamento para o diretório e subdiretórios correspondentes. - crie um novo arquivo
- excluir
DocumentsContract.deleteDocument(getContentResolver(),uri);
- Rever
①Obter OutputStream
getContentResolver().openOutputStream(uri);
② Obtenha o ParcelFileDescriptor gravável
getContentResolver().openFileDescriptor(contentUri,"w");
getContentResolver().openFile (contentUri,"w",null);
Referência específica de demonstração
2.6 Acesse diretórios específicos do aplicativo
Existem duas situações de acesso App-specific
, a primeira é o acesso App自身App-specific目录
e a segunda é o acesso 其他App目录文件
.
2.6.1 Diretório específico do próprio aplicativo
Android Q, se o aplicativo for iniciado Filtered View
, ele só poderá acessar diretamente os arquivos em seu próprio diretório:
- As interfaces Environment.getExternalStorageDirectory e getExternalStoragePublicDirectory
estão obsoletas no Android Q. O aplicativo é uma visualização filtrada e não pode acessar diretamente este diretório. - Acessar
o App através de Arquivo ("/sdcard/") é uma Visualização Filtrada e não pode acessar diretamente este diretório. - Obtenha o diretório específico do aplicativo
Obtenha a interface de mídia: getExternalMediaDirs
Obtenha a interface de cache: getExternalCacheDirs
Obtenha a interface Obb: getObbDirs
Obtenha a interface de dados: getExternalFilesDirs
2.6.2 Arquivos multimídia no diretório específico do aplicativo
Arquivos multimídia dentro do diretório específico do aplicativo:
- O acesso próprio do aplicativo
App-specific
é igual ao diretório próprio do aplicativo 2.6.1 - Acesso de outros aplicativos
① Por padrão , os arquivos multimídia contidosMedia Scanner
não serão verificados . Se a verificação for necessária, ela precisará ser adicionada ao banco de dados. O método de acesso é o mesmo do 2.5 lendo e gravando diretórios públicos. ②O aplicativo é compartilhado por meio de compartilhamentoApp-specific
MediaScannerConnection.scanFile
MediaProvider
ContentProvider
2.6.3 Outros arquivos do diretório de aplicativos
O aplicativo é uma visualização filtrada. Outros aplicativos não podem acessar diretamente o diretório privado do aplicativo atual. Você precisa usar o seguinte método:
2.6.3.1 Através de arquivo SAF
- Compartilhe a personalização do aplicativo
DocumentsProvider
A personalização do aplicativo DocumentsProvider
requer as seguintes etapas:
a) EspecifiqueDocumentsProvider
b) DocumentsProvider
Implemente a interface básica:
- Acesse o App
ACTION_OPEN_DOCUMENT
e comece a navegar
2.6.3.2 Compartilhando aplicativo para implementar FileProvider
Referência de uso específica do FileProvider
Aqui está um resumo das etapas gerais:
-
FileProvider de aplicativo especificado
-
Especifique o caminho do arquivo, o arquivo de configuração deve ser colocado em res/xml
-
Obter compartilhamento Uri
-
Defina permissões e envie Uri
-
Receba o aplicativo e defina o interfiltro aceito
-
Receber e processar Uri
2.6.3.3 Provedor privado personalizado do aplicativo
Os aplicativos podem ser personalizados ContentProvider
, especialmente 内部文件共享
, mas a interação da interface do usuário não é esperada.
2.7 MediaStore
2.7.1 Campo MediaStore_data
MediaStore
Em, DATA即(_data)字段
, Android Q
começou a ser abandonado em. Ler e gravar arquivos requer aprovação openFileDescriptor
.
2.7.2 Arquivo MediaStore Status pendente
No Android Q, MediaStore
foi adicionado um IS_PENDING Flag
para marcar Pending
o status do arquivo atual.
Outros aplicativos MediaStore
consultam arquivos. Se nenhuma setIncludePending
interface estiver definida, os arquivos definidos com status Pendente não poderão ser consultados. Isso dá ao aplicativo acesso exclusivo a esse arquivo. Usado em algumas situações, como durante o download: Durante o download, o arquivo está no status Pendente e o download é concluído, e o status Pendente do arquivo é definido como 0.
2.7.3 MediaColumns.RELATIVE_PATH define o caminho de armazenamento
No Android Q, ao MediaStore
armazenar arquivos no diretório público, além do diretório de primeiro nível de cada espaço de armazenamento especificado no relacionamento entre Uri e diretório público na Seção 2.5.1.2, o diretório secundário para armazenamento pode ser especificado por meio de MediaColumns. RELATIVE_PATH. Este diretório pode ser criado em vários níveis, o código específico é o seguinte:
- O método de inserção ContentResolver
especifica o diretório de armazenamento por meio de valores.put(Media.RELATIVE_PATH,"Pictures/album/family "). Entre eles, Pictures é um diretório de primeiro nível e album/family é um subdiretório. - O método de atualização ContentResolver
especifica o diretório de armazenamento por meio de valores.put(Media.RELATIVE_PATH,"Pictures/album/family "). Através do método de atualização, o local de armazenamento pode ser movido.
2.7.4 Acessar metadados Exif da imagem
No Android Q, se o aplicativo precisar acessar os metadados Exif na imagem, ele precisará fazer o seguinte:
- Aplicar
ACCESS_MEDIA_LOCATION权限
- O código de demonstração é o
MediaStore.setRequireOriginal返回新Uri
seguinte:
2.7.5 Visualização filtrada do aplicativo, resumo das permissões de acesso
As permissões do aplicativo para acessar diferentes diretórios são resumidas a seguir:
2.7.6 Desinstalação de aplicativos
Se o aplicativo AndroidManifest.xml
declarar em: android:hasFragileUserData="true"
Ao desinstalar o aplicativo, você será questionado se deseja reter os dados do aplicativo:
2.7.7 Migração de dados de aplicativos
No Android Q, App TargetSDK>=Q
o padrão é Filtered View
. Se o aplicativo for uma visualização filtrada, isso envolverá migração de dados, caso contrário, os dados antigos ficarão inutilizáveis. Você pode iniciar a migração de dados a partir dos seguintes aspectos:
- O aplicativo precisa
Legacy View
ser baixado para ter permissão total para operar o armazenamento - Os arquivos de aplicativos armazenados em áreas não públicas podem ser acessados
SAF访问
selecionando arquivos de diretório por meio do SAF, e os usuários podem optar por acessar os arquivos de aplicativos.
- O aplicativo pode colocar os arquivos que precisam ser salvos:
Images
,Video
,Audio
no diretório público correspondente. Outros arquivos podem ser colocadosDownloads
abaixo sem excluir os arquivos após a desinstalação.
2.7.8 Consultas do MediaStore
Ao usar MediaStore
para realizar query
ações, Projection
ao usar, Column Name
deve MediaStore
ser definido em.
2.8 Permissão WRITE_MEDIA_STORAGE
2.8.1 Antecedentes
WRITE_MEDIA_STORAGE
É uma permissão muito poderosa que permite ao App obter acesso a todos os dispositivos de armazenamento. Permissão para acessar todos os dispositivos de armazenamento, esta deve ser dada apenas ao Media Stack.
2.8.2 Impacto de compatibilidade
No sistema Android, está estipulado que o grupo de usuários WRITE_MEDIA_STORAGE
pode ser obtido media_rw
:
- Para todos os dispositivos de armazenamento removíveis, como cartões T e discos U, quando montados no Android, os aplicativos comuns têm apenas permissões de leitura e nenhuma permissão de gravação. Somente aplicativos do grupo de usuários media_rw podem gravar em dispositivos de armazenamento removíveis.
- Para armazenamento com escopo no Android Q, você pode usar essa permissão para configurar o aplicativo para ser executado em modo de compatibilidade.
- O Android CTS realizará testes. Aplicativos inicializáveis pelo usuário não podem solicitar essa permissão.
Para obter detalhes, consulte "Android Bootcamp 2019 - Privacy Overview.pdf".
2.8.3 Adaptação
Se o aplicativo precisar acessar Media
ou 外置存储设备
, você poderá usar MediaStore
ou Storage Access Framework(SAF)接口
.
3 Resumo
Ok, aqui está um resumo:
App TargetSDK > 28 即 Android10(Q)及以上
Projeto, o Google restringiu o modo sandbox de armazenamento. No uso recomendadoAndroid10(Q)以上的设备
,私有目录data/data
ele não pode mais ser acessado diretamente外部SD卡存储目录
. Se precisar usá-lo , você precisa acessá-lo外部SD卡存储目录
através da interface, e só pode ser acessado , como ,,, , , , , etc Esses diretórios externos de armazenamento de cartão SD (diretórios públicos) podem ser acessados por todos os aplicativos, portanto, não são muito seguros.SAF
MediaStore
特定的外部SD卡存储目录
Downloads
Documents
Pictures
DCIM
Movies
Music
Ringtones
App TargetSDK <= 28 即 Android10(Q)以下
O projeto é irrestrito, desde que tenhaWRITE_MEDIA_STORAGE
permissõesREAD_MEDIA_STORAGE
. Portanto, se você realmente não deseja alterar o armazenamento SD externo, altere o targetSdk do projeto para <=28.- Quanto
SAF
ao métodoMediaStore
de acesso à interface外部SD卡存储特定目录
, consulte a descrição acima ou a documentação oficial para obter detalhes. Aqui está uma demonstração deMediaStore
como usá-lo para sua referência外部SD卡存储特定目录
. Não se esqueça de clicar em Estrela.