Простой RSS-клиент на Android

    Пошаговое руководство по созданию RSS-клиента на Android. Статья для начинающих разработчиков.
    Android – операционная система для мобильных устройств от Google.
    Система очень перспективная и динамично развивается. Базовые навыки разработки будут совсем не лишними.
    Осторожно много скриншотов!

    1. Начало

    Вначале нужно зайти на официальный сайт и ознакомиться с возможностями последней версии Android 2.2. На сайте есть специальный раздел для разработчиков. Тут можно скачать все необходимые инструменты, найти инструкцию по установке, инструкцию по созданию первого приложения и справку по пакетам и классам библиотеки.

    2. Установка SDK

    На компьютере должен быть установлен JDK версии 5 или 6. Для разработки под Android нужно скачать и установить Android SDK, в котором есть эмулятор Android. Далее нужно установить Eclipse версии 3.4 или 3.5. И последнее, нужно установить плагин ADT для Eclipse.

    3. Создание RSS-клиента

    За основу я взял статью Creating Rss Reader in Android. Но в статье есть ошибки, и получить рабочей приложение сразу не получится. По моей просьбе, автор выложил полный рабочий проект. Но пока шла переписка, я сам исправил ошибки и написал свою версию программы.

    3.1. Создание проекта
    Запускаем Eclipse, выбираем меню File->New->Project. Запускается диалог New Project, выбираем папку Android и проект Android Project, жмем Next.

    image

    Открывается окно New Android Project. Заполняем поле Project name: uRSS, Выбираем в таблице Build Target строку Android 2.2. Дальше нужно заполнить свойства проекта в разделе Properties.

    Application name: uRSS.
    Package name: rembo.network.urss.
    RSSactivity.
    Min SDK Version: 8.

    Жмем Next и в следующем окне ничего не меняем и жмем Finish.

    image

    image

    Все проект создан.

    3.2. Написание кода
    Eclipse создаст много папок и файлов проекта (в них легко запутаться).
    Главные файлы такие:
    src/rembo.network.rss/RSSactivity.java – файл с классом RSSactivity, который задает логику главного окна (тут напишем обработчики событий);
    res/layout/main.xml – XML описание главного окна (тут создадим элементы управления);
    AndroidManifest.xml – файл описания свойств приложения (тут свяжем XML описание с кодом и зададим разрешение на работу в сети).

    Для начала зададим содержимое главного окна в файле res/layout/main.xml. Главное окно будет построено на основе контейнера LinearLayout. Оно содержит надпись TextView, поле для ввода текста EditText, кнопку Button и список ListView. Обратите внимание, что у каждого элемента есть android:id. Это идентификатор по которому с элементом интерфейса можно связаться из кода.

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
      
      <TextView
      android:id="@+id/label"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="Enter RSS URL:"
      />
      
      <EditText
      android:id="@+id/rssURL"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:background="@android:drawable/editbox_background"
      android:text="http://feeds.feedburner.com/MicrosoftUserGroupVinnitsya"
      />
      
      <Button
      android:id="@+id/fetchRss"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginRight="10dip"
      android:text="Fetch Rss" />

      <ListView
      android:id="@+id/rssListView"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content" />
    </LinearLayout>

    * This source code was highlighted with Source Code Highlighter.


    Для элемента списка нужно задать содержимое. Для этого создайте файл res/layout/list_item.xml.

    <?xml version="1.0" encoding="utf-8"?>
    <TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dp"
    android:textSize="13sp"
    android:autoLink="all" >
    </TextView>

    * This source code was highlighted with Source Code Highlighter.


    При нажатии на элемент списка, новость должна открыться в новом окне. Создадим файл res/layout/rss_item_displayer.xml для этого окна.

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
     >
     
     <TextView
      android:id="@+id/titleTextView"
       android:text="Title:"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:padding="10dp"
      android:textSize="13sp"
      android:autoLink="all"
      />
     
     <TextView
      android:id="@+id/contentTextView"
      android:text="Content:"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:textSize="13sp"
      android:autoLink="all"
      />
      
    </LinearLayout>

    * This source code was highlighted with Source Code Highlighter.


    Итак, интерфейс создан. Теперь нужно писать код. Создаем новый класс RssItem в файле src/rembo.network.rss/RssItem.java. Класс получает RSS содержимое из Интернета.

    package rembo.network.urss;
    import java.util.*;
    import java.text.*;
    import java.net.*;
    import java.io.*;
    import javax.xml.parsers.*;
    import org.w3c.dom.*;

    public class RssItem {

      private String title;
      private String description;
      private Date pubDate;
      private String link;

      public RssItem(String title, String description, Date pubDate, String link) {
        this.title = title;
        this.description = description;
        this.pubDate = pubDate;
        this.link = link;
      }
      
      public String getTitle()
      {
        return this.title;
      }
      
      public String getLink()
      {
        return this.link;
      }
      
      public String getDescription()
      {
        return this.description;
      }
      
      public Date getPubDate()
      {
        return this.pubDate;
      }
      
      @Override
      public String toString() {

        SimpleDateFormat sdf = new SimpleDateFormat("MM/dd - hh:mm:ss");

        String result = getTitle() + "  ( " + sdf.format(this.getPubDate()) + " )";
        return result;
      }

      public static ArrayList<RssItem> getRssItems(String feedUrl) {

        ArrayList<RssItem> rssItems = new ArrayList<RssItem>();
        
        RssItem rssItemT = new RssItem("MSUG news", "Best IT news.",
            new Date(), "http://msug.vn.ua/");

        rssItems.add(rssItemT);

        try {
          //open an URL connection make GET to the server and
          //take xml RSS data
          URL url = new URL(feedUrl);
          HttpURLConnection conn = (HttpURLConnection) url.openConnection();

          if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
            InputStream is = conn.getInputStream();

            //DocumentBuilderFactory, DocumentBuilder are used for
            //xml parsing
            DocumentBuilderFactory dbf = DocumentBuilderFactory
                .newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();

            //using db (Document Builder) parse xml data and assign
            //it to Element
            Document document = db.parse(is);
            Element element = document.getDocumentElement();

            //take rss nodes to NodeList
            NodeList nodeList = element.getElementsByTagName("item");

            if (nodeList.getLength() > 0) {
              for (int i = 0; i < nodeList.getLength(); i++) {

                //take each entry (corresponds to <item></item> tags in
                //xml data

                Element entry = (Element) nodeList.item(i);

                Element _titleE = (Element) entry.getElementsByTagName(
                    "title").item(0);
                Element _descriptionE = (Element) entry
                    .getElementsByTagName("description").item(0);
                Element _pubDateE = (Element) entry
                    .getElementsByTagName("pubDate").item(0);
                Element _linkE = (Element) entry.getElementsByTagName(
                    "link").item(0);

                String _title = _titleE.getFirstChild().getNodeValue();
                String _description = _descriptionE.getFirstChild().getNodeValue();
                Date _pubDate = new Date(_pubDateE.getFirstChild().getNodeValue());
                String _link = _linkE.getFirstChild().getNodeValue();

                //create RssItemObject and add it to the ArrayList
                RssItem rssItem = new RssItem(_title, _description,
                    _pubDate, _link);

                rssItems.add(rssItem);
              }
            }

          }
        } catch (Exception e) {
          e.printStackTrace();
        }

        return rssItems;
      }

    }

    * This source code was highlighted with Source Code Highlighter.


    Теперь создадим класс RssItemDisplayer в файле src/rembo.network.rss/RssItemDisplayer.java. Он отвечает за логику окна, которое показывает полный текст новости. Обратите внимание как по id элемента управления его получают в коде TextView titleTv = (TextView)findViewById(R.id.titleTextView);

    package rembo.network.urss;
    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.*;
    import java.util.*;
    import java.text.*;
    import java.net.*;
    import java.io.*;
    import javax.xml.parsers.*;
    import org.w3c.dom.*;
    import android.view.*;
    public class RssItemDisplayer extends Activity {
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.rss_item_displayer);

        RssItem selectedRssItem = RSSactivity.selectedRssItem;
        //Bundle extras = getIntent().getExtras();
        TextView titleTv = (TextView)findViewById(R.id.titleTextView);
        TextView contentTv = (TextView)findViewById(R.id.contentTextView);

        String title = "";
        SimpleDateFormat sdf = new SimpleDateFormat("MM/dd - hh:mm:ss");
        title = "\n" + selectedRssItem.getTitle() + "  ( "
            + sdf.format(selectedRssItem.getPubDate()) + " )\n\n";

        String content = "";
        content += selectedRssItem.getDescription() + "\n"
            + selectedRssItem.getLink();

        titleTv.setText(title);
        contentTv.setText(content);
      }
    }

    * This source code was highlighted with Source Code Highlighter.


    Теперь пишем код класса RSSactivity в файле src/rembo.network.rss/ RSSactivity.java.java. Он отвечает за логику главного окна. Обратите внимание как создаются обработчики событий для клика по кнопке и по элементу списка fetchRss.setOnClickListener(new View.OnClickListener()…

    package rembo.network.urss;

    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.*;
    import java.util.*;
    import java.text.*;
    import java.net.*;
    import java.io.*;
    import javax.xml.parsers.*;
    import org.w3c.dom.*;
    import android.view.*;
    import android.view.View;
    import android.widget.*;
    import android.content.*;

    public class RSSactivity extends Activity {
      
      public static RssItem selectedRssItem = null;
      String feedUrl = "";
      ListView rssListView = null;
      ArrayList<RssItem> rssItems = new ArrayList<RssItem>();
      ArrayAdapter<RssItem> aa = null;

      
      /** Called when the activity is first created. */
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // get textview from our layout.xml
        final TextView rssURLTV = (TextView) findViewById(R.id.rssURL);

        // get button from layout.xml
        Button fetchRss = (Button) findViewById(R.id.fetchRss);

        // define the action that will be executed when the button is clicked.
        fetchRss.setOnClickListener(new View.OnClickListener() {

          //@Override
          public void onClick(View v) {
            feedUrl = rssURLTV.getText().toString();
            //TextView TVtitle=(TextView)findViewById(R.id.label);
            //CharSequence cs="fetching";
            //TVtitle.setText(cs);
            aa.notifyDataSetChanged();
            refressRssList();
            //cs="Feed:";
            //TVtitle.setText(cs);
          }
        });

        // get the listview from layout.xml
        rssListView = (ListView) findViewById(R.id.rssListView);
        // here we specify what to execute when individual list items clicked
        rssListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

          //@Override
          public void onItemClick(AdapterView<?> av, View view, int index,
              long arg3) {
            selectedRssItem = rssItems.get(index);

            // we call the other activity that shows a single rss item in
            // one page
            Intent intent = new Intent(
                "rembo.network.urss.displayRssItem");
            startActivity(intent);
          }
        });

            //adapters are used to populate list. they take a collection,
            //a view (in our example R.layout.list_item
        aa = new ArrayAdapter<RssItem>(this, R.layout.list_item, rssItems);
            //here we bind array adapter to the list
        rssListView.setAdapter(aa);
        feedUrl = rssURLTV.getText().toString();
        refressRssList();
      }

      private void refressRssList() {

        ArrayList<RssItem> newItems = RssItem.getRssItems(feedUrl);

        rssItems.clear();
        rssItems.addAll(newItems);

        //TextView TVtitle=(TextView)findViewById(R.id.label);
        //CharSequence cs="0";
        //if(newItems.size()>0) cs="is 1";
        //if(newItems.size()>5) cs="is 5";
        ///TVtitle.setText(cs);
        
        aa.notifyDataSetChanged();
      }

    }

    * This source code was highlighted with Source Code Highlighter.


    Остался последний штрих. Нужно связать интерфейс и код в файле AndroidManifest.xml и разрешить приложению выход в Интернет.

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="rembo.network.urss"
       android:versionCode="1"
       android:versionName="1.0">
      <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".RSSactivity"
             android:label="@string/app_name">
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
        <activity android:name=".RssItemDisplayer" android:label="Display Rss Item">
          <intent-filter>
            <action android:name="rembo.network.urss.displayRssItem" />
            <category android:name="android.intent.category.DEFAULT"></category>
          </intent-filter>
        </activity>
        

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

    </manifest>

    * This source code was highlighted with Source Code Highlighter.


    3.3. Запуск
    Пора запускать наше приложение. Но вначале нужно создать виртуальную машину. Для этого в Eclipse переходим в меню Window->Android SDK and AVD Manager.

    image

    Жмем New и выбираем свойства виртуальной машины. В разделе Hardware обязательно нужно добавить GSM modem, поддержку Touchscreeen, Device ram size и Max VM application heap size (при такой конфигурации устройство удачно подключается к Интернету). Жмем Create AVD (тут придется немного подождать).

    image

    Жмем кнопку старт и запускаем виртуальную машину. Тут нужно запастись терпением. Ждать придется минут 10-20, пока машина не загрузится. Убедитесь, что виртуальное устройство подключено к Интернету. Если нет, то попробуйте перегрузиться или создать новую машину.

    image

    image

    image

    image

    После запуска виртуальной машины переходим в Eclipse и запускаем приложение (жмем Run). Снова придется подождать минут 5, пока приложение установится на виртуальную машину и запустится. Если приложение не запускается, то попробуйте выключить виртуальную машину и запустить приложение (тут уже придется долго ждать, пока виртуальная машина стартует снова).

    image

    image
    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 46
    • +4
      Классно, а главное — понятно. Будем учится писать программы под Android.
      • +3
        Android привлекает тем, что несложно начать.
        • –8
          OpenSource же :) Открытый код — бери, кодь :)
          • +6
            ReactOS тоже OpenSource
            бери, кодь :)
          • +1
            >несложно начать
            открою вам тайну — это заслуга ЯП (java), а не системы.
            • +4
              Далеко не только в джаве дело, ещё и SDK отличный!
              • +4
                почитайте android fundamentals — откроете еще пару тайн.

                там не просто жава, а фреймворк на жаве.
              • –1
                начать не сложно, сложно закончить
                • 0
                  Закончить тоже не сложно. Достаточно попасть под бан гугла.
            • +2
              Отличная статья! Давно хотел начать писать под андройдом, но не знал с чего начать!
              • +2
                Тут главное терпение. Виртуальная машина действительно очень медленная. Но все работает даже на EEE PC 900.
                • –1
                  Это зависит от железа компа :)
                  • 0
                    на eee pc 900 — 20 мин
                    на phenom 4 ядра — 5 мин
                    говорят на iMac — 1 мин
                    • –2
                      надо будет на своем «BIGBROTHER» проверить :)
                      • 0
                        на моем хакинтоше минуты полторы-две (C2D E8500/2GB)
                        • 0
                          у меня на кваде (q9400) секунд 30
                      • 0
                        так как есть андроид-девайс, то тестирую сразу на нем. быстрее получается :)
                    • +2
                      Рекомендую, кстати, статью с сайта IBM: www.ibm.com/developerworks/ru/library/x-android/
                      Очень понятное и полезное руководство
                      • 0
                        Со мной связался челове по скайпу и написал такие коментарии.
                        • НЛО прилетело и опубликовало эту надпись здесь
                          • +6
                            Дмитрий: Хотел посоветовать приписать, что это способ влоб, при разработке серьезных приложений, так делать нельзя.

                            Для затравки стоит указать направление начинающим чтобы они сразу пошли правильной дорогой
                            www.youtube.com/watch?v=xHXn3Kg2IQE&playnext_from=TL&videos=SF9H436Fikg
                            Тут как раз инженер гугла все рассказывает, очень поучительно через час все поймете сами.

                            — нельзя держать все в памяти, потому что жизненный цикл приложений андроида в любой момент это все похерить, нужно писать в базу, возвращать курсор и строить список при помощи CursorAdapter

                            — памяти и ресурсов очень мало, поэтому использовать org.w3c.dom.* для разбора XML будет очень дорого, лучше saxparser и сразу в базу без промежуточного сохранения в памяти

                            title = "\n" + selectedRssItem.getTitle() + " ( "
                            + sdf.format(selectedRssItem.getPubDate()) + " )\n\n";

                            дорогая операция, много мусора от нее остается, лучше использовать StringBuilder для конкантенации строк
                            — где-то еще видел создание объекта в функции, когда его инициализацию можно вынести в конструктор, но это уже не на столько важно

                            А так статья просто отличная, на 5 с плюсом, попалась бы она мне раньше, было бы гораздо проще начинать! спасибо.
                            • –1
                              StringBuilder надо использовать осторожно, иначе на то же и выйдет.
                            • 0
                              Дмитрий:
                              for (int i = 0; i < nodeList.getLength(); i++)
                              не помню реалищует ли NodeList интерфейс Iterable, если да, то лучше делать такие штуки for (obj1: obj2) (foreach типа), если нет, то результат nodeList.getLength() помещать в локальную переменную перед циклом

                              developer.android.com/guide/practices/design/performance.html
                              • 0
                                можно и без локальных переменных ;)
                                for (int i = nodeList.getLength() — 1; i >= 0; i--)
                              • 0
                                спасибо
                                понятно и удобно
                                • 0
                                  Извините за глупый вопрос, но где дизайн редактируется?
                                  • 0
                                    Редактора не нашел. Может плохо искал. Все писал руками в layout файлы. Может кто посоветует решения для разработки дизайна.
                                    • 0
                                      … достаточно просто переключить режим редактирования лайоута в eclipse :)
                                  • 0
                                    вот здесь
                                    www.droiddraw.org/
                                  • +1
                                    С первого взгляда не заметил ничего что использует возможности 2.2, поэтому target sdk стоит поставить 4, а minimal sdk 3. Это позволит запускать приложение на всех девайсах начиная с Android 1.5 и добавит возможность сделать различный арт для различных разрешений экрана.
                                    • 0
                                      Совершенно верно. Можно вообще 2 поставить.
                                    • +1
                                      Спасибо вам большое, как раз намедни искал нечто подобное!
                                      • –2
                                        я бы не рискнул купить телефон с андроидом…
                                        • 0
                                          Вы меня, конечно, извините, но даже для начинающих этот обзор имхо не годится — уж больно много тут вещей, которые нужно бы сделать по другому (начиная от minSkd, про который уже сказали и заканчивая SAX/XML PULL вместо DOM)
                                          • +1
                                            Напишите! А мы почитаем )
                                            • –1
                                              тут, увы, нужно слишком много всего переписывать :(
                                              • 0
                                                написал ниже
                                              • 0
                                                Можете прокоментировать подробней? Буду очень признателен.

                                                Пишу следующую статью.
                                                • +2
                                                  по андроиду:
                                                  minSdk можно (и нужно) ставить 4, а не 8
                                                  listView можно использовать через ListActivity
                                                  list_item нет смысла делать fill_parent по высоте, насколько я понимаю
                                                  ListView не нужно делать wrap_content
                                                  все размеры стоит вынести в одно место (или даже скорее проинклюдать вьюшки)
                                                  //Bundle extras = getIntent().getExtras(); я так понимаю вы неосилили и поэтому храните текущий Item как статик в главной активити?

                                                  по коду:
                                                  SimpleDateFormat стоит прокэшировать
                                                  код
                                                  ArrayList<RssItem> rssItems = new ArrayList<RssItem>();
                                                  
                                                  Блох советует писать как
                                                  ArrayList<RssItem> rssItems = new ArrayList<RssItem>();
                                                  
                                                  (однако для как минимум android <2.2 ваш способ всё же правильнее с т.з. перфоманса)
                                                  SimpleDateFormat стоит закэшировать

                                                  ps освойте уже, наконец
                                                  1) форматирование кода и
                                                  2) форматирование XML, оно ведь даже в eclipse работает (плохенько, но хоть как-то)
                                                  3) optimize imports
                                                  • 0
                                                    дальше:
                                                    про DOM vs SAX/PULL — ну тут всё понятно
                                                    onClickListener у вас синхронный, насколько я вижу, что есть страшное зло
                                                    адаптер лучше использовать resourceCursor через провайдер и сервис
                                                    интент лучше вызывать явно по имени класса, а не по action'у, я думаю
                                                    кастомизировать AVD нет никакого смысла — она по-дефолту вполне себе умеет в интернет ходить
                                                    стартует виртуалка на более-менее современных компах всего минуту (первый запуск может чуть подольше — пара минут), приложение деплоится за секунды

                                                    ну вот примерный список после прочтения по диагонали — сами видите, что замечаний предостаточно, причём многие я считаю критическими
                                                    • 0
                                                      Ценные замечания. Благодарю.

                                                      Не все пока понятно, но буду работать.
                                                • 0
                                                  вот здесь можно создавать интерфейс приложений. просто и наглядно
                                                  www.droiddraw.org/
                                                  • 0
                                                    Было бы просто супер, если описали коротко какие библиотеки для чего.
                                                    • 0
                                                      а где реализация ArrayAdapter? он кастомный?

                                                      просто сейчас заткнулся именно на этом адаптере. не понятно немного.

                                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.