Comunicação HTTP eficiente no Android com Volley
Olá povo,
Conversando com meus novos mentores Neto Marin, Marcelo Quinta e Lúcio Maciel que conheci no DevBus Brasil, percebi uma unanimidade no que diz respeito a acesso HTTP no Android: a biblioteca Volley. Quando assisti o vídeo do Ficus Kirkpatrick no Google IO de 2013 não dei muita bola, mas os meus novos “orientadores” me mostraram o quanto essa biblioteca é poderosa e fácil de usar. Tudo isso aliado ao fato dela ser mantida pelo próprio Google.
Eu já falei em outros posts aqui do blog como fazer comunicação HTTP com AsyncTask (link 1 e link 2) e com AsyncTaskLoader (link), assim como carregar imagens da web em um Adapter usando o UniversalImageLoader (link). E também como ler um JSON de um WebService REST (link). Mas com o Volley podemos resolver todos os problemas citados nos posts anteriores e ainda remover todo aquele boiler plate que fazemos ao realizar essas requisições, simplificando o código. Além disso, ela tem as seguintes vantagens:
- Comunicação paralela com possibilidade de priorização. Por exemplo, podemos criar uma fila de requisições JSON e outra de requisição de imagens e dizer que a primeira tem maior prioridade que a segunda;
- Quando giramos a Activity, a mesma é destruída (mostrei como resolver isso aqui) e se nesse momento estiver havendo alguma requisição HTTP, o resultado é perdido. Podemos implementar algum tipo de cache em memória ou em banco, mas o Volley já faz esse serviço pra gente.
- Um “ImageLoader” para carregar imagens da web, particularmente útil em adapters (falei de adapter aqui).
Depois dessa breve introdução, vamos por a mão na massa!
O Volley está em um repositório git no código-fonte do próprio Android. Então você terá que fazer um clone do mesmo via comando. Se você não tem o git instalado na sua máquina, siga esse tutorial aqui. Depois é só digitar no terminal.
git clone https://android.googlesource.com/platform/frameworks/volley
Feito isso, importe o projeto do Volley dentro do Eclipse e marque-o como biblioteca clicando com o botão direito sobre o projeto e selecionando Properties. Em seguida, selecione a opção Android do lado esquerdo e marque a checkbox “Is Library”.
Em homenagem ao Ricardo Lecheta, vou fazer uma Activity que lê o JSON de carros disponível no site do seu livro de Android (que por sinal é muito bom e uso nas minhas aulas). E para ler o JSON e as imagens dos carros, vamos usar o Volley.
Vamos começar pela classe que vai manter a fila de execução de requisições do Volley bem como seu ImageLoader. O Google recomenda que ela seja um singleton, pois podemos gerenciar quantas filas de execução podemos ter e não termos que criar várias.
public class VolleySingleton {
private static VolleySingleton mInstance = null;
// Fila de execução
private RequestQueue mRequestQueue;
// Image Loader
private ImageLoader mImageLoader;
private VolleySingleton(Context context){
mRequestQueue = Volley.newRequestQueue(context);
mImageLoader = new ImageLoader(this.mRequestQueue,
new ImageLoader.ImageCache() {
// Usando LRU (Last Recent Used) Cache
private final LruCache
mCache = new LruCache(10);
public void putBitmap(
String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
});
}
public static VolleySingleton getInstance(
Context context){
if(mInstance == null){
mInstance = new VolleySingleton(context);
}
return mInstance;
}
public RequestQueue getRequestQueue(){
return this.mRequestQueue;
}
public ImageLoader getImageLoader(){
return this.mImageLoader;
}
}
Vou definir um POJO simples que representa os objetos carro que iremos listar.
public class Carro {
String nome;
String imageUrl;
public Carro(String nome, String imageUrl) {
this.nome = nome;
this.imageUrl = imageUrl;
}
}
Abaixo temos o arquivo de layout usado no adapter. Note que estamos usando NetworkImageView ao invés do ImageView tradicional.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/itemRoot"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<com.android.volley.toolbox.NetworkImageView
android:id="@+id/img"
android:layout_width="140dp"
android:layout_height="70dp"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/txtName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="20dp"
android:text="@null"/>
A classe Adapter listada abaixo herda de ArrayAdapter e está usando o NetworkImageView e o ImageLoader do Volley.
public class CarroAdapter extends ArrayAdapter{
static final int LAYOUT = R.layout.item_lista;
public CarroAdapter(Context context,
List objects) {
super(context, LAYOUT, objects);
}
@Override
public View getView(int position,
View convertView, ViewGroup parent) {
Context ctx = parent.getContext();
if (convertView == null){
convertView = LayoutInflater.from(ctx)
.inflate(R.layout.item_lista, null);
}
NetworkImageView img = (NetworkImageView)
convertView.findViewById(R.id.img);
TextView txt = (TextView)
convertView.findViewById(R.id.txtName);
Carro carro = getItem(position);
txt.setText(carro.nome);
img.setImageUrl(
carro.imageUrl,
VolleySingleton.getInstance(
getContext()).getImageLoader());
return convertView;
}
}
E finalmente a Activity… Ela implementa duas interfaces: Response.Listener e Response.ErrorListener. O método da primeira (onResponse) será chamada quando a requisição ocorrer sem problemas, e da segunda (onErrorResponse) caso contrário. No onCreate, obtemos a fila de execução a partir do nosso singleton, e depois criamos um JsonObjectRequest. No Volley, além desse tipo de Request, temos o ImageRequest e o StringRequest, o primeiro para imagens e o segundo para qualquer requisição que retorne uma String. Após criar a requisição, a adicionamos na fila para ser executada.
No método onResponse já recebemos o JSONObject, então é só fazer o parse do mesmo. Aqui poderíamos usar o Gson, mas eu não gosto dele :p
Após transformar o JSONObject em uma Lista de Carros, criamos e setamos o adapter da nossa ListActivity.
Se alguma coisa der errado, o método onErrorResponse será chamado, e daí estamos exibindo um Toast.
public class MainActivity extends ListActivity
implements
Response.Listener,
Response.ErrorListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String url="http://www.livroandroid.com.br/livro/"+
"carros/carros_classicos.json";
RequestQueue queue = Volley.newRequestQueue(this);
JsonObjectRequest jsObjRequest =
new JsonObjectRequest(
Request.Method.GET, // Requisição via HTTP_GET
url, // url da requisição
null, // JSONObject a ser enviado via POST
this, // Response.Listener
this); // Response.ErrorListener
queue.add(jsObjRequest);
}
@Override
public void onResponse(JSONObject response) {
List carros = new ArrayList();
try {
// Não precisamos converter o
// InputStream em String \o/
JSONObject jsonCarros =
response.getJSONObject("carros");
JSONArray jsonCarro =
jsonCarros.getJSONArray("carro");
for (int i = 0; i < jsonCarro.length(); i++) {
JSONObject jsonCarroItem =
jsonCarro.getJSONObject(i);
String nome =
jsonCarroItem.getString("nome");
String thumbnail =
jsonCarroItem.getString("url_foto");
Carro carro = new Carro(nome, thumbnail);
carros.add(carro);
}
} catch (Exception e){
e.printStackTrace();
}
setListAdapter(new CarroAdapter(this, carros));
}
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(this, "Erro!",
Toast.LENGTH_SHORT).show();
}
}
Ah! Como toda app Android que acessa a Web, adicione a permissão de Internet no seu AndroidManifest.xml.
É isso, a partir de agora podemos utilizar o Volley em nossas aplicações com essa biblioteca usada e mantida pelo próprio Google. Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber