Notice: Undefined offset: 1 in /home5/fructorg/public_html/qt.e-werest/engine/modules/viewer/plugs/modifier.declension.php on line 45
Android: 22 Поиск по тегам / Qt.E-WeREST.org
  
 

Публикация приложений для ОС Android. Android Market.

Продолжается серия статей "Публикация приложений для ОС Android". В предыдущей статье я рассмотрел общие вопросы по подготовке приложения к публикации вне зависимости от метода распространения, к которому вы прибегнете. Эта статья

( Читать дальше )

Публикация приложений для ОС Android. Подготовка приложения.

В рамках серии статей, объединённых общим названием "Публикация приложений для ОС Android" я хочу рассказать о технической стороне по подготовке приложения к публикации и осветить несколько основных подходов к распространению готового приложения на устройства пользователей.



( Читать дальше )

Necessitas — порт Qt на Android

Основная задача проекта Necessitas — портирование Qt на платформу Android. Работа над проектом Necessitas была начата в 2009 году румынским программистом-энтузиастом Богданом Ватрой. Сейчас это динамически развивающийся проект, объединяющий



( Читать дальше )

Nokia продолжает говорить "Нет" Android

Компания Nokia уже неоднократно говорила о том, что не планирует использовать в своих смартфонах операционную систему Google Android. Более того, не так давно исполнительный вице-президент и член совета директоров Ансси Ваньоки (Anssi Vanjoki) довольно красочно прокомментировал вариант использования данной ОС финским производителем.




( Читать дальше )
  • +1
  • 19 ноября 2010, 15:48
  • Eric
  • 2

Новейшая система защиты от пиратства Google взломана за месяц

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


( Читать дальше )

Небольшое приложение для облачных вычислений в Android

 

Небольшое приложение для облачных вычислений в Androi

Дата:  18.02.2010
Доступ к файловой системе телефона под управлением Android через браузер

Источник: IBM developerWorks Россия
АвторБилл Циммерли, специалист в области инженерии знаний, независимый писатель

Описание: Технология облачных вычислений (cloud computing) требует как минимум двух компонентов: клиентской программы, которая выполняется на мобильном устройстве, и серверного программного обеспечения, работающего на сетевом сервере. В этой статье рассказывается о создании Android-сервиса, имитирующего сетевой сервер. Он откроет новые, неожиданные возможности вашего устройства. Поместив небольшое "облачко" в ваш карманный компьютер, вы превратите его в полезный локальный Web-сервер.

Уровень сложности:  средний

Содержание:

Работа с XML в Android

 

Работа с XML в Android

Дата:  19.02.2010
Разработка Java-приложений для мобильных устройств

Источник: IBM developerWorks Россия
АвторМайкл Галпин, инженер по программному обеспечению, Vitria Technology

Описание:  Android – это современная операционная система с открытым кодом, ориентированная на мобильные устройства и включающая в себя SDK, который позволяет создавать приложения с широким кругом возможностей. Возможности Android особенно привлекательны, если приложения должны взаимодействовать с Web-сервисами, поскольку в этом случае они обязаны понимать XML – своего рода язык Web. В этой статье рассказывается о нескольких различных вариантах работы с XML в Android, а также об их использовании при создании приложений.

Уровень сложности:  средний

Содержание:



Начало работы

Прочитав эту статью, вы узнаете о создании Android-приложений, способных работать с XML в Интернете. Программы для Android пишутся на Java™, поэтому для понимания материала необходим опыт использования этого языка программирования. Кроме того, вам понадобится инструментарий разработки программного обеспечения для Android (SDK). Все примеры кода, приведенные в этой статье, могут работать с любой версией Android, хотя для их создания использовался SDK 1.5_pre. В принципе приложения для Android можно создавать, имея под рукой только SDK и текстовый редактор, но гораздо проще использовать специальный модуль ADT, подключаемый к Eclipse (Android Developer Tools - инструментарий Android-разработчика). В этой статье используется ADT 0.9 и Eclipse 3.4.2 Java Edition (ссылки на все программы приведены в разделе Ресурсы).


XML в Android

Android представляет собой платформу с открытым кодом для разработки приложений для мобильных устройств. С ее помощью можно получить доступ ко всем компонентам устройства, на котором выполняется эта ОС, начиная от низкоуровневого программирования графики и заканчивая использованием встроенной камеры. В Android есть столько всего интересного, что логично задать вопрос: а зачем вообще задумываться об XML? Однако интерес представляет не столько сам XML, сколько взаимодействие с другими объектами с его помощью. В частности, XML является распространенным форматом для обмена информацией в Интернете, поэтому велика вероятность, что он понадобится вам для доступа к данным в Web. Кроме того, XML может потребоваться для передачи данных, например, Web-сервису. Другими словами, если вы хотите, чтобы ваше приложение для Android работало через Интернет, вам скорее всего придется иметь дело с XML. К счастью, существует множество вариантов работы с XML в Android.


Парсеры XML

Часто встречающиеся аббревиатуры

  • API: Application programming interface (Интерфейс прикладного программирования)
  • RSS: Real Simple Syndication (Действительно простая синдикация)
  • SDK: Software Developers Kit (пакет разработчика программного обеспечения)
  • UI: пользовательский интерфейс
  • URL: универсальный локатор ресурса
  • XML: расширяемый язык разметки

Одной из наиболее привлекательных черт платформы Android является использование языка программирования Java. SDK Android поддерживает не всю, но достаточно большую часть возможностей стандартной среды выполнения Java (Java Runtime Environment – JRE). Cама платформа Java уже долгое время поддерживает множество различных способов использования XML, причем большинство API для Java, ориентированных на XML, доступны в Android. Примерами таких API могут служить объектная модель документов (Document Object Model — DOM) и простой Java API для XML (Java's Simple API for XML – SAX), которые уже много лет являются частью технологии Java. Обратным примером является более новый потоковый API ( Streaming API for XML – StAX), который не поддерживается в Android (при этом в состав Android входит эквивалентная по своим возможностям библиотека). В Android также недоступен API для связывания с данными XML (Java XML Binding — JAXB). Его безусловно можно реализовать для данной платформы, однако он отличается некоторой тяжеловесностью, которая проявляется в том, что множество экземпляров разных классов часто требуется для представления документов XML. В связи с этим он является менее предпочтительным при создании приложений для портативных устройств, подобных тем, на которых работает Android. Далее мы рассмотрим в качестве примера простой источник XML-данных в Интернете, а также способы их разбора внутри Android-приложения при помощи перечисленных выше API. Мы начнем с рассмотрения основных компонентов простого приложения, работающего с XML-данными, полученными через Интернет.


Приложение для чтения новостей для Android

Ниже будет рассмотрено приложение, получающее информационную ленту RSS с сайта Androidster, популярного среди Android-разработчиков, и трансформирующее ее в набор простых Java-объектов. Этот набор далее будет выступать в качестве содержимого компонента ListView (ссылка на исходный код приведена в разделе Загрузка). Приложение следует классическим принципам полиморфизма: его поведение будет оставаться внешне одинаковым при использовании различных алгоритмов разбора XML. Данная модель легко представима в Java, как показано в листинге 1.


Листинг 1. Интерфейс парсера XML-лент


        
package org.developerworks.android;
import java.util.List;

public interface FeedParser {
    List<Message> parse();
}

В листинге 2 показан класс

Message
, являющийся примером простого Java-объекта (Plain Old Java Object — POJO) и служащий для представления определенной структуры данных (сообщения).


Листинг 2. POJO-класс

Message


        
public class Message implements Comparable<Message>{
    static SimpleDateFormat FORMATTER = 
        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
    private String title;
    private URL link;
    private String description;
    private Date date;

      // get- и set-методы опущены для краткости
    public void setLink(String link) {
        try {
            this.link = new URL(link);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    public String getDate() {
        return FORMATTER.format(this.date);
    }

    public void setDate(String date) {
        // удлинение представления даты при необходимости
        while (!date.endsWith("00")){
            date += "0";
        }
        try {
            this.date = FORMATTER.parse(date.trim());
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
    
    @Override
    public String toString() {
             // реализация опущена для краткости
    }

    @Override
    public int hashCode() {
             // реализация опущена для краткости
    }
    
    @Override
    public boolean equals(Object obj) {
             // реализация опущена для краткости
    }
      // сортировка по дате
    public int compareTo(Message another) {
        if (another == null) return 1;
        // сортировка по убывания, наиболее свежие записи выводят сверху
        return another.date.compareTo(date);
    }
}

Класс сообщения, показанный в листинге 2, достаточно прост. Он позволяет скрыть от внешних компонентов некоторые детали своего внутреннего состояния, в частности, он разрешает работать с датами и ссылками как со строковыми объектами, хотя хранятся они в строго типизированном виде (

java.util.Date
и
java.net.URL
). Этот класс является классическим примером объекта-значения, обладающего собственной реализацией методов
equals()
и
hashCode()
на основе текущего состояния. Он также реализует интерфейс
Comparable
, служащий для сортировки (на практике этот метод не нужен, поскольку записи в ленте всегда упорядочены).

 

Каждая реализация парсера (алгоритма разбора XML) принимает на вход URL RSS-ленты Androidster и открывает HTTP-соединение с указанным сайтом. Эту функциональность логично вынести в абстрактный базовый Java-класс, как показано в листинге 3.


Листинг 3. Базовый класс парсеров лент


        
public abstract class BaseFeedParser implements FeedParser {

    // Имена тегов XML
    static final String PUB_DATE = "pubDate";
    static final  String DESCRIPTION = "description";
    static final  String LINK = "link";
    static final  String TITLE = "title";
    static final  String ITEM = "item";
    
    final URL feedUrl;

    protected BaseFeedParser(String feedUrl){
        try {
            this.feedUrl = new URL(feedUrl);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    protected InputStream getInputStream() {
        try {
            return feedUrl.openConnection().getInputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

Этот базовый класс хранит URL ленты в переменной

feedUrl
  и использует его для открытия потока ввода (
java.io.InputStream
). В случае возникновения любых ошибок он просто генерирует исключение типа
RuntimeException
, приводящее к быстрому аварийному завершению приложения. Кроме того, класс определяет ряд простых констант для имен тегов. Значение этих тегов иллюстрируется в листинге 4, в котором показан фрагмент информационной ленты.


Листинг 4. Пример ленты XML


        
<?xml version="1.0" encoding="UTF-8"?>

<!-- generator="FeedCreator 1.7.2" -->

<rss version="2.0">
    
  <channel>
        
    <title>android_news</title>
           
    <description>android_news</description>
        
    <link>http://www.androidster.com/android_news.php</link>
        
    <lastBuildDate>Sun, 19 Apr 2009 19:43:45 +0100</lastBuildDate>
        
    <generator>FeedCreator 1.7.2</generator>
        
    <item>
            
      <title>Samsung S8000 to Run Android, Play DivX, Take Over the 
World</title>
             
      <link>http://www.androidster.com/android_news/samsung-s8000-to-run-android-
play-divx-take-over-the-world</link>
            
      <description>More details have emerged on the first Samsung handset 
to run Android. A yet-to-be announced phone called the S8000 is being 
reported ...</description>
            
      <pubDate>Thu, 16 Apr 2009 07:18:51 +0100</pubDate>
        
    </item>
        
    <item>
            
      <title>Android Cupcake Update on the Horizon</title>
            
      <link>http://www.androidster.com/android_news/android-cupcake-update-
on-the-horizon</link>
            
      <description>After months of discovery and hearsay, the Android 
build that we have all been waiting for is about to finally make it 
out ...</description>
            
      <pubDate>Tue, 14 Apr 2009 04:13:21 +0100</pubDate>
        
    </item>
    
  </channel>

</rss>

Как видно из примера в листинге 4, каждый элемент

ITEM
соответствует экземпляру класса
Message
. При этом дочерние элементы записи (
TITLE
,
LINK
и т. д.) соответствуют свойствам
Message
. Теперь, когда вы получили представление о том, как выглядят ленты RSS, можно переходить к рассмотрению их разбора при помощи различных технологий, доступных в Android. В качестве первой такой технологии мы рассмотрим SAX.


Использование SAX

В Java API SAX часто используется в тех случаях, когда требуется быстрый парсер и необходимо минимизировать расход памяти в приложении. Именно поэтому SAX очень привлекателен для использования в мобильных устройствах под управлением Android. При этом при создании приложений для Android этот API можно использовать в точности так же, как и в Java. Реализация интерфейса

FeedParser
, основанная на SAX, показана в листинге 5.


Листинг 5. Реализация парсера на основе SAX


        
public class SaxFeedParser extends BaseFeedParser {

    protected SaxFeedParser(String feedUrl){
        super(feedUrl);
    }
    
    public List<Message> parse() {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
            SAXParser parser = factory.newSAXParser();
            RssHandler handler = new RssHandler();
            parser.parse(this.getInputStream(), handler);
            return handler.getMessages();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } 
    }
}

Этот фрагмент кода должен выглядеть привычно для тех, у кого уже есть опыт использования SAX. Большая часть кода заключена в обработчике, что характерно для любого SAX-парсера. Обработчик получает уведомления о событиях от парсера по мере того, как тот разбирает документ XML. В данном случае в роли такого обработчика выступает класс

RssHandler
(листинг 6).


Листинг 6. Обработчик событий SAX


        
import static org.developerworks.android.BaseFeedParser.*;

public class RssHandler extends DefaultHandler{
    private List<Message> messages;
    private Message currentMessage;
    private StringBuilder builder;
    
    public List<Message> getMessages(){
        return this.messages;
    }
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        super.characters(ch, start, length);
        builder.append(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String name)
            throws SAXException {
        super.endElement(uri, localName, name);
        if (this.currentMessage != null){
            if (localName.equalsIgnoreCase(TITLE)){
                currentMessage.setTitle(builder.toString());
            } else if (localName.equalsIgnoreCase(LINK)){
                currentMessage.setLink(builder.toString());
            } else if (localName.equalsIgnoreCase(DESCRIPTION)){
                currentMessage.setDescription(builder.toString());
            } else if (localName.equalsIgnoreCase(PUB_DATE)){
                currentMessage.setDate(builder.toString());
            } else if (localName.equalsIgnoreCase(ITEM)){
                messages.add(currentMessage);
            }
            builder.setLength(0);    
        }
    }

    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
        messages = new ArrayList<Message>();
        builder = new StringBuilder();
    }

    @Override
    public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException {
        super.startElement(uri, localName, name, attributes);
        if (localName.equalsIgnoreCase(ITEM)){
            this.currentMessage = new Message();
        }
    }
}

Класс

RssHandler
 является наследником класса
org.xml.sax.helpers.DefaultHandler
, который предоставляет пустую реализацию по умолчанию для обработчиков всех событий, генерируемых SAX-парсером. Благодаря этому базовому классу дочерние классы могут переопределять только методы, которые соответствуют интересующим их событиям.
RssHandler
реализует еще один API, состоящий из метода
getMessages
. Этот метод возвращает список объектов типа
Message
, составляемый в процессе обработки событий, полученных от SAX-парсера. Кроме того, класс включает две внутренние переменные:
currentMessage
типа
Message
, представляющую собой текущее разбираемое сообщение, и
builder
типа
StringBuilder
, в которой хранится текстовое содержимое текстовых вершин. Обе эти переменные инициализируются в методе
startDocument
, который вызывается при начале разбора документа парсером.

 

Обратите внимание на метод

startElement
в листинге 6. Он вызывается каждый раз, когда парсер встречает открывающий тег в документе XML. В нашем случае значение имеют только теги
ITEM
, для каждого из которых необходимо создать новый экземпляр типа
Message
. Далее рассмотрим метод
characters
. Он вызывается при разборе содержимого текстовых вершин в XML, которое просто добавляется в переменную
builder
. Наконец, взгляните на метод
endElement
, вызывающийся парсером при обнаружении закрывающего тега. В случае, если данный тег соответствует одному из атрибутов сообщения, например,
TITLE
или
LINK
, устанавливается значение соответствующего свойства объекта, хранящегося в переменной
currentMessage
. При этом значение берется из переменной
builder
. Если же закрывающим тегом является
ITEM
, то текущее сообщение (
currentMessage
) добавляется в список сообщений. Подобное поведение является классическим примером использования SAX, и в нем нет ничего специфичного для Android. Таким образом, если вы умеете писать SAX-парсеры на Java, то у вас не будет проблем с их созданием для Android. Более того, в SDK Android реализован ряд полезных функций в дополнение к SAX.


Упрощение использования SAX в Android

SDK Android включает вспомогательный класс

android.util.Xml
. Его использование в парсере SAX показано в листинге 7.


Листинг 7. Реализация SAX-парсера в Android


        
public class AndroidSaxFeedParser extends BaseFeedParser {

    public AndroidSaxFeedParser(String feedUrl) {
        super(feedUrl);
    }

    public List<Message> parse() {
        RssHandler handler = new RssHandler();
        try {
            Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, handler);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return handler.getMessages();
    }

}

Обратите внимание, что этот класс по-прежнему способен работать с любыми обработчиками SAX, поэтому в нем используется ранее созданный

RssHandler
. Возможность повторного использования обработчиков является несомненным преимуществом, однако они могут оказаться чересчур сложными. Нетрудно представить, что если придется анализировать значительно более сложные документы XML, обработчик может легко превратиться в неиссякаемый источник ошибок. Например, взгляните еще раз на метод
endElement
 в листинге 6 и обратите внимание на проверку переменной
currentMessage
на null перед установкой значения свойств. Теперь вернитесь к фрагменту XML, приведенному в листинге 4. Как видите, он включает элементы
TITLE
и
LINK
, находящиеся вне элемента
ITEM
. Именно этим объясняется проверка на null, поскольку иначе обработка первого же элемента
TITLE
приведет к исключению типа
NullPointerException
. К счастью, Android включает свой собственный вариант API SAX, благодаря которому можно не писать собственные обработчики. Пример приведен в листинге 8.


Листинг 8. Упрощенная реализация SAX-парсера для приложения Android


        
public class AndroidSaxFeedParser extends BaseFeedParser {

    public AndroidSaxFeedParser(String feedUrl) {
        super(feedUrl);
    }

    public List<Message> parse() {
        final Message currentMessage = new Message();
        RootElement root = new RootElement("rss");
        final List<Message> messages = new ArrayList<Message>();
        Element channel = root.getChild("channel");
        Element item = channel.getChild(ITEM);
        item.setEndElementListener(new EndElementListener(){
            public void end() {
                messages.add(currentMessage.copy());
            }
        });
        item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener(){
            public void end(String body) {
                currentMessage.setTitle(body);
            }
        });
        item.getChild(LINK).setEndTextElementListener(new EndTextElementListener(){
            public void end(String body) {
                currentMessage.setLink(body);
            }
        });
        item.getChild(DESCRIPTION).setEndTextElementListener(new 
EndTextElementListener(){
            public void end(String body) {
                currentMessage.setDescription(body);
            }
        });
        item.getChild(PUB_DATE).setEndTextElementListener(new EndTextElementListener(){
            public void end(String body) {
                currentMessage.setDate(body);
            }
        });
        try {
            Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, 
root.getContentHandler());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return messages;
    }
}

Как и было обещано, в этом фрагменте кода не используются классы-обработчики SAX. Вместо этого парсер использует классы из пакета android.sax, входящего в состав SDK. Эти классы помогают моделировать структуру документов XML и добавлять слушатели событий по мере необходимости. В приведенном выше примере декларируется, что документ XML имеет корневую вершину

rss
, у которой есть дочерняя вершина
channel
. Внутри нее располагаются элементы
ITEM
, для которых добавляются слушатели. Каждый из слушателей реализуется в виде анонимного внутреннего класса, реализующего специальный интерфейс (в данном случае либо
EndElementListner
, либо
EndTextElementListener
). Обратите внимание, что в этом подходе не приходится отдельно хранить текстовое содержимое, что не только проще, но и эффективнее. При этом обработчик, который передается в вспомогательный метод Xml.parse, автоматически определяется корневым элементом документа.

 

Подход, продемонстрированный в листинге 8, не является обязательным. Если вас устраивает стандартный способ работы с SAX в Java, вы можете использовать его. В противном случае вы можете работать с SAX через вспомогательные классы, предоставляемые SDK Android. Однако что делать, если вы вообще не хотите использовать SAX? В этом случае есть еще несколько вариантов, одним из которых является DOM.


Использование DOM

Разбор документов XML по принципам DOM полностью поддерживается в Android. Этот API работает точно так же, как в Java-приложениях для серверов и настольных компьютеров. Реализация интерфейса парсера лент RSS на основе DOM показана в листинге 9.


Листинг 9. Реализация парсера XML-лент на основе DOM


        
public class DomFeedParser extends BaseFeedParser {

    protected DomFeedParser(String feedUrl) {
        super(feedUrl);
    }

    public List<Message> parse() {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        List<Message> messages = new ArrayList<Message>();
        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document dom = builder.parse(this.getInputStream());
            Element root = dom.getDocumentElement();
            NodeList items = root.getElementsByTagName(ITEM);
            for (int i=0;i<items.getLength();i++){
                Message message = new Message();
                Node item = items.item(i);
                NodeList properties = item.getChildNodes();
                for (int j=0;j<properties.getLength();j++){
                    Node property = properties.item(j);
                    String name = property.getNodeName();
                    if (name.equalsIgnoreCase(TITLE)){
                        message.setTitle(property.getFirstChild().getNodeValue());
                    } else if (name.equalsIgnoreCase(LINK)){
                        message.setLink(property.getFirstChild().getNodeValue());
                    } else if (name.equalsIgnoreCase(DESCRIPTION)){
                        StringBuilder text = new StringBuilder();
                        NodeList chars = property.getChildNodes();
                        for (int k=0;k<chars.getLength();k++){
                            text.append(chars.item(k).getNodeValue());
                        }
                        message.setDescription(text.toString());
                    } else if (name.equalsIgnoreCase(PUB_DATE)){
                        message.setDate(property.getFirstChild().getNodeValue());
                    }
                }
                messages.add(message);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } 
        return messages;
    }
}

Как и в случае c SAX, в данном коде нет ничего специфичного для платформы Android. Парсер DOM считывает содержимое всего документа в память и предоставляет методы API для обхода дерева XML, позволяющие находить нужную информацию. Этот подход весьма очевиден и в некотором смысле проще реализаций, основанных на SAX. Однако использование DOM, как правило, требует больше памяти, поскольку в нее считываются все узлы XML. Это может представлять собой реальную проблему для портативных устройств под управлением Android, за исключением случаев разбора заведомо небольших документов XML. Учитывая это, нетрудно было предположить, что SAX должен быть значительно популярнее среди разработчиков Android-приложений, поэтому именно для него были созданы вспомогательные классы. Кроме SAX и DOM, платформа поддерживает и третий тип парсеров, а именно принимающие парсеры (pull parsers).


Принимающий парсер XML

Как было замечено выше, Android не поддерживает API StAX. Однако в состав Android входит принимающий парсер, работающий аналогично StAX. Он позволяет вашему приложению принимать события от парсера в отличие от SAX-парсеров, которые автоматически передают события обработчику. Реализация принимающего парсера для анализа лент RSS показана в листинге 10.


Листинг 10. Реализация принимающего парсера


        
public class XmlPullFeedParser extends BaseFeedParser {
    public XmlPullFeedParser(String feedUrl) {
        super(feedUrl);
    }
    public List<Message> parse() {
        List<Message> messages = null;
        XmlPullParser parser = Xml.newPullParser();
        try {
            // автоматическое определение кодировки потока
            parser.setInput(this.getInputStream(), null);
            int eventType = parser.getEventType();
            Message currentMessage = null;
            boolean done = false;
            while (eventType != XmlPullParser.END_DOCUMENT && !done){
                String name = null;
                switch (eventType){
                    case XmlPullParser.START_DOCUMENT:
                        messages = new ArrayList<Message>();
                        break;
                    case XmlPullParser.START_TAG:
                        name = parser.getName();
                        if (name.equalsIgnoreCase(ITEM)){
                            currentMessage = new Message();
                        } else if (currentMessage != null){
                            if (name.equalsIgnoreCase(LINK)){
                                currentMessage.setLink(parser.nextText());
                            } else if (name.equalsIgnoreCase(DESCRIPTION)){
                                currentMessage.setDescription(parser.nextText());
                            } else if (name.equalsIgnoreCase(PUB_DATE)){
                                currentMessage.setDate(parser.nextText());
                            } else if (name.equalsIgnoreCase(TITLE)){
                                currentMessage.setTitle(parser.nextText());
                            }    
                        }
                        break;
                    case XmlPullParser.END_TAG:
                        name = parser.getName();
                        if (name.equalsIgnoreCase(ITEM) && 
currentMessage != null){
                            messages.add(currentMessage);
                        } else if (name.equalsIgnoreCase(CHANNEL)){
                            done = true;
                        }
                        break;
                }
                eventType = parser.next();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return messages;
    }
}

Принцип работы принимающего парсера похож на SAX. Он оперирует теми же событиями (начало элемента, конец элемента), однако их необходимо запрашивать явным образом при помощи метода (

parser.next()
). События имеют числовые идентификаторы (коды), поэтому для их выбора можно использовать оператор case-switch. Следует отметить, что вместо отслеживания закрывающих тегов, как в SAX, в случае принимающего парсера проще обрабатывать содержимое элемента при обнаружении открывающего тега. В листинге 10 приведен пример вызова метода
parser.nextText()
 при обработке начала элемента для получения его текстового содержимого. Это существенно упрощает разбор документов по сравнению с SAX. Кроме того, обратите внимание на установку булева флага
done
, сигнализирующего об окончании обработки интересующего фрагмента документа. Это позволяет остановить процесс чтения документа XML если вы уверены, что в оставшейся его части нет ничего интересного для вашего приложения. Это весьма полезная возможность, особенно в тех случаях, когда требуется считать лишь малую часть документа. Подобные оптимизации играют весьма важную роль на мобильных устройствах, которые зачастую работают через медленные соединения. Таким образом, использование принимающего парсера может быть не только проще, но и выгоднее с точки зрения производительности. Наконец, он может использоваться для редактирования документов XML.


Создание документов XML

До этого момента мы рассматривали варианта разбора документов XML, полученных из Интернета. Однако существуют ситуации, в которых вашему приложению необходимо отправлять данные в формате XML удаленному серверу. Разумеется, вы можете просто создавать текстовое представление XML при помощи

StringBuilder
или аналогичного класса. В качестве альтернативного решения можно воспользоваться принимающим парсером, как показано в листинге 11.


Листинг 11. Формирование документа XML при помощи принимающего парсера


        
private String writeXml(List<Message> messages){
    XmlSerializer serializer = Xml.newSerializer();
    StringWriter writer = new StringWriter();
    try {
        serializer.setOutput(writer);
        serializer.startDocument("UTF-8", true);
        serializer.startTag("", "messages");
        serializer.attribute("", "number", String.valueOf(messages.size()));
        for (Message msg: messages){
            serializer.startTag("", "message");
            serializer.attribute("", "date", msg.getDate());
            serializer.startTag("", "title");
            serializer.text(msg.getTitle());
            serializer.endTag("", "title");
            serializer.startTag("", "url");
            serializer.text(msg.getLink().toExternalForm());
            serializer.endTag("", "url");
            serializer.startTag("", "body");
            serializer.text(msg.getDescription());
            serializer.endTag("", "body");
            serializer.endTag("", "message");
        }
        serializer.endTag("", "messages");
        serializer.endDocument();
        return writer.toString();
    } catch (Exception e) {
        throw new RuntimeException(e);
    } 
}

Класс

XmlSerializer
находится в том же пакете, что и
XmlPullParser
, использовавшийся в предыдущем разделе. Однако вместо того, чтобы запрашивать события, он помещает их в поток или передает классу-писателю (в примере выше таковым является
java.io.StringWriter
). Парсер предоставляет простой API, содержащий методы начала и завершения документа, создания элементов, а также формирования текстового содержимого элементов и атрибутов. Этот подход обладает существенным преимуществом по сравнению со
StringBuilder
: с его помощью легче гарантировать синтаксическую корректность документа XML.


Заключение

Вне зависимости от того, какое приложение вы создаете для Android, если ему требуется принимать или отправлять данные через Интернет, оно скорее всего должно будет уметь работать с XML. Как было продемонстрировано в этой статье, Android поддерживает множество технологий для обработки документов XML, поэтому вы можете делать свой выбор как на основе личных предпочтений, так и основываясь на конкретной ситуации. В большинстве случаев можно смело выбирать SAX и использовать этот API либо традиционным образом, либо через тонкий вспомогательный класс. Если вы работаете с небольшими документами XML, то, возможно, проще использовать DOM. В противном случае, а также если требуется обработать лишь часть документа, использование принимающего парсера XML может быть выгоднее из соображений производительности. Кроме того, принимающий парсер предоставляет удобные возможности для создания и редактирования документов. Таким образом, каковы бы ни были потребности вашего приложения в отношении работы с XML, вы всегда сможете найти нужную технологию в SDK Android.



Загрузка

Имя Размер
AndroidXml.zip 70 KБ

Ресурсы

Научиться

Получить продукты и технологии

  • SDK Android: загрузите пакет разработчика, ознакомьтесь с документацией по API и прочитайте самые свежие новости об Android на официальном сайте разработчиков для этой платформы. (EN)
     
  • Загрузите исходный код проекта Android. (EN)
     
  • Загрузите и попробуйте в деле последнюю версию

    ( Читать дальше )

Подключение к датчикам в Android

 

Подключение к датчикам в Android

Дата:  26.02.2010
Наблюдаем за окружающей средой дистанционно

Источник: IBM developerWorks Россия
Автор: Фрэнк Эйблсон, проектировщик ПО, Независимый разработчик

Описание:  В этой статье мы научимся взаимодействовать через Android с различными датчиками в целях мониторинга окружающей среды.

Уровень сложности:  средний



Содержание:



Введение

Платформа Android идеально подходит для создания инновационных приложений с использованием аппаратных датчиков, особенно Java-программистами. Мы рассмотрим некоторые варианты интерфейсов для приложений Android, включая использование подсистемы датчиков и записи аудиофрагментов.

Какое приложение лучше построить, чтобы задействовать аппаратные возможности устройства на платформе Android? Подойдет любое, в котором требуются электронные глаза и уши. На ум приходит электронная няня, система безопасности или даже сейсмограф. Хотя с метафизической точки зрения одновременно присутствовать в двух местах невозможно, Android может предложить некоторые практические способы преодоления этого ограничения. В рамках этой статьи под Android-устройством понимается не просто "мобильный телефон", а установленное в определенном месте устройство с беспроводным соединением с сетью, таким как EDGE или Wi-Fi. Загрузите исходный код примера приложения для этой статьи.


Сенсорные возможности Android

Один из приятных аспектов работы с платформой Android заключается в возможности получить доступ к некоторым полезным компонентам самого устройства. До сих пор разработчиков мобильных устройств разочаровывала невозможность доступа к их внутреннему оборудованию. Хотя между вами и металлом все же остается прослойка Java-среды Android, команда разработчиков Android вывела многие возможности аппаратуры на поверхность. А так как это платформа с открытым исходным кодом, можно засучить рукава и написать собственный код для решения своих задач.

 

Загрузите SDK Android, если вы этого еще не сделали. Рекомендуем также изучить содержимое пакета android.hardware и следить за примерами этой статьи. Пакет android.media package содержит классы, которые предоставляют разработчикам новые полезные функции.

 

Ниже описаны некоторые аппаратно-ориентированные функции, содержащиеся в SDK Android.


Таблица 1. Аппаратно-ориентированные функции SDK Android

Функция Описание
android.hardware.Camera
Класс, позволяющий приложениям взаимодействовать с видеокамерой в целях фотосъемки, записи изображений с экрана предварительного просмотра или для изменения параметров настройки.
android.hardware.SensorManager
Класс, обеспечивающий доступ к внутренним датчикам платформы Android. Не каждое устройство на платформе Android поддерживает все датчики из
SensorManager
, однако интересно обдумать такие возможности. (Краткое описание имеющихся датчиков приведено ниже.)
android.hardware.SensorListener
Интерфейс реализован с помощью класса, который используется для ввода значений датчиков по мере их изменения в режиме реального времени. Приложение реализует этот интерфейс для мониторинга одного или нескольких имеющихся аппаратных датчиков. Например, код из этой статьи содержит класс, который использует этот интерфейс для контроля ориентации устройства и показаний встроенного акселерометра.
android.media.MediaRecorder
Класс, используемый для записи медиафрагментов, который можно применять для записи звуков в определенном месте (например, в детской комнате). Можно также анализировать аудиофрагменты для контроля доступа в помещение и в целях безопасности. Например, можно открывать дверь собственным голосом в обычное время своего прихода, вместо того, чтобы обращаться к консьержу за ключом.
android.FaceDetector
Класс, который позволяет распознавать лицо человека по хранящейся в памяти фотографии. Ничто не удостоверяет личность лучше, чем лицо. Если использовать его для блокировки устройства, вам больше не придется запоминать пароли – достаточно биометрических возможностей мобильного телефона.
android.os.* Пакет, содержащий несколько полезных классов для взаимодействия с операционной средой, включая управление питанием, поиск файлов, обработчик и классы для обмена сообщениями. Как и многие другие портативные устройства, телефоны на базе Android могут потреблять достаточно много электроэнергии. Обеспечение "бодрствования" устройства в нужный момент, чтобы проконтролировать нужное событие, - важный аспект проектирования, заслуживающий особого внимания.
java.util.Date

java.util.Timer

java.util.TimerTask
При измерении событий реального мира часто имеют значение дата и время. Например, класс
java.util.Date
позволяет получить метку времени, когда происходит какое-либо событие или возникает определенное состояние.
java.util.Timer
и
java.util.TimerTask
можно использовать соответственно для выполнения периодических действий по расписанию или разового действия в определенный момент времени.

Android.hardware.SensorManager содержит несколько констант, которые характеризуют различные аспекты системы датчиков Android, в том числе:

Тип датчика
Ориентация, акселерометр, свет, магнитное поле, близость, температура и т.д.
Частота измерений
Максимальная, для игр, обычная, для пользовательского интерфейса. Когда приложение запрашивает конкретное значение частоты отсчетов, с точки зрения сенсорной подсистемы это лишь рекомендация. Никакой гарантии, что измерения будут производиться с указанной частотой, нет.
Точность
Высокая, низкая, средняя, ненадежные данные.

Центром сенсорных приложений служит интерфейс

SensorListener
. Он включает в себя два необходимых метода:

  • Метод
    onSensorChanged(int sensor,float values[])
    вызывается всякий раз, когда изменяется значение датчика. Этот метод вызывается только для датчиков, контролируемых данным приложением (подробнее об этом ниже). В число аргументов метода входит целое, которое указывает, что значение датчика изменилось, и массив значений с плавающей запятой, отражающих собственно значение датчика. Некоторые датчики выдают только одно значение данных, тогда как другие предоставляют три значения с плавающей запятой. Датчики ориентации и акселерометр дают по три значения данных каждый.
  • Метод
    onAccuracyChanged(int sensor,int accuracy)
    вызывается при изменении точности показаний датчика. Аргументами служат два целых числа: одно указывает датчик, а другое соответствует новому значению точности этого датчика.

Для взаимодействия с датчиком приложение должно зарегистрироваться на прием действий, связанных с одним или несколькими датчиками. Регистрация осуществляется с помощью метода

registerListener
класса
SensorManager
. Пример кода для этой статьи демонстрирует, как приложение регистрируется и отменяет регистрацию с помощью
SensorListener
.

 

Помните, что не каждое устройство Android поддерживает тот или иной датчик, указанный в SDK. Если на конкретном устройстве тот или иной датчик отсутствует, ваше приложение должно обрабатывать эту ситуацию аккуратно.


Пример работы с датчиком

Этот пример приложения просто следит за изменениями значений датчиков ориентации и акселерометра (исходный код приведен в разделе Загрузки). При изменении значений датчиков они отображаются на экране в виджете

TextView
. Рисунок 1 демонстрирует приложение в действии.


Рисунок 1. Мониторинг ускорения и ориентации
Мониторинг ускорения и ориентации

Приложение создано в среде Eclipse с плагином Android Developer Tools. (Более подробную информацию о разработке Android-приложений с помощью Eclipse см. в разделе Ресурсы.) В листинге 1 приведен код этого приложения.


Листинг 1. IBMEyes.java


package com.msi.ibm.eyes;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.hardware.SensorManager;
import android.hardware.SensorListener;
public class IBMEyes extends Activity implements SensorListener {
    final String tag = "IBMEyes";
    SensorManager sm = null;
    TextView xViewA = null;
    TextView yViewA = null;
    TextView zViewA = null;
    TextView xViewO = null;
    TextView yViewO = null;
    TextView zViewO = null;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       // get reference to SensorManager
        sm = (SensorManager) getSystemService(SENSOR_SERVICE);
        setContentView(R.layout.main);
        xViewA = (TextView) findViewById(R.id.xbox);
        yViewA = (TextView) findViewById(R.id.ybox);
        zViewA = (TextView) findViewById(R.id.zbox);
        xViewO = (TextView) findViewById(R.id.xboxo);
        yViewO = (TextView) findViewById(R.id.yboxo);
        zViewO = (TextView) findViewById(R.id.zboxo);
    }
    public void onSensorChanged(int sensor, float[] values) {
        synchronized (this) {
            Log.d(tag, "onSensorChanged: " + sensor + ", x: " + 
values[0] + ", y: " + values[1] + ", z: " + values[2]);
            if (sensor == SensorManager.SENSOR_ORIENTATION) {
                xViewO.setText("Orientation X: " + values[0]);
                yViewO.setText("Orientation Y: " + values[1]);
                zViewO.setText("Orientation Z: " + values[2]);
            }
            if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
                xViewA.setText("Accel X: " + values[0]);
                yViewA.setText("Accel Y: " + values[1]);
                zViewA.setText("Accel Z: " + values[2]);
            }            
        }
    }

    public void onAccuracyChanged(int sensor, int accuracy) {
    	Log.d(tag,"onAccuracyChanged: " + sensor + ", accuracy: " + accuracy);
    }
    @Override
    protected void onResume() {
        super.onResume();
      // register this class as a listener for the orientation and accelerometer sensors
        sm.registerListener(this, 
                SensorManager.SENSOR_ORIENTATION |SensorManager.SENSOR_ACCELEROMETER,
                SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onStop() {
        // unregister listener
        sm.unregisterListener(this);
        super.onStop();
    }    
}

Это обычное приложение на основе действий, поскольку оно просто выводит на экран данные, получаемые от датчиков. Для ситуаций, когда устройство должно одновременно выполнять другие высокоприоритетные действия, приложение лучше было бы реализовать в форме сервиса.

 

Метод действия

onCreate
получает ссылку на
SensorManager
, где расположены все связанные с датчиками функции. Кроме того, метод
onCreate
создает ссылки на шесть виджетов
TextView
, в которые будут выводиться результаты измерений.

 

Метод

onResume()
, используя ссылку на
SensorManager
, регистрируется на прием обновлений данных датчика посредством метода
registerListener
:

  • Первый параметр представляет собой экземпляр класса, реализующий интерфейс
    SensorListener
    .
  • Второй параметр - это битовая маска желаемых датчиков. В данном случае приложение запрашивает данные из
    SENSOR_ORIENTATION
    и
    SENSOR_ACCELEROMETER
    .
  • Третий параметр указывает системе, как быстро должны обновляться значения датчиков для данного приложения.

Когда работа приложения приостанавливается, нужно отменить регистрацию приемника, чтобы больше не получать обновления значений датчика. Это делается с помощью метода

unregisterListener
класса
SensorManager
. Единственным параметром является экземпляр
SensorListener
.

 

При вызове обоих методов

registerListener
и
unregisterListener
приложение использует ключевое слово
this
. Обратите внимание на ключевое слово
implements
в определении класса, которое декларирует, что этот класс реализует интерфейс
SensorListener
. Вот почему в
registerListener
и
unregisterListener
передается this.

 

SensorListener
должен реализовать два метода:
onSensorChange
и
onAccuracyChanged
. Наш пример приложения не заботится о точности датчиков, а лишь вводит текущие значения X, Y и Z этих датчиков. Метод
onAccuracyChanged
, по существу, ничего не делает; он просто добавляет запись в журнал при каждом вызове.

 

Кажется, что метод

onSensorChanged
вызывается постоянно, так как акселерометр и датчик ориентации передают данные быстро. Чтобы определить, какой датчик передает данные, мы смотрим на первый параметр. Когда передающий датчик выявлен, соответствующие элементы пользовательского интерфейса обновляются данными, содержащимися в массиве значений с плавающей запятой, принятом в качестве второго аргумента метода. Хотя пример просто отображает эти значения, в более сложных приложениях они могут анализироваться, сравниваться с предыдущими значениями или использоваться в некоем алгоритме распознавания образов для определения того, что делает пользователь (или что происходит во внешней среде).

 

Теперь, когда мы рассмотрели подсистему датчиков, в следующем разделе приведем пример кода, который записывает звук на телефон Android. Этот пример работает на устройстве для разработчиков Dev1.


Применение MediaRecorder

Пакет Android.media содержит классы для взаимодействия с мультимедийной подсистемой. Класс

android.media.MediaRecorder
используется для записи медиафрагментов, включая аудио и видео.
MediaRecorder
действует как конечный автомат. Вы задаете различные параметры, такие как устройство-источник и формат. После установки запись может выполняться как угодно долго, пока не будет остановлена.

 

В листинге 2 приведен код для записи звука на устройство Android. Этот код не включает элементы пользовательского интерфейса приложения (полный исходный код см. в разделе загрузок).


Листинг 2. Запись аудиофрагмента


MediaRecorder mrec ;
File audiofile = null;
private static final String TAG="SoundRecordingDemo";
protected void startRecording() throws IOException 
{
   mrec.setAudioSource(MediaRecorder.AudioSource.MIC);
   mrec.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
   mrec.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
   if (mSampleFile == null) 
   {
       File sampleDir = Environment.getExternalStorageDirectory();
       try 
       { 
          audiofile = File.createTempFile("ibm", ".3gp", sampleDir);
       }
       catch (IOException e) 
       {
           Log.e(TAG,"sdcard access error");
           return;
       }
   }
   mrec.setOutputFile(audiofile.getAbsolutePath());
   mrec.prepare();
   mrec.start();
}
protected void stopRecording() 
{
   mrec.stop();
   mrec.release();
   processaudiofile(audiofile.getAbsolutePath());
}
protected void processaudiofile() 
{
   ContentValues values = new ContentValues(3);
   long current = System.currentTimeMillis();
   values.put(MediaStore.Audio.Media.TITLE, "audio" + audiofile.getName());
   values.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
   values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/3gpp");
   values.put(MediaStore.Audio.Media.DATA, audiofile.getAbsolutePath());
   ContentResolver contentResolver = getContentResolver();

   Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
   Uri newUri = contentResolver.insert(base, values);

   sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
}

В методе

startRecording
создается и инициализируется экземпляр
MediaRecorder
.

  • В качестве источника данных выбирается микрофон (
    MIC
    ).
  • Выходной формат устанавливается в 3GPP (файлы *.3gp) - медиаформат, ориентированный на мобильные устройства.
  • Кодер настроен на
    AMR_NB
    - аудиоформат с частотой дискретизации 8 кГц. NB означает узкую полосу частот. Различные форматы данных и имеющиеся кодеры рассматриваются в документации SDK.

Аудиофайл хранится не во внутренней памяти, а на отдельной карте.

External.getExternalStorageDirectory()
возвращает имена карты памяти и временного файла, созданного в этом каталоге. Затем этот файл связывается с экземпляром
MediaRecorder
, обращаясь к методу
setOutputFile
. Аудиоданные будут храниться в этом файле.

 

Вызов метода

prepare
завершает инициализацию
MediaRecorder
. Когда нужно начать процесс записи, вызывается метод
start
. Запись в файл на карте памяти ведется до тех пор, пока не будет вызван метод
stop
. Этот метод освобождает ресурсы, выделенные экземпляру
MediaRecorder
.

 

Когда аудиофрагмент записан, можно выполнить несколько действий:

  • Добавить аудиозапись в медиатеку на устройстве.
  • Выполнить некоторые шаги по распознаванию звука:
    • Не плач ли это ребенка?
    • Это голос хозяина, и нужно разблокировать телефон?
    • Или это фраза "Сезам, откройся", которая отпирает потайную дверь?
  • Автоматически загрузить звуковой файл в сетевую папку для обработки.

В примере кода метод

processaudiofile
добавляет аудиозапись в медиатеку. Для уведомления встроенного приложения о том, что доступна новая информация, используется
Intent
.

 

И последнее замечание об этом фрагменте кода: сразу после создания он не будет записывать аудио. Вы увидите созданный файл, но не услышите звука. Нужно добавить разрешение в файл AndroidManifest.xml:

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

Теперь вы кое-что знаете о взаимодействии с датчиками Android и записи аудио. В следующем разделе приводится более широкий обзор архитектуры приложений, связанных со сбором данных и системами передачи сообщений.


Android как измерительная платформа

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


Рисунок 2. Блок-схема Android-ориентированной системы датчиков
Блок-схема построенной на Android системы датчиков

Это очень гибкая архитектура; логику приложения можно распределять между локальным Android-устройством и серверными ресурсами, которые могут подключаться к более крупным базам данных и вычислительным мощностям. Например, аудиотрек, записанный на локальном Android-устройстве, можно отправить методом

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


Заключение

Из этой статьи вы получили первое представление о работе с датчиками Android. Приведенные примеры приложений определяют ориентацию и ускорение, а также взаимодействуют со средствами звукозаписи с использованием класса

MediaRecorder
. Android - гибкая, удобная платформа для создания систем взаимодействия с реальным миром. Сфера влияния Android очень быстро расширяется, и эта платформа осваивает все новые области применения. Не упускайте ее из вида.



Загрузка

Описание Имя Размер
Исходный код примера Eyes os-android-sensorEyes.zip 28 КБ
Исходный код примера IBMAudio os-android-sensorIBMAudio.zip 33 КБ

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Об авторе

Когда Фрэнк Эйблсон (Frank Ableson) закончил карьеру баскетболиста в команде своего колледжа, не заключив многолетнего контракта с «Лос-Анджедес Лейкерс», он занялся разработкой компьютерных программ. Он любит решать сложные задачи, особенно из области связи и интерфейсов с аппаратурой. Свободное время Фрэнк проводит со своей женой Никки и детьми. С ним можно связаться по адресу: frank@cfgsolutions.com.



Введение в разработку для платформы Android

 

Введение в разработку для платформы Android

Дата:  26.02.2010
Платформа с открытым исходным кодом для специализированных устройств

Источник: IBM developerWorks Россия
Автор: Фрэнк Эйблсон, проектировщик ПО, Независимый разработчик

Описание:  Android — это полноценная операционная среда, основанная на ядре Linux® V2.6. На начальном этапе областью распространения Android был сегмент мобильных телефонов, включая смартфоны и более дешевые раскладные устройства. Однако полный спектр вычислительных сервисов и богатые функциональные возможности Android позволяют выйти за рамки рынка мобильных телефонов. Android может быть полезен для других платформ и приложений. Настоящая статья дает введение в платформу Android и учит программированию простых приложений для этой платформы.

Уровень сложности:  простой



Содержание:



Введение

BlackBerry и iPhone, две удобные и массовые мобильные платформы, олицетворяют собой противоположные концы спектра мобильных устройств. BlackBerry незаменим для корпоративного пользователя. В классе же потребительских устройств у iPhone мало конкурентов в части удобства обращения и «крутизны». Android, молодая и еще не утвердившаяся платформа, способна проявить себя на обоих концах спектра мобильных телефонов и, возможно, даже стать мостом через пропасть между работой и игрой.

 

Сегодня многие сетевые или поддерживающие сеть устройства работают на том или ином варианте ядра Linux. Это солидная платформа: недорогая в развертывании и поддержке и хорошо принимаемая благодаря удобному подходу к развертыванию. Пользовательский интерфейс таких устройств зачастую основан на HTML и может просматриваться с помощью браузера для РС или Mac. Но не всяким устройством нужно управлять посредством компьютера. Рассмотрим обычные бытовые приборы, такие, как плита, микроволновая печь или хлебопечка. Что если у ваших бытовых приборов была бы ОС Android и цветной сенсорный экран? Будь у плиты интерфейс Android, автор и сам, возможно, смог бы что-нибудь приготовить.

 

Из этой стати вы узнаете о платформе Android и о том, как ее можно использовать для мобильных и стационарных приложений. Мы установим SDK Android и создадим простую программу. Загрузите исходный код примера приложения для этой статьи.


Краткая история Android

Платформа Android является продуктом группы Open Handset Alliance, ставящей себе целью создание более совершенного мобильного телефона. Эта группа во главе с Google включает операторов мобильных сетей, производителей телефонов и компонентов, разработчиков программных решений и поставщиков услуг, а также маркетинговые компании. С точки зрения разработки программного обеспечения Android находится в самом центре мира открытого ПО.

 

Первым выпущенным на рынок телефоном на платформе Android стало устройство G1 производства HTC, которое распространяла T-Mobile. Это устройство появилось почти через год после того, как о нем пошли слухи, и единственным инструментом разработки программ для него были постепенно совершенствуемые последовательные выпуски SDK. Накануне выпуска G1 команда Android представила SDK v1.0, и начали появляться приложения для новой платформы.

 

Чтобы стимулировать инновации, Google спонсировала два «Конкурса разработчиков для Android», победители которых получили миллионы долларов. Через несколько месяцев после выхода G1 открылся сайт Android Market, откуда пользователи могли загружать приложения прямо в свой телефон. Всего за полтора года новая мобильная платформа вышла на арену.


Платформа Android

По широте возможностей платформа Android не уступает операционным системам настольных ПК. Это многоуровневая среда на основе ядра Linux с богатыми функциональными возможностями. В подсистему пользовательского интерфейса входят:

  • Окна
  • Представления
  • Виджеты для отображения общих элементов, таких как редактируемые поля, списки и развертывающиеся списки.

Android содержит встраиваемый браузер на базе WebKit - того же механизма с открытым исходным кодом, который лежит в основе браузера Safari мобильного телефона iPhone.

 

Android обладает широким спектром возможностей подключения, охватывающим Wi-Fi, Bluetooth и протоколы передачи данных через сотовую сеть (GPRS, EDGE, 3G и др.). Популярным приемом в приложениях для Android является ссылка на Google Maps для отображения адреса непосредственно в приложении.В стек программного обеспечения Android входит и поддержка сервисов, основанных на определении местоположения (например, GPS), и акселерометров, хотя не все устройства на этой платформе оснащены необходимым оборудованием. Есть также поддержка видеокамеры.

 

Исторически двумя областями, где мобильные приложения отставали от своих настольных собратьев, были графика/мультимедиа и способы хранения данных. Android решает проблему графики благодаря встроенной поддержке 2-D и 3-D графики, включая библиотеку OpenGL. Задача хранения данных упрощается благодаря наличию в платформе Android популярной базы данных с открытым исходным кодом SQLite. На рисунке 1 показана упрощенная схема уровней программного обеспечения Android.


Рисунок 1. Уровни программного обеспечения Android
Уровни программного обеспечения Android


Архитектура приложений

Как уже говорилось, Android работает поверх ядра Linux. Android-приложения пишутся на языке программирования Java и выполняются в виртуальной машине (VM). Важно отметить, что виртуальная машина – это не JVM, как можно было бы ожидать, а открытая технология Dalvik Virtual Machine. Каждое приложение Android запускается внутри экземпляра Dalvik VM, который, в свою очередь заключен в пределах управляемого ядром Linux процесса, как показано на рисунке 2.


Рисунок 2. Dalvik VM
Dalvik VM

Android-приложение содержит элементы одного или нескольких перечисленных ниже типов:

Действия (Activities)
Приложение с графическим интерфейсом реализуется с помощью действия. Когда пользователь выбирает приложение на главном экране или экране пуска приложений, он вызывает действие.
Сервисы (Services)
Сервисы применяются для приложений, которые работают в течение длительного времени, таких как сетевой монитор или проверка обновлений.
Источники данных (Content providers)
Источник данных можно представить себе как сервер баз данных. Его задача - управление доступом к хранящимся данным, таким как база данных SQLite. Если приложение совсем простое, источник данных создавать не обязательно. Если вы пишете более сложное приложение или приложение, в котором к данным обращается несколько действий или приложений, источник данных служит средством организации доступа к вашей информации.
Приемники (Broadcast receivers)
Android-приложение может запускаться для обработки элемента данных или реагирования на события, например, на получение текстового сообщения.

Приложение для Android развертывается на устройстве вместе с файлом AndroidManifest.xml. Этот файл содержит необходимую информацию о конфигурации, которая позволяет правильно установить приложение на устройстве. Он включает также необходимые имена классов и типы событий, которые может обрабатывать приложение, и разрешения, требуемые для его работы. Так, если приложению нужен доступ к сети – например, чтобы загрузить файл, - соответствующее разрешение должно быть явно указано в файле манифеста. Это конкретное разрешение могут иметь многие приложения. Такая защита путем декларирования помогает уменьшить вероятность повреждения устройства по вине некорректно написанного приложения.

В следующем разделе рассматривается среда разработки, необходимая для создания Android-приложений.


Необходимые инструменты

Самый простой способ приступить к разработке приложений для Android - это загрузить SDK Android и Eclipse IDE (см. Ресурсы). Разработку Android-приложений можно вести на платформах Microsoft® Windows®, Mac OS X или Linux.

 

В этой статье предполагается, что вы используете Eclipse IDE и плагин Android Developer Tools для Eclipse. Android-приложения пишутся на языке Java, но компилируются и выполняются в Dalvik VM (не в виртуальной машине Java). Кодирование на языке Java в рамках Eclipse – интуитивно понятный процесс; Eclipse предоставляет богатую среду Java, включая контекстно-зависимую справку и подсказки к коду. Когда ваш Java-код будет безошибочно скомпилирован, Android Developer Tools сам позаботится о том, чтобы приложение был надлежащим образом упаковано, в том числе снабдит его файлом AndroidManifest.xml.

 

Android-приложение можно написать и без Eclipse и плагина Android Developer Tools, но для этого нужно хорошо разбираться в Android SDK.

 

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

android.jar
Файл архива Java, содержащий все классы SDK Android, необходимые для создания приложений.
documention.html и каталог docs
Документация SDK предоставляется локально и через Интернет. В основном она выполнена в формате Javadocs, что позволяет легко ориентироваться во множестве пакетов SDK. Документация включает также общее руководство по разработке и ссылки на широкое сообщество программистов Android.
Каталог с примерами
Подкаталог с примерами содержит полный исходный код различных приложений, в том числе ApiDemo, который демонстрирует многие API. Примеры приложений – отличная отправная точка для изучения разработки Android-приложений.
Каталог инструментов
Содержит все инструменты командной строки для создания Android-приложений. Наиболее часто используемый и полезный инструмент – это утилита
adb
(Android Debug Bridge).
usb_driver
Каталог, содержащий все необходимые драйверы для подключения среды разработки к поддерживающим Android устройствам, таким, как G1 или разблокированный телефон для разработки Android Dev 1. Эти файлы необходимы только разработчикам, использующим платформу Windows.

Android-приложения могут работать как на реальном устройстве, так и на эмуляторе Android, который прилагается к SDK Android. На рисунке 3 показан главный экран эмулятора Android.


Рисунок 3. Эмулятор Android
Эмулятор Android

Отладочный мост Android

Утилита

adb
поддерживает несколько дополнительных аргументов командной строки, которые обеспечивают мощные функции, такие как копирование файлов в устройство и из него. Аргумент оболочки командной строки позволяет подключаться к самому телефону и подавать простые команды оболочки. Рисунок 4 иллюстрирует команду оболочки
adb
, подаваемую реальному устройству, подключенному к ноутбуку под Windows с помощью кабеля USB.


Рисунок 4. Применение команды оболочки
adb

Применение команды оболочки adb

В рамках этой консоли можно:

  • Отображать конфигурацию сети с несколькими сетевыми соединениями. Обратите внимание на наличие нескольких сетевых соединений:
    • lo
      - это локальное (петлевое) соединение.
    • tiwlan0
      - это соединение WiFi с адресом, предоставленным локальным сервером DHCP.
  • Отображать значение переменной среды
    PATH
    .
  • Выполнять команду
    su
    , чтобы стать суперпользователем.
  • Переходить в каталог /data/app, где хранятся пользовательские приложения.
  • Печатать листинг каталогов, в котором вы увидите одно приложение. Файлы приложений Android на самом деле представляют собой архивы, которые можно просматривать с помощью WinZip или эквивалентной программы. Эти файлы имеют расширение apk.
  • Подавать команду ping, чтобы проверить доступность Google.com.

В этой же среде командной строки можно взаимодействовать с базами данных SQLite, запускать программы и решать многие другие задачи системного уровня. Это довольно примечательная функция с учетом того, что речь идет о подключении к телефону.

 

В следующем разделе мы создадим простое приложение для Android.


Создание простого приложения

Этот раздел содержит краткий обзор процесса создания Android-приложения. Наш пример приложения предельно прост: это несколько видоизмененное приложение «Hello Android». Мы добавим незначительные изменения, чтобы сделать цвет фона экрана белым – тогда телефон можно будет использовать в качестве фонарика. Не очень оригинально, но как пример полезно. Загрузите весь исходный код.

 

Чтобы создать приложение в Eclipse, выберите File > New > Android project, что приведет к запуску мастера нового проекта Android (рисунок 5).


Рисунок 5. Мастер нового проекта Android
Мастер нового проекта Android

Теперь создадим простое приложение с одним действием, а также макет пользовательского интерфейса, который будет храниться в папке main.xml. Макет содержит текстовый элемент, который мы заменим на Android FlashLight (фонарик Android). Этот простой макет приведен в листинге 1.


Листинг 1. Макет Flashlight


<?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"
    android:background="@color/all_white">
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello" android:textColor="@color/all_black" 
   android:gravity="center_horizontal"/>
</LinearLayout>

Создайте пару ресурсов цвета в strings.xml (листинг 2).


Листинг 2. Цвет в strings.xml


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Android FlashLight</string>
    <string name="app_name">FlashLight</string>
    <color name="all_white">#FFFFFF</color>
    <color name="all_black">#000000</color>
</resources>

Цвет фона основного экрана определяется как

all_white
. В файле strings.xml видно, что
all_white
соответствует значению триплета RGB #FFFFFF, что означает чисто белый цвет.

 

Макет содержит одно поле

TextView
, которое на самом деле - просто нередактируемый фрагмент статического текста. Текст будет черным и отцентрованным по горизонтали с помощью атрибута
gravity
.

 

Приложение содержит исходный файл Java с именем FlashLight.java, как показано в листинге 3.


Листинг 3. Flashlight.java


package com.msi.flashlight;
import android.app.Activity;
import android.os.Bundle;
public class FlashLight extends Activity {
    /** Called when the activity is first created. */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Код создается непосредственно в мастере новых проектов:

  • Это часть пакета Java с именем com.msi.flashlight.
  • Он импортирует два класса:
    • класс действия
    • класс пакета
  • При инициировании этого действия вызывается метод
    onCreate
    , который передается в
    savedInstanceState
    . Не сомневайтесь по поводу использования этого пакета для наших целей; он применяется, когда действие приостановлено, а затем возобновляется.
  • Метод
    onCreate
    переопределяет метод класса действия с таким же именем. Он обращается к методу суперкласса
    onCreate
    .
  • Обращение к
    setContentView()
    относится к макету UI, определенному в файле main.xml. Содержимое main.xml и strings.xml, автоматически отображается на константы, определенные в исходном файле R.java. Никогда не редактируйте этот файл напрямую, так как он изменяется после каждой сборки.

Запуск приложения приводит к отображению белого экрана с черным текстом (рисунок 6).


Рисунок 6. Белый экран фонарика
Белый экран фонарика

Настройка файла AndroidManifest.xml для приложения FlashLight показана в листинге 4.


Листинг 4. AndroidManifest.xml для приложения FlashLight


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.msi.flashlight"
      android:versionCode="1"
      android:versionName="1.0.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".FlashLight"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Этот файл создается автоматически плагином Android Developer Tools для Eclipse. Вам не нужно ничего делать.

 

Разумеется, это не очень мощное приложение. Но оно может пригодиться, если вы хотите что-нибудь почитать, не нарушая сна супруги, или если погас свет и надо найти путь к предохранителям в подвале.


Заключение

При чтении этой статьи вы получили самые общие сведения о платформе Android и создали небольшое приложение. Надеюсь, этот пример произвел на вас достаточное впечатление, чтобы захотелось узнать о платформе побольше. Android обещает стать ведущей платформой с открытым исходным кодом, выходящей далеко за пределы рынка сотовых телефонов.



Загрузка

        Исходный код FlashLight (zip, 24 Кб)

Ресурсы

Научиться

  • Оригинал статьи (EN).
     
  • Open Handset Alliance - группа из 47 компаний, специализирующихся на ИТ и мобильной связи, которые поставили перед собой цель ускорить внедрение инноваций в сфере мобильной телефонии и предложить потребителям еще более широкие возможности, меньшие цены и лучшее качество связи. Сообща они разработали Android, первую полную, открытую и бесплатную платформу мобильной телефонии.(EN)
     
  • Сайт для разработчиков Android содержит документацию, загрузки, блоги и многое другое. (EN)
     
  • Подробнее о Dalvik Virtual Machine.(EN)
     
  • Видеоруководства по устройству Dalvik VM на YouTube.(EN)
     
  • Unlocking Android: A Developer's Guide - руководство с практическими рекомендациями по ОС Android и инструментам разработки.(EN)
     
  • Интервью и дискуссии разработчиков ПО в подкастах на developerWorks.(EN)
     
  • Чтобы оставаться в курсе новостей, посещайте раздел технических событий и Web-кастов на developerWorks.(EN)
     
  • Следите за developerWorks по Твиттеру.(EN)
     
  • Следите за предстоящими конференциями, выставками, Web-кастами и другими мероприятиями во всем мире, которые могут заинтересовать разработчиков открытого ПО IBM.(EN)
     
  • Посетите раздел открытого ПО developerWorks, содержащий богатую справочную информацию, инструменты и новости по проектам, которые помогают в разработке технологий с открытым исходным кодом и их использовании с продуктами IBM.(EN)
     
  • Узнавайте о технологиях и возможностях продуктов IBM и open source и учитесь работать с ними с помощью бесплатных демонстраций по требованию на developerWorks.(EN)
     

Получить продукты и технологии

Обсудить

Об авторе

Когда Фрэнк Эйблсон (Frank Ableson) закончил карьеру баскетболиста в команде своего колледжа, не заключив многолетнего контракта с «Лос-Анджедес Лейкерс», он занялся разработкой компьютерных программ. Он любит решать сложные задачи, особенно из области связи и интерфейсов с аппаратурой. Свободное время Фрэнк проводит со своей женой Никки и детьми. С ним можно связаться по адресу: frank@cfgsolutions.com.



Пишем игру для Android. Часть 5. Хранение настроек

 

Пишем игру для Android. Часть 5. Хранение настроек

27.03.2009

Источник: megadarja.blogspot.com



Вот мы и добрались до конца. Осталось сделать только главное меню приложения, а также сделать игре настройки (собственно, меню только для того и нужно, чтобы было откуда настройки вызывать). Ну первое мы еще с прошлой статьи умеем, так что особых сложностей быть не должно. А вот второе следует рассмотреть подробнее. Итак, приступим.

Окно приветствия

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

На нашей форме приветствия будет какая-нибудь картинка и три кнопки: "Начать игру", "Настройки" и "Выход". Картинку в формате png, которую мы назовем

start.png
нужно положить в папку /res/drawable. Названия кнопок нужно вынести в
strings.xml
, добавив следующие строки:

res/values/strings.xml

<resources>
    <string name="app_name">PingPong</string>
    <string name="start_title">Start Game</string>
    <string name="settings_title">Settings</string>
    <string name="exit_title">Exit</string>
</resources>

Тогда разметка для новой формы будет выглядеть так:

res/layout/start.xml

<?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"
    android:gravity="bottom"
    android:background="@drawable/start"
    android:padding="8dip">

    <Button android:id="@+id/StartButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:text="@string/start_title" />

    <Button android:id="@+id/SettingsButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:text="@string/settings_title" />

    <Button android:id="@+id/ExitButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:text="@string/exit_title" />
</LinearLayout>

Фоновое изображение можно задать экрану с помощью полезного свойства

android:background
. Собственно, так можно задавать фон и кнопкам, и вообще чему угодно. Получили вот такую разметку:

Экран приветствия

Добавим соответствующий этой разметке класс

StartScreen.java
. Сразу обработаем нажатия всех кнопок:

StartScreen.java

public class StartScreen extends Activity implements OnClickListener
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.start);

        // Кнопка "Start"
        Button startButton = (Button)findViewById(R.id.StartButton);
        startButton.setOnClickListener(this);

        // Кнопка "Exit"
        Button exitButton = (Button)findViewById(R.id.ExitButton);
        exitButton.setOnClickListener(this);

        // Кнопка "Settings"
        Button settingsButton = (Button)findViewById(R.id.SettingsButton);
        settingsButton.setOnClickListener(this);
    }

    /** Обработка нажатия кнопок */
    public void onClick(View v)
    {
        switch (v.getId())
        {
            case R.id.StartButton:
            {
                Intent intent = new Intent();
                intent.setClass(this, GameScreen.class);
                startActivity(intent);
                break;
            }

            case R.id.SettingsButton:
            {
                break;
            }

            case R.id.ExitButton:
                finish();
                break;

            default:
                break;
        }
    }
}

По нажатию на кнопку Start происходит переход на экран с игрой. Обратите внимание, что

StartScreen
при этом не закрывается. Это значит, что, когда закроется
StartScreen
, мы попадем обратно на экран приветствия. По нажатию на Settings пока что ничего не происходит, по Exit — приложение закрывается.

Осталось только зарегистрировать эту форму в приложении и сделать ее главной. Для этого идем в

AndroidManifest.xml
:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.android.pingpong"
      android:versionCode="1"
      android:versionName="1.0.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".GameScreen"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" /> 
                <category android:name="android.intent.category.SAMPLE_CODE" /> 
            </intent-filter>
        </activity>
        <activity android:name=".StartScreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
</application> </manifest>

Теперь приложение начинается с

StartScreen
, все кнопки работают.

Настройки

Я сделаю в настройках два параметра — максимальное количество очков и сложность. Сложность игры будем менять, варьируя скорость мячика и ракеток.

Сама форма с настройками делается достаточно просто. Есть специальный наследник класса

Activity
PreferenceActivity
, который именно для этого и предназначен. Когда мы хотим сделать форму с настройками, нужно унаследоваться именно от него, и он возьмет на себя большую часть рутины.

Разметка

Разметка для формы с настройками выглядит несколько необычно:

res/xml/preferences.xml

<?xml version="1.0" encoding="UTF-8"?>
<PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android" >
    <PreferenceCategory
        android:title="@string/prefs_title">

        <ListPreference android:key="@string/pref_difficulty"
            android:title="@string/difficulty_title"
            android:entries="@array/difficulty"
            android:entryValues="@array/difficulty"
            android:defaultValue="1"
           />

        <EditTextPreference android:key="@string/pref_max_score"
             android:title="@string/score_title"
             android:defaultValue="10"
             />
    </PreferenceCategory>
</PreferenceScreen>

Настолько необычно, что, если поместить этот XML в папку layout, eclipse начнет ругаться, что не может разрезолвить такие классы. Собственно, это не просто разметка: там также содержатся ключи настроек и значения по умолчанию. Поэтому мы создадим отдельную папку xml, и поместим этот файл туда. А теперь обо всем по порядку.

PreferenceCategory

Ну,

PreferenceScreen
все понятно, а что такое
PreferenceCategory
? Как ни удивительно, это категория настроек. Например, у какой-нибудь игры могут быть настройки графики, настройки звука, настройки сети и т.д.. Удобно отобразить их сгруппированными, вот так:

Пример формы с настройками

А можно обойтись без групп:

PreferenceScreen
уже сам по себе является контейнером для настроек. В нашем случае, например, настроек мало и группировать нечего. Но это я так, для полноты картины.

Какие можно сделать настройки

Как видно даже не прошлой картинке, настройки могут быть разными. И флажки, и текстовые поля, и списки. Все они происходят от одного класса

Preference
, и наследуют от него всякие полезные атрибуты, которые можно задавать в разметке, как то:

android:title
Заголовок настройки или контейнера настроек.
android:summary
Краткое описание. Проще говоря, это то, что пишется под заголовком мелким шрифтом.
android:defaultValue
Значение по умолчанию
android:key
Ключ, с которым данная настройка будет храниться и с которым можно будет к ней обращаться.
android:dependency
Задает зависимость от другого контрола. Например, можно поставить эдитору зависимость от флажка, и тогда, если флажок не установлен, но эдитор будет неактивен.

Ну и еще кое-что. Рассмотрим некоторые конкретные виды настроек.

CheckBoxPreference

Вот такой флажок:

Флажок
EditTextPreference

Редактор текста.

Редактор текста

Честно говоря, у редактора текста очень хотелось бы валидатор, а еще лучше маску. Например, IP-адрес имеет специфический формат, и хотелось бы запрещать пользователю вводить там что попало. Но мне такую функциональность так и не удалось обнаружить.

ListPreference

Своеобразная реализация Dropdown-а. Хотя, на телефоне наверно и вправду так удобнее.

Список

На этом контроле хотелось бы остановиться подробнее. А конкретнее, рассказать, откуда он, собственно, берет элементы списка. А берет он их из ресурсов с помощью таких атрибутов.

android:entries

Здесь хранится ссылка на ресурс, в котором хранятся отображаемые элементы списка. Все значения, которые хочется вынести в ресурсы, хранятся в папке values. До сих пор там была только одна XML-ка —

strings.xml
. Но теперь надо добавить еще одну — arrays.xml. И добавить в узел
resources
следующее:

<string-array name="performance">
    <item>Best performance</item>
    <item>Normal performance and appearance</item>
    <item>Best appearance</item>
</string-array>

После этого можно смело указывать в

android:entries
этот ресурс, список будет загружен.

Кстати говоря, в

item
-ах может быть не непосредственно строка, а ссылка на строку в
strings.xml
. Например, в нашем случае будет так (разумеется, стоит добавить соответствующие значения в
strings.xml
):

res/values/arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="difficulty">
        <item>@string/difficulty_easy</item>
        <item>@string/difficulty_normal</item>
        <item>@string/difficulty_hard</item>
    </string-array>
</resources>
android:entryValues
Список действительных значений. Также ссылка на ресурс, и задавать можно так же. Если кодов будет меньше, чем значений, приложение будет валиться с исключением. Если больше — не будет. В нашем случае можно в entries и entryValues смело задавать одно и то же, но бывает, когда имеет смысл их разделять.

А еще мне никак не удалось победить у

ListPreference
атрибут
adnroid:defaultValue
. Не работает и все.

RingtonePreference

Настройка звукового сигнала. В нашем приложении, например, можно было бы добавить звук, с которым мячик ударяется о стенку, и выбирать этот звук с помощью такого контрола. Выглядит это так:

Рингтон

Класс формы

Класс для формы с настройками будет выглядеть так:

SettingsScreen.java

public class SettingsScreen extends PreferenceActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // Настройки и их разметка загружаются из XML-файла
        addPreferencesFromResource(R.xml.preferences);
    }
}

Кстати, настройки необязательно загружать из XML-ки, можно добавлять все эти настройки прямо в коде конструктора. В сэмплах, которые идут с Android SDK, такие примеры есть.

Добавляем в

StartScreen
код для открытия формы настроек, прописываем
SettingsScreen
в
AndroidManifest.xml
. (Все выглядит точно так же, как и для
GameScreen
, так что листинги не привожу). И увидим мы следующее:

Форма с настройками

Не знаю, кому как, а мне нравится, когда рядом с названием опции написано ее значение. Но как это сделать автоматически, я так и не нашла, пришлось все делать руками, используя для этого поле

summary
. Итак, summary настройки должно обновляться при изменении значения. По счастью, есть такое событие
OnPreferenceChange
. Итак, пишем:

SettingsScreen.java

public class SettingsScreen extends PreferenceActivity
          implements Preference.OnPreferenceChangeListener
{
    /* Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
        ListPreference difficulty = 
                                (ListPreference)this.findPreference("pref_difficulty");
        difficulty.setSummary(difficulty.getEntry());
        difficulty.setOnPreferenceChangeListener(this);

        EditTextPreference maxScore = 
                      (EditTextPreference)this.findPreference("pref_max_score");
        maxScore.setSummary(maxScore.getText());
        maxScore.setOnPreferenceChangeListener(this);
}
public boolean onPreferenceChange(Preference preference, Object newValue) { preference.setSummary((CharSequence)newValue); return true; }
}

Думаю, все понятно без слов. Теперь мы видим такую картину:

Настройки и их значения

И, если мы будем менять настройки, изменения сразу же будут отображаться в

summary
.

Использование настроек в других формах

Ну все, настройки мы сделали, они как-то сами где-то сохранились. Теперь возникла необходимость их прочитать и что-нибудь с ними сделать. Читать и делать мы будем в классе

GameManager
, а конкретно, в конструкторе. Для работы с сохраненными настройками приложения используется класс
SharedPreferences
. Вся работа по чтению и применению настроек выглядит так:

SettingsScreen.java

public GameManager(SurfaceHolder surfaceHolder, Context context)
{
    ...

    // стили для рисования игрового поля
    ...

    // игровые объекты
    ...
    // применение настроек
    SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);

    String difficulty = settings.getString(res.getString(R.string.pref_difficulty),
                                                   res.getString(R.string.difficulty_normal));
    setDifficulty(difficulty);

    int maxScore = Integer.parseInt(settings.getString(
                                          res.getString(R.string.pref_max_score), "10"));
    setMaxScore(maxScore);
}

Метод

setDifficulty
приводить не буду, там ничего особо умного не написано.

Настройки из
SharedPreferences
можно читать с помощью методов
getString
,
getInt
,
getBoolean
и т.п. Все они принимают два параметра &mdahs; ключ к настройке (то, что мы задавали с помощью атрибута
android:key
) и значение по умолчанию. Однако, воспользоваться чем-то кроме
getString

мне так и не удалось.

Заключение

Итак, мануал закончен. Получился он огромным, но при этом собственно про андроид оказалось не так уж и много, что самое-то обидное :( Спасибо, если кто дочитал до конца. Буду рада услышать любые мнения.

Отдельное спасибо xeye и std.denis

А вот и все исходники