Opções de armazenamento

O Android oferece várias opções para salvar dados de aplicativos de forma persistente. A solução escolhida depende das necessidades especificas, como se os dados devem ser privados para o aplicativo ou acessíveis para outros aplicativos (e o usuário) e quanto espaço é necessário para os dados.

As opções de armazenamento de dados são:

Preferências compartilhadas
Armazene dados primitivos privados em pares chave-valor.
Armazenamento interno
Armazene dados privados na memória do dispositivo.
Armazenamento externo
Armazena dados públicos no armazenamento externo compartilhado.
Bancos de dados SQLite
Armazene dados estruturados em um banco de dados privado.
Conexão de rede
Armazene dados na web com o seu próprio servidor de rede.

O Android permite expor até seus dados privados para outros aplicativos — com um provedor de conteúdo. Um provedor de conteúdo é um componente opcional que expõe acesso de leitura/gravação para dados do aplicativo, respeitando as restrições que você definir. Para obter mais instruções sobre o uso de provedores de conteúdo, consulte a documentação Provedores de conteúdo.

Como usar preferências compartilhadas

A classe SharedPreferences oferece uma estrutura de trabalho geral que permite salvar e recuperar pares persistentes chave-valor de tipos de dados primitivos. Você pode usar SharedPreferences para salvar qualquer dado primitivo: booleanos, flutuantes, inteiros, longos e strings. Esses dados persistirão entre sessões do usuário (mesmo se o aplicativo for eliminado).

Para obter um objeto SharedPreferences para o aplicativo, use um destes dois métodos:

  • getSharedPreferences() — use esse método se precisar de vários arquivos de preferência, identificados por nome e especificados no primeiro parâmetro.
  • getPreferences() — use esse método se precisar de apenas um arquivo de preferência para a Activity. Como esse será o único arquivo de preferências para a Activity, o nome não é fornecido.

Para gravar valores:

  1. Chame edit() para obter um SharedPreferences.Editor.
  2. Adicione valores com métodos como putBoolean() e putString().
  3. Confirme os novos valores com commit()

Para ler valores, use métodos de SharedPreferences como getBoolean() e getString().

Veja a seguir um exemplo que salva uma preferência para o modo de pressionamento de tela silencioso em uma calculadora:

public class Calc extends Activity {
    public static final String PREFS_NAME = "MyPrefsFile";

    @Override
    protected void onCreate(Bundle state){
       super.onCreate(state);
       . . .

       // Restore preferences
       SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
       boolean silent = settings.getBoolean("silentMode", false);
       setSilent(silent);
    }

    @Override
    protected void onStop(){
       super.onStop();

      // We need an Editor object to make preference changes.
      // All objects are from android.context.Context
      SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
      SharedPreferences.Editor editor = settings.edit();
      editor.putBoolean("silentMode", mSilentMode);

      // Commit the edits!
      editor.commit();
    }
}

Como usar armazenamento interno

É possível salvar arquivos diretamente no armazenamento interno do dispositivo. Por padrão, os arquivos salvos no armazenamento interno são privados do aplicativo e não podem ser acessados por outros aplicativo (nem pelo usuário). Quando o usuário desinstalar o aplicativo, esses arquivos serão removidos.

Para criar e gravar um arquivo privado no armazenamento interno:

  1. Chame openFileOutput() com o nome do arquivo e o modo operacional. Isso retorna um FileOutputStream.
  2. Grave no arquivo com write().
  3. Feche o stream com close().

Por exemplo:

String FILENAME = "hello_file";
String string = "hello world!";

FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();

MODE_PRIVATE cria o arquivo (ou substitui um arquivo de mesmo nome) e o torna privado para o aplicativo. Os outros modos disponíveis são: MODE_APPEND, MODE_WORLD_READABLE e MODE_WORLD_WRITEABLE.

Observação: As constantes MODE_WORLD_READABLE e MODE_WORLD_WRITEABLE estão obsoletas desde a API de nível 17. A partir do Android N, se usadas, acionarão uma SecurityException. Isso significa que aplicativos direcionados ao Android N ou versão posterior não poderão compartilhar arquivos privados por nome, e tentativas de compartilhar um URI “file://” acionarão uma FileUriExposedException. Se o aplicativo precisar compartilhar arquivos privados com outros aplicativos, poderá usar um FileProvider com FLAG_GRANT_READ_URI_PERMISSION. Consulte também Compartilhamento de arquivos.

Para ler um arquivo no armazenamento interno:

  1. Chame openFileInput() e passe o nome do arquivo a ler. Isso retorna um FileInputStream.
  2. Leia bytes do arquivo com read().
  3. E feche o stream com close().

Dica: Se você quiser salvar um arquivo estático no aplicativo durante a compilação, salve o arquivo no diretório res/raw/ do projeto. Você pode abri-lo com openRawResource(), passando o ID de recurso R.raw.<filename>. Esse método retorna um InputStream que pode ser usado para ler o arquivo (mas não é possível gravar no arquivo original).

Salvar arquivos de cache

Se você quiser armazenar dados em cache, em vez de armazená-los de forma persistente, use getCacheDir() para abrir um File que representa o diretório interno onde o aplicativo deve salvar arquivos de cache temporários.

Quando houver pouco espaço de armazenamento interno no dispositivo, o Android poderá excluir esses arquivos de cache para liberar espaço. No entanto, não dependa do sistema para limpar esses arquivos. Mantenha sempre você mesmo os arquivos de cache e respeite um limite razoável de espaço consumido — como 1 MB. Quando o usuário desinstalar o aplicativo, esses arquivos serão removidos.

Outros métodos úteis

getFilesDir()
Obtém o caminho absoluto do diretório do sistema de arquivos onde os arquivos internos são salvos.
getDir()
Cria um diretório (ou abre um diretório existente) no espaço de armazenamento interno.
deleteFile()
Exclui um arquivo salvo no armazenamento interno.
fileList()
Retorna uma matriz dos arquivos salvos pelo aplicativo nesse momento.

Como usar armazenamento externo

Todo dispositivo compatível com Android permite um “armazenamento externo” compartilhado que pode ser usado para salvar arquivos. Esse armazenamento pode ser uma mídia de armazenamento removível (como um cartão SD) ou um armazenamento interno (não removível). Os arquivos salvos no armazenamento externo podem ser lidos por todos e modificados pelo usuário quando o armazenamento em massa de USB é ativado para transferir arquivos para um computador.

Atenção: O armazenamento externo poderá ficar indisponível se o usuário montar o armazenamento externo em um computador ou remover a mídia. Não é aplicada nenhuma segurança nos arquivos salvos no armazenamento externo. Todos os aplicativos podem ler e gravar arquivos colocados no armazenamento externo e o usuário pode removê-los.

Uso do acesso a diretórios com escopo

No Android 7.0 ou versão posterior, se for necessário acessar um diretório específico no armazenamento externo, use o acesso a diretórios com escopo. O acesso a diretórios com escopo simplifica a forma com que o aplicativo acessa diretórios de armazenamento externo padrão, como o diretório Pictures, e oferece uma IU de permissões simples que detalha claramente a qual diretório o aplicativo está solicitando acesso. Para obter mais detalhes sobre o acesso a diretórios com escopo, consulte Uso do acesso a diretórios com escopo.

Obter acesso ao armazenamento externo

Para ler ou gravar arquivos no armazenamento externo, o aplicativo deve adquirir as permissões de sistema READ_EXTERNAL_STORAGE ou WRITE_EXTERNAL_STORAGE. Por exemplo:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

Se você precisar ler e gravar arquivos, solicite apenas a permissão WRITE_EXTERNAL_STORAGE porque ela exige implicitamente o acesso de leitura.

Observação: A partir do Android 4.4, essas permissões não são necessárias para ler ou gravar apenas arquivos privados do aplicativo. Para obter mais informações, consulte a seção abaixo sobre salvar arquivos privados do aplicativo.

Verificar disponibilidade da mídia

Antes de executar qualquer trabalho com o armazenamento externo, chame sempre getExternalStorageState() para verificar se a mídia está disponível. A mídia pode estar montada em um computador, ausente, somente para leitura ou em algum outro estado. Por exemplo, veja a seguir alguns métodos que podem ser usados para verificar a disponibilidade:

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

O método getExternalStorageState() retorna outros estados que você pode verificar, como se a mídia está sendo compartilhada (conectada a um computador), se não está presente, se foi removida incorretamente etc. Use esses estados para notificar o usuário com mais informações quando o aplicativo precisar acessar a mídia.

Salvar arquivos que podem ser compartilhados com outros aplicativos

Normalmente, arquivos novos adquiridos pelo usuário por meio do aplicativo devem ser salvos em um local “público” no dispositivo, acessíveis por outros aplicativos e facilmente copiáveis do dispositivo pelo usuário. Para fazer isso, use um dos diretórios públicos compartilhados, como Music/, Pictures/ e Ringtones/.

Para obter um File representando o diretório público adequado, chame getExternalStoragePublicDirectory(), passando o tipo de diretório desejado, como DIRECTORY_MUSIC, DIRECTORY_PICTURES, DIRECTORY_RINGTONES ou outros. Salvar os arquivos no diretório de tipo de mídia correspondente permite que o detector de mídia do sistema categorize adequadamente os arquivos no sistema (por exemplo, os toques aparecem nas configurações de sistema como toques e não como música).

Por exemplo, veja a seguir um método que cria um diretório para um novo álbum de fotografias no diretório público de fotos:

public File getAlbumStorageDir(String albumName) {
    // Get the directory for the user's public pictures directory.
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

Salvar arquivos privados do aplicativo

Se você estiver processando arquivos que não devem ser usados por outros aplicativos (como texturas gráficas ou efeitos sonoros usados apenas pelo seu aplicativo), use um diretório de armazenamento privado no armazenamento externo chamando getExternalFilesDir(). Esse método também recebe um argumento type para definir o tipo do subdiretório (como DIRECTORY_MOVIES). Se você não precisar de um diretório de mídia específico, passe null para receber o diretório raiz do diretório privado do aplicativo.

A partir do Android 4.4, a leitura ou gravação de arquivos nos diretórios privados do aplicativo não exigem as permissões READ_EXTERNAL_STORAGE ou WRITE_EXTERNAL_STORAGE. Portanto, você pode declarar que a permissão deve ser solicitada penas nas versões anteriores do Android, adicionando o atributo maxSdkVersion:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     android:maxSdkVersion="18" />
    ...
</manifest>

Observação: quando o usuário desinstala o aplicativo, esse diretório e todo o seu conteúdo é excluído. Além disso, o detector de mídia do sistema não lê arquivos nesses diretórios, o que significa que eles não podem ser acessados pelo provedor de conteúdo MediaStore. Desse modo, não use esses diretórios para mídia que, essencialmente, pertence ao usuário, como fotos capturadas ou editadas pelo aplicativo ou música comprada pelo usuário usando o aplicativo — esses arquivos devem ser salvos nos diretórios públicos.

Algumas vezes, um dispositivo que alocou uma partição da memória interna para uso como armazenamento externo também pode oferecer um slot de cartão SD. Quando esse dispositivo executa o Android 4.3 ou versões anteriores, o método getExternalFilesDir() oferece acesso apenas à partição interna e o aplicativo não consegue ler ou gravar o cartão SD. No entanto, a partir do Android 4.4, é possível acessar os dois locais chamando getExternalFilesDirs(), que retorna uma matriz de File com uma entrada para cada local. A primeira entrada na matriz é considerada o armazenamento externo principal e o local a ser usado, a menos que esteja cheio ou indisponível. Se você quiser acessar os locais possíveis, sem deixar de ser compatível com o Android 4.3 ou anterior, use o método estático ContextCompat.getExternalFilesDirs() da biblioteca de suporte. Esse método também retorna uma matriz de File, mas contém sempre apenas uma entrada no Android 4.3 e anteriores.

Atenção: embora os diretórios fornecidos por getExternalFilesDir() e getExternalFilesDirs() não sejam acessíveis pelo provedor de conteúdo MediaStore, outros aplicativos com a permissão READ_EXTERNAL_STORAGE podem acessar todos os arquivos no armazenamento externo, inclusive os fornecidos por esses dois métodos. Se você precisar restringir completamente o acesso a seus arquivos, grave-os no armazenamento interno.

Salvar arquivos de cache

Para abrir um File que represente o diretório de armazenamento externo onde você deve salvar arquivos de cache, chame getExternalCacheDir(). Se o usuário desinstalar o aplicativo, esses arquivos serão automaticamente excluídos.

De forma semelhante a ContextCompat.getExternalFilesDirs(), mencionado acima, também é possível acessar um diretório de cache em um armazenamento externo secundário (se disponível) chamando ContextCompat.getExternalCacheDirs().

Dica: para preservar o espaço de arquivos e manter o desempenho do aplicativo, é importante gerenciar cuidadosamente os arquivos de cache durante todo o ciclo de vida do aplicativo, removendo os que não forem mais necessários.

Como usar bancos de dados

O Android é totalmente compatível com bancos de dados SQLite. Todos os bancos de dados criados poderão ser acessados pelo nome em qualquer classe do aplicativo, mas não fora dele.

O método recomendado para criar um novo banco de dados SQLite é criar uma subclasse de SQLiteOpenHelper e modificar o método onCreate(), em que é possível executar um comando SQLite para criar tabelas no banco de dados. Por exemplo:

public class DictionaryOpenHelper extends SQLiteOpenHelper {

    private static final int DATABASE_VERSION = 2;
    private static final String DICTIONARY_TABLE_NAME = "dictionary";
    private static final String DICTIONARY_TABLE_CREATE =
                "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" +
                KEY_WORD + " TEXT, " +
                KEY_DEFINITION + " TEXT);";

    DictionaryOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(DICTIONARY_TABLE_CREATE);
    }
}

É possível obter uma instância da sua implementação de SQLiteOpenHelper usando o construtor que você definiu. Para gravar e ler no banco de dados, chame respectivamente getWritableDatabase() e getReadableDatabase(). Os dois métodos retornam um objeto SQLiteDatabase que representa o banco de dados e fornece métodos para operações do SQLite.

Você pode executar consultas do SQLite usando os métodos de query() de SQLiteDatabase, que aceitam vários parâmetros de consulta, como tabela a consultar, projeção, seleção, colunas e agrupamento, entre outros. Para consultas complexas, como as que exigem aliases de coluna, use SQLiteQueryBuilder, que fornece vários métodos convenientes para a criação de consultas.

Toda consulta do SQLite retorna um Cursor, que aponta para as linhas encontradas pela consulta. O Cursor é sempre o mecanismo usado para navegar pelos resultados de uma consulta de banco de dados e ler linhas e colunas.

Para obter aplicativos de amostra, que demonstram como usar bancos de dados SQLite no Android, consulte os aplicativos Note Pad e Searchable Dictionary.

Depuração de bancos de dados

O Android SDK contém uma ferramenta de banco de dados sqlite3 que permite navegar pelo conteúdo de tabelas e executar comandos do SQL e outras funções úteis em bancos de dados SQLite. Consulte Examinar bancos de dados sqlite3 de um shell remoto para saber como executar essa ferramenta.

Como usar conexões de rede

Você pode usar a rede (se disponível) para armazenar e recuperar dados em seus próprios serviços baseados na Web. Para executar operações de rede, use as classes dos seguintes pacotes: