Ir para o conteúdo
ou

Software livre Brasil

 Voltar a Blog de Thia...
Tela cheia

Acessando a API(webservice) do Instagram pelo Android

7 de Setembro de 2014, 12:51 , por Software Livre Brasil - 0sem comentários ainda | Ninguém está seguindo este artigo ainda.
Visualizado 206 vezes
<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 &#8220;\A&#8221; 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> &lt;uses-permission android:name="android.permission.INTERNET"/&gt; </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> &lt;TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Buscar por tag no Instagram" /&gt; &lt;EditText android:id="@+id/pesquisa" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" &gt; &lt;requestFocus /&gt; &lt;/EditText&gt; &lt;Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Buscar" android:onClick="buscar" /&gt; &lt;ListView android:id="@+id/listView" android:layout_width="fill_parent" android:layout_height="fill_parent"&gt; &lt;/ListView&gt; </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&lt;String, Void, List&lt;Map&lt;String,Object&gt;&gt; &gt; { private ProgressDialog dialog; @Override protected void onPreExecute() { dialog = new ProgressDialog(MainActivity.this); dialog.setMessage("Aguarde..."); dialog.show(); } @Override protected List&lt;Map&lt;String,Object&gt;&gt; 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&lt;Map&lt;String,Object&gt;&gt; fotos = new ArrayList&lt; Map&lt;String,Object&gt; &gt;(); try { JSONObject jsonObject = new JSONObject( conteudo ); JSONArray resultados = jsonObject.getJSONArray("data"); for(int i = 0 ; i &lt;10 ; i++){ JSONObject fotoJSON = resultados.getJSONObject(i); Map&lt;String,Object&gt; mapa = new HashMap&lt;String,Object&gt;(); 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&lt;Map&lt;String,Object&gt;&gt; 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&lt;String, Void, List&lt;Map&lt;String,Object&gt;&gt; &gt; </pre> <div></div> <p><strong>1º &#8211; String</strong>: É o valor que passamos para a <em>AsyncTask</em> processar, no nosso caso é a tag que queremos pesquisar.</p> <p><strong>2º &#8211; 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º &#8211; List&lt;Map&lt;String,Object&gt;&gt;</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> &#8211; 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> &#8211; este método é chamado em outra <em>Thread</em>, onde o &#8220;trabalho pesado&#8221; 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 &#8211; List&lt;Map&lt;String,Object&gt;&gt;).</p> <p><strong>3) onProgressUpdate</strong> &#8211; 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> &#8211; 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 &#8220;chave&#8221; data, para isso chamamos o método <em>getJSONArray(&#8220;data&#8221;)</em> que retorna um objeto <em>JSONArray</em> que contem todos os objetos que estão dentro da chave &#8220;data&#8221;, 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(&#8220;images&#8221;)</em> que irá retornar o objeto JSON armazenado com a chave &#8220;imagens&#8221;, deste objeto chamamos o <em>getJSONObject(&#8220;standard_resolution&#8221;)</em> que irá retornar outro objeto JSON e deste chamamos <em>getString(&#8220;url&#8221;)</em> que irá devolver o valor String armazenado com a chave &#8220;url&#8221;. 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> &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" &gt; &lt;ImageView android:id="@+id/thumb" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="center" /&gt; &lt;TextView android:id="@+id/url" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:layout_margin="10dp"/&gt; &lt;TextView android:id="@+id/username" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:layout_margin="10dp" /&gt; &lt;/LinearLayout&gt; </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&lt;? extends Map&lt;String, ?&gt;&gt; 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&lt;String, Object&gt; data = (HashMap&lt;String, Object&gt;) 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>
Fonte: http://www.botecodigital.info/android/acessando-a-apiwebservice-do-instagram-pelo-android/

0sem comentários ainda

Enviar um comentário

Os campos são obrigatórios.

Se você é um usuário registrado, pode se identificar e ser reconhecido automaticamente.