Persistindo dados simples com SharedPreferences

Muitas vezes precisamos persistir dados durante o uso da aplicação, para que possam ser utilizados posteriormente, como preferências de usuário, indicação de primeira vez do uso do aplicativo, e justamente quando estes dados são consideravelmente pequenos não vale a pena criar um banco de dados, que acaba gerando um trabalho extra. Pensando nisso o Android disponibilizou uma forma de armazenamento de dados denominadoSharedPreferences ou Preferências de Usuário.

As SharedPreferences são utilizadas em situações onde não há necessidade de criação de um banco de dados, ou até mesmo quando há pouco número de dados a ser armazenado. Em linhas gerais, este armazenamento pode consistir em diversos tipos de dados, imagine uma aplicação de previsão do tempo, onde o usuário pode escolher entre exibir a temperatura em Fahrenheit ou em grau Celsius, este tipo de preferência é o tipo de dado que deve ser armazenado nas preferências de usuário, pois quando a aplicação iniciar já estará na escala de temperatura escolhida, o que é bem melhor do que perguntar ao usuário a cada vez que ele entre na aplicação. Outra utilização bem comum é uma flag para saber se é a primeira vez que o usuário entra na aplicação, para exibição ou omissão de algum tutorial. Geralmente as informações referentes à configurações da aplicação também podem ser armazenadas nas SharedPreferences.

Nota: Jamais, em hipótese alguma, salve senhas de usuários nas SharedPreferences, inclusive recomenda-se que nenhum dado pessoal de usuário, como cartão de crédito, telefone, e senhas sejam armazenadas de alguma forma no aparelho. Este tipo de prática não está de acordo com boas práticas no desenvolvimento mobile e deve ser tratado de outra forma. Falaremos disso mais a frente.

Entendendo as SharedPreferences

As SharedPreferences consiste em uma interface que permite acessar e modificar dados de preferência de usuário. O valor armazenado apresenta-se sob formato chave-valor ou key-value, ou seja, cada preferência armazenada possui uma identificação ou chave e associada a ela está um valor. Ela permite armazenamento de diversos tipos de valor, como int, float, Strings, booleans e sets de Strings.

getPreferences() x getSharedPreferences()

Existem basicamente duas formas de se trabalhar com as SharedPreferences, quando há poucas e quando há muitas preferências. Digamos que o que precisa ser salvo sejam dados dentro de uma mesma chave, como preferências de configuração da aplicação, então o método recomendado a ser escolhido é getPreferences(). Já se deverá ser salvas diversas preferências então deve-se escolher o getSharedPreferences().

Criando uma Preferência

Como dito anteriormente, a sharedPreference utiliza o conceito de chave-valor (key-value) para armazenamento de valores. Muitas vezes precisamos armazenar mais de um valor, porém que estejam correlacionados. Digamos que em uma aplicação você possua diversas chaves para serem armazenadas, algumas ligadas à categorias, outras ligadas aos tutoriais do app, outras ligadas a configurações de localização. O ideal é que se tenha uma sharedpreference para cada “categoria” de valores a serem salvos, e que dentro de cada categoria sim salve-se as preferências específicas.

Para criar uma preferência basta chamar o método getSharedPreferences e passar dois parâmetros: nome da chave (String) e modo (int). O nome da chave é o identificador, funciona como um id da preferência, já o modo está relacionado a modos de acesso das preferências, veja abaixo os modos existentes:

PRIVATE — Não permite que a preferência seja acessada de outras aplicações, a não ser que compartilhem do mesmo UserId.

MODE_APPEND — Aplica-se à criação de arquivos, para ser utilizado com openFileOutput(String, int). Neste caso se o arquivo já existir então o dado é colocado após o último item, evitando que o arquivo seja apagado.

MODE_ENABLE_WRITE_AHEAD_LOGGING — aplicado à banco de dados

Nota: Há outros modos além dos citados, porém já estão em estado deprecated, portanto sempre verifique se o método ainda pode ser utilizado, para não usar valores deprecated.criação de um banco de dados, ou até mesmo quando há pouco número de dados a ser armazenado. Em linhas gerais, este arma
SharedPreferences sharedPreferences = getSharedPreferences(getString(R.string.pref_key), Context.MODE_PRIVATE);

Salvando uma preferência

Em determinada situação será preciso salvar uma preferência, e neste momento utiliza-se de um Editor, que através dele as preferências terão seus valores modificados, considerando seu tipo. Este "valor armazenado" na preferência é persistido até que a preferência seja alterada ou apagada.

Para então salvar a preferência desejada, precisa acessar o método edit() no objeto da preferência, criado anteriormente. Depois disso deverá ser colocado a chave e o valor no tipo de preferência desejada, escolhendo entre inteiros, booleans, strings, entre outros.

SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(getString(R.string.pref_text), fieldText.getText().toString());
editor.apply();
Nota: Ao salvar uma preferência é preciso chamar o método apply() ou commit() para que a mudança seja aplicada.

Apply() versus Commit()

Aparentemente os métodos apply() e commit() executam a mesma ação: salvar as alterações nas preferências. Porém, eles possuem particularidades que os diferenciam em certos aspectos.

O commit(), que está desde a primeira versão da API, é executado de forma síncrona, ou seja, na MainThread. Além disto ele retorna um booleano, ou seja, é possível, através de seu retorno, verificar se a preferência foi realmente salva.

O apply() foi incluído a partir da versão 9, apesar de comittar imediatamente as alterações em memória, é gerado um processo assíncrono que irá salvar as alterações na preferência desejada. Este método não possui retorno, o que impossibilita de verificar imediatamente se a preferência foi salva. Porém, possui a vantagem de não ocupar a MainThread com um procedimento que pode ser feito de forma assíncrona.

A escolha entre qual método deve ser utilizado requer uma análise considerando os pontos citados. É preciso ressaltar também, que se duas preferências forem setadas (ou seja estiverem sendo salvas) concomitantemente, o Android entende que só a última é verdadeira, invalidando a anterior.

Buscando uma preferência

Buscar uma preferência também é uma ação que poderá ser requisitada diversas vezes. Como as preferências são salvas baseadas num par chave-valor basta buscá-la informando a chave. Da mesma forma que criam-se preferências passando o modo de acesso, na hora de buscá-las ocorre a mesma coisa, e posteriormente busca-se o valor desejado, também passando a chave, veja o exemplo:

sharedPreferences = getSharedPreferences(getString(R.string.pref_key), Context.MODE_PRIVATE);
String result = sharedPreferences.getString(getString(R.string.pref_text), "");
resultText.setText("Resultado --> " + result);

Conclusão

As SharedPreferences auxiliam o desenvolvimento de uma aplicação android, permitem um menor esforço que se teria ao utilizar banco de dados. É preciso considerar a real necessidade de utilizar uma preferência, para não sobrecarregar o arquivo sem necessidade.

Nota: Todos o código utilizado neste exemplo encontram-se no seguinte projeto no github: https://github.com/paulacr/SharedPreferencesExample