Пользователь
9 июля 2010 в 17:45

Простой 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
Александр Решетник @degratnik
карма
0,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое

Комментарии (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
          Где?
          • +1
            правый клик->open with->layout editor
    • 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? он кастомный?

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

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