<p>O Instagram como você já deve ter ouvido falar é um serviço de compartilhamento de fotos. Como muitos dos aplicativos web ele fornece uma API para permitir que outras aplicações troquem informações com ele, por isso é um exemplo legal de como fazer o <em>Android</em> acessar um <em>WebService</em>. </p>
<p>Para construir um aplicativo que se comunique com o <strong>Instagram</strong> é necessário registrar sua aplicação para obter um <em>cliente_id</em> e uma <em>secret</em>, isso pode ser feito pelo endereço <a href="http://instagram.com/developer/" target="_blank">Instagram Developer</a>. Após logar com sua conta você deve ir em <em>Manage Clients</em> -> <em>Register a New Client</em>. Após ter se cadastrado corretamente você terá os seguintes dados.</p>
<p><a href="http://www.botecodigital.info/wp-content/uploads/2014/09/instagram.png" title="Acessando a API(webservice) do Instagram pelo Android"><img src="http://www.botecodigital.info/wp-content/uploads/2014/09/instagram.png" alt="instagram" width="700" /></a></p>
<p>Para consultar por tag temos as seguintes URL</p>
<pre>
https://api.instagram.com/v1/tags/{TAG_A_BUSCAR}/media/recent?client_id={CLIENT_ID}
</pre>
<p>Para consultar as postagens de um usuário temos a seguinte URL</p>
<pre>
https://api.instagram.com/v1/users/{ID_DO_USUARIO}/media/recent/?client_id={CLIENT_ID}
</pre>
<p><small>* o ID_DO_USUARIO não é o login e sim o identificar, você pode utilizar a <a href="http://www.pinceladasdaweb.com.br/instagram/user-id/" target="_blank">ferramenta do pinceladas da web</a></small></p>
<p>Existem várias outras URLs que fornecem informações diferentes, você pode ver melhor na <a href="http://instagram.com/developer/endpoints/">documentação</a> oficial.</p>
<p>Se você já acessou a URL pelo navegador deve ter notado que o formato de resposta é no formato <a href="http://www.botecodigital.info/jquery/trocando-dados-utilizando-json/">JSON que já falamos um pouco aqui no blog</a> como acessar informações via Javascript, mas agora usaremos Java, este é o legal de utilizar estes formatos para integração de sistemas, ficamos independente de linguagem. Parando um pouco de enrolação vamos ver um exemplo de retorno de uma requisição a uma das URLs(https://api.instagram.com/v1/tags/cervejadigital/media/recent/?client_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) indentada para facilitar o entendimento.</p>
<pre>
{
"pagination":{
"next_max_tag_id":"1401066135218647",
"deprecation_warning":"next_max_id and min_id are deprecated for this endpoint; use min_tag_id and max_tag_id instead",
"next_max_id":"1401066135218647",
"next_min_id":"1408850711681714",
"min_tag_id":"1408850711681714",
"next_url":"https:\/\/api.instagram.com\/v1\/tags\/cervejadigital\/media\/recent?client_id=c3f1322f34db4dc8bbbf386532f73a65\u0026max_tag_id=1401066135218647"
},
"meta":{
"code":200
},
"data":[
{
"attribution":null,
"tags":[
"cerveja",
"birra",
"cervejaespecial",
"fullpintbr",
"beer",
"instagood",
"olut",
"beergeek",
"cervejadigital",
"biere",
"brejadodia",
"pivo",
"beerstagram",
"cerveza"
],
"location":null,
"comments":{
"count":1,
"data":[
{
"created_time":"1408890792",
"text":"Ta sabendo heim !! Top.",
"from":{
"username":"diegofioravante",
"profile_picture":"http:\/\/photos-a.ak.instagram.com\/hphotos-ak-xaf1\/10608078_1489474217961600_1032687804_a.jpg",
"id":"352581790",
"full_name":"Diego Team Studio 1"
},
"id":"794155987601013531"
}
]
},
"filter":"Normal",
"created_time":"1408850711",
"link":"http:\/\/instagram.com\/p\/sENt3lNTnc\/",
"likes":{
"count":35,
"data":[
{
"username":"fullpintbr",
"profile_picture":"http:\/\/images.ak.instagram.com\/profiles\/profile_4967034_75sq_1383496224.jpg",
"id":"4967034",
"full_name":"FullPintBR"
},
{
"username":"flaviozfagundes",
"profile_picture":"http:\/\/images.ak.instagram.com\/profiles\/profile_288787283_75sq_1358204915.jpg",
"id":"288787283",
"full_name":"Fl\u00e1vio Fagundes"
},
{
"username":"tomas_hasik",
"profile_picture":"http:\/\/photos-f.ak.instagram.com\/hphotos-ak-xfp1\/10311211_701742096555653_153939091_a.jpg",
"id":"333632542",
"full_name":"Tomas Hasik - Beer Geek"
},
{
"username":"craftshack",
"profile_picture":"http:\/\/photos-g.ak.instagram.com\/hphotos-ak-xap1\/925917_446556922153766_663491287_a.jpg",
"id":"256411806",
"full_name":"CraftShack"
}
]
},
"images":{
"low_resolution":{
"url":"http:\/\/scontent-b.cdninstagram.com\/hphotos-xfa1\/t51.2885-15\/10643909_319055608268798_1981051214_a.jpg",
"width":306,
"height":306
},
"thumbnail":{
"url":"http:\/\/scontent-b.cdninstagram.com\/hphotos-xfa1\/t51.2885-15\/10643909_319055608268798_1981051214_s.jpg",
"width":150,
"height":150
},
"standard_resolution":{
"url":"http:\/\/scontent-b.cdninstagram.com\/hphotos-xfa1\/t51.2885-15\/10643909_319055608268798_1981051214_n.jpg",
"width":640,
"height":640
}
},
"users_in_photo":[
],
"caption":{
"created_time":"1408850711",
"text":"Cervejaria argentina que, apesar de pertencer ao grupo AB-Inbev, surpreende ao produzir brands suaves mas com alt\u00edssima qualidade.\u00a0Origin\u00e1ria de Rio Negro, sul do pa\u00eds, mais precisamente na patag\u00f4nia argentina, a cervejaria vem produzindo desde 2007 e agora conta com tr\u00eas brands: weisse, amber lager e bohemian pilsner.\n\nEsta Weisse utiliza tanto o l\u00fapulo como o trigo produzido na regi\u00e3o. Uma \u00f3tima cerveja.\n\n#cervejadigital #fullpintbr #biere #beer #beerstagram #beergeek #birra #pivo #olut #instagood #brejadodia #cervejaespecial #cerveja #cerveza",
"from":{
"username":"tafinardi",
"profile_picture":"http:\/\/images.ak.instagram.com\/profiles\/profile_366708405_75sq_1398893403.jpg",
"id":"366708405",
"full_name":"Thiago Alves Finardi"
},
"id":"793819761463343856"
},
"type":"image",
"id":"793819760985192924_366708405",
"user":{
"username":"tafinardi",
"website":"",
"profile_picture":"http:\/\/images.ak.instagram.com\/profiles\/profile_366708405_75sq_1398893403.jpg",
"full_name":"Thiago Alves Finardi",
"bio":"",
"id":"366708405"
}
}
]
}
</pre>
<p>Como resposta temos um objeto que entre seus atributos, o que mais nos interessa agora é o data que é um array de objetos que dentro de cada um representa uma postagem com seus dados como tags, comentários, link, likes, e o mais importante para nós um array <strong>imagens</strong> que é um array com vários tamanhos de imagem(low_resolution, thumbnail, standard_resolution) que tem a resolução e um link para imagem. Também temos o atributo <em>user</em> com dados da pessoa que postou.</p>
<p>Então antes de fazermos o <em>parse</em> deste documento JSON devemos ler os dados da URL para um <em>String</em>, o que podemos fazer através do método abaixo.</p>
<pre>
public static String acessarURL( String link){
try {
URL url = new URL(link);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
Scanner scanner = new Scanner(is);
String conteudo = scanner.useDelimiter("\\A").next();
scanner.close();
return conteudo;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
</pre>
<p>Resumindo na linha 4 criamos um objeto <em>URL</em> com o endereço que queremos acessar, na linha 5 abrimos a conexão com este endereço e na linha 6 pegamos o <em>InputStream</em>, na próxima linha é criado um objeto <em>Scanner</em> para ler do InputStream, na linha é definido o delimitador de leitura do <em>Scanner</em> como a expressão regular “\A” para ler até o final do arquivo.</p>
<p>E só lembrando que para este método funcionar dentro de um aplicativo <em>Android</em> este deve ter permissão de acesso a rede, que deve ser colocada no arquivo <em>AndroidManifest.xml</em>.</p>
<pre>
<uses-permission android:name="android.permission.INTERNET"/>
</pre>
<p>Como já sabemos como pegar as informações do serviço vamos começar a trabalhar em nossa pequena aplicação de exemplo, começamos com o layout do <em>Activity</em>:</p>
<pre>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Buscar por tag no Instagram"
/>
<EditText
android:id="@+id/pesquisa"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
>
<requestFocus />
</EditText>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Buscar"
android:onClick="buscar"
/>
<ListView
android:id="@+id/listView"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</ListView>
</pre>
<p>Basicamente é apenas um campo de busca para digitar a tag, um botão e um <em>ListView</em>, onde será<br />
listado os resultados.</p>
<p>Neste momento começaríamos a implementar o método <em>buscar</em> no <em>Activity</em> e pensaríamos em chamar o método <em>acessarURL</em> dentro do <em>buscar</em>, mas isso não é uma boa ideia(nem possível), pois o acesso a recursos da rede é algo muito lento e como o método <em>buscar</em> está rodando na <strong>UI Thread</strong> que se demorar para responder causa o erro ANR(Application Not Responding).</p>
<p>Para realizar qualquer atividade que deve demorar, devemos realizá-la em uma outra <em>Thread</em>. Para facilitar esta atividade o Android fornece a classe <em>AsyncTask</em> que a executa em background em outra <em>Thread</em>. No nosso exemplo criaremos uma classe interna que estende o <em>AsyncTask</em> que irá acessar o rede e popular o nosso <em>ListView</em> como os dados resultantes. Veja como irá ficar.</p>
<pre>
private class InstagramTask extends AsyncTask<String, Void, List<Map<String,Object>> > {
private ProgressDialog dialog;
@Override
protected void onPreExecute() {
dialog = new ProgressDialog(MainActivity.this);
dialog.setMessage("Aguarde...");
dialog.show();
}
@Override
protected List<Map<String,Object>> doInBackground(String... param) {
String pesquisa = param[0].trim();
String codigo = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
String link = "https://api.instagram.com/v1/tags/"+pesquisa+"/media/recent?client_id="+codigo;
String conteudo = HttpUtil.acessarURL( link );
List<Map<String,Object>> fotos = new ArrayList< Map<String,Object> >();
try {
JSONObject jsonObject = new JSONObject( conteudo );
JSONArray resultados = jsonObject.getJSONArray("data");
for(int i = 0 ; i <10 ; i++){
JSONObject fotoJSON = resultados.getJSONObject(i);
Map<String,Object> mapa = new HashMap<String,Object>();
String foto = fotoJSON.getJSONObject("images").getJSONObject("standard_resolution").getString("url") ;
String url = fotoJSON.getString("link");
String username = fotoJSON.getJSONObject("user").getString("username");
String thumb = fotoJSON.getJSONObject("images").getJSONObject("thumbnail").getString("url") ;
mapa.put( "url" , url );
mapa.put( "username" , username );
mapa.put( "thumb" , loadBitmap(thumb) );
fotos.add(mapa);
}
} catch (Exception e) {
e.printStackTrace();
}
return fotos;
}
protected void onPostExecute(List<Map<String,Object>> fotos) {
String[] de = {"url" , "username" , "thumb" };
int[] para = { R.id.url , R.id.username , R.id.thumb };
MySimpleAdapter adapter = new MySimpleAdapter(MainActivity.this , fotos , R.layout.layout_foto, de , para);
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(adapter);
dialog.dismiss();
}
public Bitmap loadBitmap(String url) {
try {
URL newurl = new URL(url);
Bitmap b = BitmapFactory.decodeStream(newurl.openConnection().getInputStream());
return b;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
</pre>
<p>Vamos então entender um pouco como a classe <em>AsyncTask</em> funciona, ela é uma classe genérica que utiliza três parâmetros </p>
<pre>
private class InstagramTask extends AsyncTask<String, Void, List<Map<String,Object>> >
</pre>
<div></div>
<p><strong>1º – String</strong>: É o valor que passamos para a <em>AsyncTask</em> processar, no nosso caso é a tag que queremos pesquisar.</p>
<p><strong>2º – Void</strong>: É o tipo que representa o progresso, normalmente um <em>Integer</em> que irá representar a porcentagem de progresso. Como não iremos utilizar colocamos <em>Void</em>.</p>
<p><strong>3º – List<Map<String,Object>></strong>: É o retorno da operação que será executada em uma segunda <em>Thread</em>, no nosso caso é um <em>List</em> de <em>Map</em> que iremos utilizar para popular o <em>ListView</em>.</p>
<div></div>
<p>Quando chamamos o método <em>execute</em> da classe <em>AsyncTask</em> uma série de métodos é chamada, vamos velos.</p>
<div></div>
<p><strong>1) onPreExecute </strong> – Este método é chamado na <em>UI Thread</em> antes da tarefa ser executada, normalmente é aberto um <em>ProgressDialog</em> para dar uma mensagem para o usuário ficar esperando a tarefa ser completada.</p>
<p><strong>2) doInBackground</strong> – este método é chamado em outra <em>Thread</em>, onde o “trabalho pesado” deve ser feito. Ele recebe como parâmetro o 1º valor definido na <em>AsyncTask</em>(no nosso caso String) e retorna o 3º valor definido(no nosso caso – List<Map<String,Object>>).</p>
<p><strong>3) onProgressUpdate</strong> – este método é invocado na <em>UI Thread</em> para atualizar o progresso da operação, nós não estamos utilizando.</p>
<p><strong>4) onPostExecute</strong> – este método irá ser executado na <em>UI Thread</em> após o método <em>doInBackground</em> ser chamado e ele recebe como parâmetro o próprio retorno do método <em>doInBackground</em>.</p>
<div></div>
<p>Agora vamos analisar melhor o que fazemos em cada um dos métodos.</p>
<p>No método <em>onPreExecute()</em> criamos um objeto <em>ProgressDialog</em>, configuramos uma mensagem para ele, ao chamarmos o método <em>show()</em> será mostrada uma caixa de dialogo para o usuário esperar.</p>
<p>No método <em>doInBackground</em> começamos pegando o valor de parâmetro que irá ser passado para pesquisarmos. Na linha 15 e 16 criamos a URL de consulta concatenando a tag de pesquisa com nosso código de <em>client_id</em>. Na linha seguinte utilizamos o método <em>acessarURL</em> que vimos acima para pegar o conteúdo do endereço e nos retorna em uma String, criamos este método em uma classe utilitária. Esta String contém os dados que queremos exibir e estão no formato JSON e teremos que processá-los para criar um <em>List</em> de <em>Map</em>(que criamos na linha 21) que utilizaremos para exibir no componente <em>ListView</em>.</p>
<p>Para processar um documento JSON iremos utilizar a classe <strong>JSONObject</strong> que recebe a String que contem o documento JSON a ser convertida como parâmetro do construtor. Como vimos em nosso documento JSON de exemplo retornado por uma requisição, ele é composto de valores armazenado no formato chave/valor, sendo que o valor pode ser outros objeto JSON, ou mesmo um array de objetos JSON. O nosso objeto <em>jsonObject</em> é a raiz de nosso documento e queremos pegar o array de objetos JSON que está armazenado em sua “chave” data, para isso chamamos o método <em>getJSONArray(“data”)</em> que retorna um objeto <em>JSONArray</em> que contem todos os objetos que estão dentro da chave “data”, iremos percorrer este <em>JSONArray</em> com um laço <em>for</em> e pegar cada cada um dos objetos JSON(linha 29) que representa uma foto e pegar os valores que queremos exibir. </p>
<p>Na linha 31 criamos um objeto Map para armazenar as informações.</p>
<p>Na linha 33 fazemos chamadas encadeadas de método, primeiro chamamos <em>getJSONObject(“images”)</em> que irá retornar o objeto JSON armazenado com a chave “imagens”, deste objeto chamamos o <em>getJSONObject(“standard_resolution”)</em> que irá retornar outro objeto JSON e deste chamamos <em>getString(“url”)</em> que irá devolver o valor String armazenado com a chave “url”. Como vimos temos vários objetos dentro de objetos, consulte o documento de exemplo para melhor visualizar.</p>
<p>Nas linhas seguintes pegamos as demais informações que queremos exibir e nas linhas 39, 40 e 41 colocamos estes valores dentro da estrutura <em>Map</em>. Você deve ter notado que na linha 41 não armazenamos a URL da miniatura da foto, pois queremos exibi-la em um <em>ImagemView</em> que não é capaz de exibir a imagem através de uma URL então temos que baixar a imagem e criar um objeto <em>Bitmap</em>, fazemos isso através do método <em>loadBitmap</em>(linha 66) que recebe um String de URL que utiliza para criar um objeto <em>URL</em> do qual abre uma conexão e pega o seu <em>InputStream</em> utilizando ele para passar para o <em>BitmapFactory</em> que irá lê-lo para criar um objeto <em>Bitmap</em>.</p>
<p>No método <em>onPostExecute(List> fotos)</em> iremos popular nosso <em>ListView</em>, sendo que cada um dos <em>Map</em> inseridos dentro do <em>List</em> irão virar um item do nosso <em>ListView</em> seguindo o layout do XML abaixo que criamos em <strong>res/layout/layout_foto.xml</strong></p>
<pre>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/thumb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center"
/>
<TextView
android:id="@+id/url"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_margin="10dp"/>
<TextView
android:id="@+id/username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_margin="10dp"
/>
</LinearLayout>
</pre>
<p>Para popular o <em>ListView</em> não podemos utilizar um <em>SimpleAdapter</em> pois como vimos no layout acima temos um <em>ImagemView</em> ao qual devemos atribuir o <em>Bitmap</em> que colocamos dentro do nosso <em>Map</em>, para podermos fazer isso devemos criar nosso próprio <em>Adapter</em> e sobrescrever o método <em> getView(int position, View convertView, ViewGroup parent)</em> que é chamado para cada um dos <em>Map</em> do nosso <em>List</em> como vemos no código abaixo.</p>
<pre>
public class MySimpleAdapter extends SimpleAdapter {
private Context mContext;
public LayoutInflater inflater = null;
private int resource;
public MySimpleAdapter(Context context , List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
mContext = context;
this.resource = resource;
inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
if (convertView == null)
vi = inflater.inflate(resource, null);
HashMap<String, Object> data = (HashMap<String, Object>) getItem(position);
ImageView image = (ImageView) vi.findViewById(R.id.thumb);
image.setImageBitmap( (Bitmap) data.get("thumb") );
TextView tvURL = (TextView) vi.findViewById(R.id.url);
tvURL.setText( data.get("url").toString() );
TextView tvUsername = (TextView) vi.findViewById(R.id.username);
tvUsername.setText( data.get("username").toString() );
return vi;
}
}
</pre>
<p>No construtor do <em>MySimpleAdapter</em> é chamado o construtor da classe pai, guardamos os valores de de <em>context</em> e <em>resource</em> que é o identificador do layout dos itens. Também criamos um <em>LayoutInflater</em> para transformar o <em>resource</em> em um objeto <em>View</em>.</p>
<p>Dentro do método <em>getView</em> é carregado o layout, pegamos o <em>Map</em>(linha 19) com as informações que iremos exibir. Na linha 21 chamamos o método <em>findViewById(E.id.thumb)</em> para pegar o <em>ImageView</em> do view de layout, na linha abaixo adicionamos o bitmap do <em>Map</em> através do método <em>setImageBitmap</em>. Seguimos colocando as informações do <em>Map</em> nos outros views do layout.</p>
<p>Visto por cima o <em>MySimpleAdapter</em> e o layout voltamos aos <em>onPostExecute</em> onde temos dois arrays um de String que representa as chaves do <em>Map</em> e um de <em>int</em> que representa os componente do layout na ordem relativa as chaves do <em>Map</em>. Criamos o Adapter e adicionamo ao <em>ListView</em>, isso irá exibir os dados na tela e podemos esconder o dialog através do <em> dialog.dismiss();</em>.</p>
<p>Agora podemos voltar ao método <em>buscar</em> do nosso <em>Activity</em> que irá pegar o valor da pesquisa e passar para o <em>AsyncTask</em> para processar.</p>
<pre>
public void buscar(View v){
EditText edT = (EditText) findViewById(R.id.pesquisa);
String pesquisa = edT.getText().toString();
InstagramTask task = new InstagramTask();
task.execute(pesquisa);
}
</pre>
<p>Era isso, espero não ter ficado muito confuso, para ficar mais claro você <a href="http://www.botecodigital.info/exemplos/instagram/instagram.zip">conferir o exemplo completo</a>.</p>
Acessando a API(webservice) do Instagram pelo Android
7 de Setembro de 2014, 12:51 - sem comentários ainda | Ninguém está seguindo este artigo ainda.
Visualizado 206 vezes
0sem comentários ainda