Escrita e Leitura de Arquivo no Android–Parte II

No primeiro post sobre escrita e leitura de arquivos no Android descrevi como ler e escrever na memória interna do Android. Este post descreve como escrever e ler em arquivos armazenados no cartão SD do dispositivo.

Antes de optar em gravar informações no cartão SD do dispositivo, lembre-se que o usuário terá acesso as informações deste arquivo e poderá apagá-lo a qualquer momento. Também não existe a garantia do arquivo não ser apagado durante a conexão ou desconexão do dispositivo com o microcomputador, caso o mesmo seja desconectado de forma incorreta.

Para demonstrar a leitura e escrita no cartão SD, vou utilizar o mesmo layout do projeto da parte I do post:
arquivo1
Antes de criar e escrever em um arquivo no cartão SD de um dispositivo, é necessário verificar se o cartão está inserido, quais são as permissões de escrita e leitura, se o sistema de arquivo é reconhecido pelo dispositivo, entre outras informações.

Foi criada a classe ManageFile com o método getStateSDcard() que verifica o estado do cartão SD, o método  WriteFile() que faz a escrita do texto e o método ReadFile() que faz a leitura do texto armazenado no cartão SD.

public class ManageFile {
    private static final String TAG = "ManageFile";
    private Context context;
    private boolean sdCardAvailable;
    private boolean sdCardWritableReadable;
    private boolean sdCardReadableOnly;
    
    public ManageFile(Context context){
        this.context = context;
    }

    /**
     * Escreve no arquivo texto.
     * @param text Texto a ser escrito.
     * @return True se o texto foi escrito com sucesso.
     */
    public boolean WriteFile(String text){
        try {
            File file = new File(context.getExternalFilesDir(null),
                "romar.txt");
            FileOutputStream out = new FileOutputStream(file, true);
            out.write(text.getBytes());
            out.write("\n".getBytes());
            out.flush();
            out.close();    
            return true;
            
        } catch (Exception e) {
            Log.e(TAG, e.toString());
            return false;
        }
    }
    
    /**
     * Faz a leitura do arquivo
     * @return O texto lido.
     * @throws FileNotFoundException
     * @throws IOException
     */
    public String ReadFile() throws FileNotFoundException, IOException{
        File textfile = new File(context.getExternalFilesDir(null),
            "romar.txt");

        FileInputStream input = new FileInputStream(textfile);
        byte[] buffer = new byte[(int)textfile.length()];
        
        input.read(buffer);            
        
        return new String(buffer);
    }
    
    public void getStateSDcard(){
        
        // Obtêm o status do cartão SD
        String status = Environment.getExternalStorageState();
        
        if (Environment.MEDIA_BAD_REMOVAL.equals(status)) {
            // Midia foi removida antes de ser montada
            sdCardAvailable = false;
            sdCardWritableReadable = false;
            sdCardReadableOnly = false;
            Log.d(TAG, "Midia removida.");
        }
        else if (Environment.MEDIA_CHECKING.equals(status)) {
            // Midia está presente e está sendo feita a verificação
            sdCardAvailable = true;
            sdCardWritableReadable = false;
            sdCardReadableOnly = false;
            Log.d(TAG, "Midia sendo verificada.");
        }
        else if (Environment.MEDIA_MOUNTED.equals(status)) {
            // A midia está presente e montada neste momento com
            // permissão de escrita e leitura
            sdCardAvailable = true;
            sdCardWritableReadable = true;
            sdCardReadableOnly = false;
            Log.d(TAG, "Midia com permissão de escrita e leitura.");
        }
        else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(status)) {
            // A midia está presente e montada neste momento com 
            // permissão somente de leitura
            sdCardAvailable = true;
            sdCardWritableReadable = false;
            sdCardReadableOnly = false;
            Log.d(TAG, "Midia com permissão somente leitura.");
        }
        else if (Environment.MEDIA_NOFS.equals(status)) {
            // A midia está presente, mas está vazia ou utilizando um
            // sistema de arquivos não suportado    
            sdCardAvailable = false;
            sdCardWritableReadable = false;
            sdCardReadableOnly = false;
            Log.d(TAG, "Midia com sistema de arquivos não compatível.");
        }
        else if (Environment.MEDIA_REMOVED.equals(status)) {
            // A midia não está presente
            sdCardAvailable = false;
            sdCardWritableReadable = false;
            sdCardReadableOnly = false;
            Log.d(TAG, "Midia não presente.");
        }
        else if (Environment.MEDIA_SHARED.equals(status)) {
            // A midia está presente, não montada e compartilhada 
            // via USB
            sdCardAvailable = false;
            sdCardWritableReadable = false;
            sdCardReadableOnly = false;
            Log.d(TAG, "Midia compartilhada via USB.");
        }
        else if (Environment.MEDIA_UNMOUNTABLE.equals(status)) {
            // A midia está presente mas não pode ser montada
            sdCardAvailable = false;
            sdCardWritableReadable = false;
            sdCardReadableOnly = false;
            Log.d(TAG, "Midia não pode ser montada");
        }
        else if (Environment.MEDIA_UNMOUNTED.equals(status)) {
            // A midia está presente mas não montada
            sdCardAvailable = false;
            sdCardWritableReadable = false;
            sdCardReadableOnly = false;
            Log.d(TAG, "Midia não montada.");
        }
    }

    public boolean isSdCardAvailable() {
        return sdCardAvailable;
    }

    public void setSdCardAvailable(boolean sdCardAvailable) {
        this.sdCardAvailable = sdCardAvailable;
    }

    public boolean isSdCardWritableReadable() {
        return sdCardWritableReadable;
    }

    public void setSdCardWritableReadable(boolean sdCardWritableReadable) {
        this.sdCardWritableReadable = sdCardWritableReadable;
    }

    public boolean isSdCardReadableOnly() {
        return sdCardReadableOnly;
    }

    public void setSdCardReadableOnly(boolean sdCardReadableOnly) {
        this.sdCardReadableOnly = sdCardReadableOnly;
    }

}

No método getStateSDcard() foi utilizado o método getExternalStorageState() da classe Environment do Android que retorna o estado do cartão SD. Para cada estado retornado é setado um flag para indicar se o cartão está disponível, se permite escrita e leitura, ou se permite somente somente leitura.

No método WriteFile() foi utilizado o método getExternalFilesDir() do Android para obter o diretório do cartão SD. Foi passado o valor null como parâmetro, para que seja retornada a raiz do diretório da aplicação na área de armazenamento externa, no nosso caso o cartão SD (válido para a API 8, vide nota abaixo).

O arquivo vai ser escrito no diretório:
/Android/data/<nome_pacote>/files/
onde nome_pacote é o pacote da aplicação, no nosso caso br.com.romar.

NOTA: O método getExternalFilesDir() deve ser utilizado em projetos que rodam a partir da API 8. Para projetos que rodam na API 7 ou inferior, deverá ser utilizado o método getExternalStorageDirectory(), que irá retornar a raiz do diretório do cartão SD. Neste caso o arquivo será gravado na raiz do cartão e não no diretório da aplicação. No caso da API 8, quando a aplicação for desinstalada os arquivos que estiverem no cartão SD no diretório /Android/data/<nome_pacote>/files serão apagados.

No método ReadFile() foi utilizado o mesmo método  getExternalFilesDir() do Android para abrir o arquivo gravado anteriormente para leitura.

Na Activity principal do projeto foi criado um botão para escrita e leitura do arquivo. Segue o código que trata o click em cada um dos botões:
@Override
public void onClick(View v) {
    
try {
    switch (v.getId()) {
    case R.id.btnRead: // Faz a leitura do arquivo    
        
        // Verifica se o sdcard tem permissão para leitura
        if (managefile.isSdCardAvailable() && 
                (managefile.isSdCardReadableOnly() || 
                        managefile.isSdCardWritableReadable())) {
            managefile.getStateSDcard();
            textRead.setText(managefile.ReadFile());                    
        }
        else {
            Toast.makeText(this, 
                "O cartão SD não está disponível, ou não permite" +
                " leitura", Toast.LENGTH_SHORT).show();
        }            
        break;

    case R.id.btnWrite: // Faz a escrita do arquivo
        
        // Verifica se o sdcard tem permissão para escrita
        if (managefile.isSdCardAvailable() &&
                managefile.isSdCardWritableReadable()) {
            // Avisa o usuário se a gravação foi bem sucedida
            if(managefile.WriteFile(editText.getText().toString()) == 
                true){
                Toast.makeText(this, 
                    "Texto gravado com sucesso.",
                    Toast.LENGTH_SHORT).show();
            }
            else{
                Toast.makeText(this, 
                    "Não foi possível escrever o texto.", 
                    Toast.LENGTH_SHORT).show();
            }
        }
        else {
            Toast.makeText(this, 
                    "O cartão SD não está disponível, 
                    ou não permite" +
                    " escrita.", Toast.LENGTH_SHORT).show();                    
        }        
        
        // Reseta o campo do texto
        editText.setText("");
        
        break;
        
    default:
        break;
    }

} catch (FileNotFoundException e) {
    Log.e(TAG, e.toString());
} catch (IOException e) {
    Log.e(TAG, e.toString());
}

}

Antes de fazer a escrita ou leitura no arquivo, o código verifica primeiro se o cartão SD está disponível e permite escrita ou leitura dependendo do caso.

O cartão SD do dispositivo por padrão possui somente permissão de leitura. Para que seja possível escrever no cartão é necessário que no momento da instalação da aplicação o usuário dê a permissão de escrita na área de armazenamento externo. Para que a aplicação tenha a permissão de escrita é necessário inserir no arquivo AndroidManifest.xml da aplicação a seguinte tag:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />