O AndroidPIT utiliza cookies para garantir que você tenha a melhor experiência no nosso site. OK
10 min para ler 28 Compartilhado 13 Comentários

Desenvolvimento Android: como criar seu próprio RSS reader

Caros aspirantes a programadores Android, este é o meu segundo artigo da série de desenvolvimento para Android. O primeiro artigo serviu de introdução ao ambiente de programação para Android, e hoje vou mostrar como criar o seu próprio RSS feed. Ao longo do tutorial, outros conceitos e princípios básicos também serão explicados.

LV1A3278
 © Coursera

Se você ainda tem o projeto de aplicativo “Hello AndroidPIT” do primeiro artigo, poderá agora continuar a trabalhar com ele. Do contrário, você deverá iniciar um novo projeto no Android Studio.

Rich Site Summary (RSS)

Em primeiro lugar, o que é um feed RSS? Muitos de vocês lêem feeds RSS diariamente num aplicativo reader do seu computador ou dispositivo móvel. Para programar um app que possa mostrar um feed RSS, você deve saber um pouco a respeito do RSS em si. A Wikipedia fornece uma boa visão geral a respeito do assunto.

Por exemplo, o feed RSS do AndroidPIT pode ser encontrado aqui.

Em resumo, RSS é um arquivo XML. O XML é feito principalmente para mostrar texto, e é projetado para machine readability. Nosso primeiro objetivo é desenvolver um aplicativo que use o arquivo XML dos feeds do AndroidPIT e mostrá-lo adequadamente em nosso aplicativo “Hello AndroidPIT”.

Representando o Feed RSS

Em seguida, irei mostrar como trazer o arquivo XML para a tela do seu dispositivo. Pegaremos o app do primeiro artigo e iremos modificá-lo para os nossos fins.

No Android, o layout é geralmente definido via XML. Portanto, se planeja se dedicar ao desenvolvimento, vale a pena aprender mais sobre XML, pois é um formato que você usará continuamente.

O layout é armazenado na pasta res/layout. Se abrir o fragment_main.xml em seu projeto, poderá ver o layout XML e uma prévia do mesmo.

pi
No projeto do primeiro artigo.  / © AndroidPIT

Queremos substituir  “Hello AndroidPIT!” pelo nosso Feed RSS. Para fazê-lo, temos de dar ao TextView um identificador sobre o que desejamos mostrar. Neste caso, o atributo android:id irá realizar a tarefa:

<TextView
       android:id="@+id/rss_feed"
       android:text="@string/hello_world"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content" />

Depois de dar uma id ao Textview, temos de achar aqueles que queremos usar e adicioná-los ao nosso segmento Placeholder fragment. Para isso, usamos o método findViewById. Ele deve parecer assim dentro do seu método onCreateView:

@Override
public View onCreateView(
    LayoutInflater inflater, 
    ViewGroup container, 
    Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.fragment_main, container, false);
    mRssFeed = (TextView) rootView.findViewById(R.id.rss_feed);
    return rootView;
}

Para recuperar o feed RSS no início do aplicativo, precisamos do seguinte método onStart:

       @Override
       public void onStart() {
           super.onStart();
           InputStream in = null;
           try {
               URL url = new URL("https://www.androidpit.com/feed/main.xml");
               HttpURLConnection conn = (HttpURLConnection) url.openConnection();
               in = conn.getInputStream();
               ByteArrayOutputStream out = new ByteArrayOutputStream();
               byte[] buffer = new byte[1024];
               for (int count; (count = in.read(buffer)) != -1; ) {
                   out.write(buffer, 0, count);
               }
               byte[] response = out.toByteArray();
               String rssFeed = new String(response, "UTF-8");
               mRssFeed.setText(rssFeed);
           } catch (IOException e) {
               e.printStackTrace();
           } finally {
               if (in != null) {
                   try {
                       in.close();
                   } catch (IOException e) {
                       e.printStackTrace();
                   }
               }
           }
       }

Se você não entende o código-fonte, não se preocupe e apenas copie e cole o que fizemos. Para entendê-lo, você precisará aprender Java e a linguagem de programação.

Se tentar rodar o app agora, ele começara a compilar mas irá congelar em algum ponto.

Ao desenvolver apps, é preciso aprender como lidar com panes e o que elas significam. Se abrir o logcat ao mesmo tempo em que rodar seu aplicativo, ele irá registrar todas as mensagens de erro emitidas pelo app. No nosso caso, aparecerá o seguinte:

com.rockylabs.androidpitrss E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.rockylabs.androidpitrss, PID: 14367
    java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.rockylabs.androidpitrss/com.rockylabs.androidpitrss.MainActivity}:
android.os.NetworkOnMainThreadException

O que aconteceu, afinal? O seu aplicativo tentou acessar a internet no thread principal, mas se o pedido demorar muito, o app para de responder e uma mensagem de erro é emitida. Para evitar isso, o Android foi programado para congelar em vez de rodar infinitamente.

No Android, precisamos trabalhar num mundo de fluxos de trabalho assíncronos. Isso significa que quando um pedido é feito ao sistema, ele rodará em segundo plano e uma notificação aparecerá para você tão logo a tarefa esteja completa.

O padrão de todas as tarefas é o user interface thread, chamado UI thread. No caso acima, se tentar acessar a internet e o pedido do servidor demorar muito, a interface fica parada e não prossegue. Como resultado, você recebe uma mensagem de erro de que a aplicação não está reagindo. Esse aviso também é conhecido como Application Not Responding (ANR). Esse tipo de mensagem deve ser evitada de todas as formas, para manter o aplicativo funcional para o usuário.

anr dialog
© Creative Commons Attribution 2.5

O Android fornece uma maneira bem simples de lidar com esse problema na forma de uma classe diferente chamada AsyncTask. Você pode rodar a tarefa em segundo plano num thread de fundo e rodar o resultado no thread da UI.

Em nosso app, queremos rodar uma tarefa com AsyncTask em segundo plano. No passo anterior, temos o código-fonte para o método onStart inserido em nosso código. Agora precisamos separar a rede e a interface de usuário, e é por isso que criamos o método getAndroidPitRssFeed. Isso torna mais fácil usar o código-fonte várias vezes em vez de gastar muito tempo copiando e colando. O método getAndroidPitRssFeed é o seguinte:

       private class GetAndroidPitRssFeedTask extends AsyncTask<Void, Void, String> {

           @Override
           protected String doInBackground(Void... voids) {
               String result = "";
               try {
                   result = getAndroidPitRssFeed();
               } catch (IOException e) {
                   e.printStackTrace();
               }
               return result;
           }

           @Override
           protected void onPostExecute(String rssFeed) {
               mRssFeed.setText(rssFeed);
           }
       }

Se rodarmos de novo o app, iremos produzir novamente uma parada, mas desta vez será por falta de autorização em vez de tempo esgotado. Não vimos nenhuma permissão para acessar a internet e assim não podemos obter o feed RSS.

Se tiver aberto o logcat com o aplicativo rodando, verá a seguinte mensagem de erro:

Caused by: java.lang.SecurityException: Permission denied (missing INTERNET permission?)

Para configurar as permissões de internet, você vai precisar abrir o arquivo AndroidManifest.xml. Lá você verá as seguintes linhas de código:

<uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="19" />

 
Iremos adicionar o seguinte abaixo delas:
 

<uses-permission android:name="android.permission.INTERNET" />

 
E pronto! Se rodar o aplicativo, verá que ele começará a mostrar o XML do seu feed RSS. Como deve ter notado, vai levar algum tempo para carregar. Todo o feed será carregado. Mais tarde, iremos mostrar como melhorar a experiência de usuário instalando um indicador de progresso.
 
Até agora, tudo bem. Você conseguiu criar seu próprio feed RSS. Contudo, ninguém quer visualizar o formato XML, que é um tanto cru, então teremos de limpá-lo. Além disso, não podemos passar da borda da tela, e não conseguimos rolar para baixo neste ponto.


ScrollView

A solução para o problema acima é adicionar o componente Scrollview já disponível no SDK Android:

<ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/rss_feed"
            android:text="@string/hello_world"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </ScrollView>

No próximo passo, você irá aprender como o arquivo XML é lido, para podermos modificá-lo um pouco e extrair os títulos de artigos e apresentá-los numa lista, para que a visualização fique mais clara.

android entwickler studio teil zwei rss 01
Os frutos do nosso trabalho: XML mostrado, mas sem possibilidade de rolagem. / © AndroidPIT

Leitura de XML

Nosso próximo objetivo é fazer com que o título de cada artigo seja mostrado dentros das tags <title>. Se olharmos para o feed RSS (imagem acima), ele se encontra dentro da porção <rss> do código e logo após a tag <channel>. Para ficar mais simples, iremos ignorar todas as outras tags por ora. Após termos trabalhado por todo o arquivo XML, entenderemos melhor para que elas são usadas.

Para lidar com um arquivo XML, precisamos procurar entre o emaranhado de coisas e tirar a informação que precisamos dele. Felizmente para nós, alguém já criou uma ferramenta para isso. Como tal, vamos usar um analisador para extrair as informações de que precisamos e vamos usar XmlPullParser do SDK Android.

A análise do arquivo XML seguirá os seguintes estágios:

START_DOCUMENT é para começar o documento, quando o analisador ainda não leu nada.

START_TAG é o que inicia o analisador

TEXT é quando o analisador está ocupado com conteúdo

ENG_TAG é quando o analizador está na tag final

END_DOCUMENT é quando o documento está terminado e não há mais opções de análise disponíveis.

Primeiro, precisamos do analisador para criar:

         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
         XmlPullParser xpp = factory.newPullParser();

Em seguida temos de dar ao analisador uma fonte para analisar. Vamos atribuí-lo ao string que baixamos do servidor e está sendo mostrado via TextView.

Como mencionamos anteriormente, queremos ler o feed RSS, canal, item aberto, e as tags de título para ler o título do artigo. Vamos ignorar todo o resto. Então adicionamos readRSS, read channel, ReadItem, e read title. Para isso emitimos o seguinte código:

private List<String> readRss(XmlPullParser parser) 
    throws XmlPullParserException, IOException {
                List<String> items = new ArrayList<>();
                parser.require(XmlPullParser.START_TAG, null, "rss");
                while (parser.next() != XmlPullParser.END_TAG) {
                    if (parser.getEventType() != XmlPullParser.START_TAG) {
                        continue;
                    }
                    String name = parser.getName();
                    if (name.equals("channel")) {
                        items.addAll(readChannel(parser));
                    } else {
                        skip(parser);
                    }
                }
                return items;
            }

Agora precisamos implementar o canal de leitura para ler os conteúdos, o resto deixamos de lado:

private List<String> readChannel(XmlPullParser parser) 
    throws IOException, XmlPullParserException {
                List<String> items = new ArrayList<>();
                parser.require(XmlPullParser.START_TAG, null, "channel");
                while (parser.next() != XmlPullParser.END_TAG) {
                    if (parser.getEventType() != XmlPullParser.START_TAG) {
                        continue;
                    }
                    String name = parser.getName();
                    if (name.equals("item")) {
                        items.add(readItem(parser));
                    } else {
                        skip(parser);
                    }
                }
                return items;
            }

ReadItem também é necessário:

private String readItem(XmlPullParser parser) 
    throws XmlPullParserException, IOException {
                String result = null;
                parser.require(XmlPullParser.START_TAG, null, "item");
                while (parser.next() != XmlPullParser.END_TAG) {
                    if (parser.getEventType() != XmlPullParser.START_TAG) {
                        continue;
                    }
                    String name = parser.getName();
                    if (name.equals("title")) {
                        result = readTitle(parser);
                    } else {
                        skip(parser);
                    }
                }
                return result;
            }

Estamos quase lá, só precisamos obter read title para puxar o título:

            // Processes title tags in the feed.
            private String readTitle(XmlPullParser parser)
                    throws IOException, XmlPullParserException {
                parser.require(XmlPullParser.START_TAG, null, "title");
                String title = readText(parser);
                parser.require(XmlPullParser.END_TAG, null, "title");
                return title;
            }

E o texto no dia <title> :

           private String readText(XmlPullParser parser)
                    throws IOException, XmlPullParserException {
                String result = "";
                if (parser.next() == XmlPullParser.TEXT) {
                    result = parser.getText();
                    parser.nextTag();
                }
                return result;
            }

Com este método, o resto das tags será pulado:

private void skip(XmlPullParser parser) 
    throws XmlPullParserException, IOException {
                if (parser.getEventType() != XmlPullParser.START_TAG) {
                    throw new IllegalStateException();
                }
                int depth = 1;
                while (depth != 0) {
                    switch (parser.next()) {
                        case XmlPullParser.END_TAG:
                            depth–;
                            break;
                        case XmlPullParser.START_TAG:
                            depth++;
                            break;
                    }
                }
            }

Agora somos quase profissionais no que diz respeito ao analisador XML! No próximo passo vamos emitir o título do item numa lista.

List Fragment

No SDK Android existe uma classe chamada ListFragment que irá nos ajudar a lidar com listas.

Uma vez que já temos muitas linhas de texto, extraídas do arquivo XML, podemos usar uma função-padrão prática do Android para representar o título. Um adaptador é um tipo de ponte entre os dados que queremos apresentar e os TextViews que representam os dados.

Com um adaptador, podemos usar um método TextView para cada título da nossa lista. Você precisa usar o Placeholder fragment para conseguir isso:

   public static class PlaceholderFragment extends ListFragment {

Por fim, ainda precisamos usar o AsyncTask para que o nosso analisador possa funcionar:

            @Override
            protected List<String> doInBackground(Void... voids) {
                List<String> result = null;
                try {
                    String feed = getAndroidPitRssFeed();
                    result = parse(feed);
                } catch (XmlPullParserException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return result;
            }

            @Override
            protected void onPostExecute(List<String> rssFeed) {
                    setListAdapter(new ArrayAdapter<>(
                            getActivity(),
                            android.R.layout.simple_list_item_1,
                            android.R.id.text1,
                            rssFeed));
            }

Se tentar rodar o aplicativo agora, primeiro você deve ver um indicador de prograsso enquanto o feed RSS é baixado do servidor e, em seguida, uma lista de todos os títulos do feed serão mostrados, como abaixo:

android entwickler studio teil zwei rss 04
Aqui está! Um indicador de progresso e o título do arquivo XML. / © AndroidPIT

Conclusão

UFA! Sabemos que este guia é um grande passo se comparado ao primeiro exercício. Mas se você conseguiu passar por todas as fases, deve orgulhar-se do que fez e do que aprendeu. Mesmo se não tiver entendido todos os detalhes, seu entendimento sobre como programar dentro do Android irá crescer com o tempo. Quanto mais praticar o desenvolvimento Android, mais irá entender sobre esse tipo de coisa.

O feed que você programou funciona, mas não é muito útil, pois no momento mostra apenas os títulos dos artigos. Em nosso próximo exercício, iremos extender a funcionalidade e fazer com que possamos ler a introdução dos artigos quando clicarmos no título.

O que achou? Que melhorias gostaria de ver no tutorial do RSS?

28 Compartilhado

13 Comentários

Escreva um comentário:

O AndroidPIT utiliza cookies para garantir que você tenha a melhor experiência no nosso site. Mais informações

Entendi