<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3333619357391010212</id><updated>2012-02-13T21:52:45.141+08:00</updated><category term='о жизни'/><category term='business'/><category term='tools'/><category term='net'/><category term='src'/><category term='funny'/><category term='patterns'/><category term='books'/><category term='economy'/><category term='boost'/><category term='datamining'/><category term='algo'/><category term='xslt'/><category term='links'/><category term='blog'/><category term='delphi'/><category term='c#'/><category term='C++'/><category term='a17'/><category term='android'/><category term='туризм'/><category term='LinkedIn'/><category term='Named Folders'/><category term='CLI/C++'/><category term='release'/><category term='writing'/><category term='x64'/><category term='hardware'/><category term='science'/><category term='database'/><title type='text'>Блог dv</title><subtitle type='html'>О жизни, о программировании. 
Все публикуемые исходные коды можно взять 
&lt;a href="http://code.google.com/p/dvsrc/"&gt;здесь&lt;/a&gt;</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>69</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-6747884310945947964</id><published>2012-02-01T15:04:00.000+08:00</published><updated>2012-02-04T12:11:56.228+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Тестирование Android приложений</title><content type='html'>Тестирование - тема очень многообразная. Разновидностей тестов масса, методик тестирования тоже, количество разнообразных инструментов просто огромно. Начинающему разобраться не просто. Но за что бороться есть, поскольку грамотная стратегия тестирования существенно улучшает качество приложения.&lt;br/&gt;&lt;br/&gt;Данная статья - это каталогизированный набор ссылок по теме тестирования Android приложений, с краткими аннотациями. Я создал его для того, чтобы самому было удобнее разбираться во всем этом материале и выбирать оптимальную стратегию тестирования собственных Android приложений. Надеюсь, этот набор ссылок пригодится и вам. &lt;a name='more'&gt;&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;С чего начать&lt;/h2&gt;В июне 2011 вышла книга, посвященная тестированию Android-приложений: &lt;a href="http://dtmilano.blogspot.com/2011/06/android-application-testing-guide.html"&gt;Diego Torres Milano. Android Application Testing Guide&lt;/a&gt;. Книга добротная, написана хорошо. &lt;a href="http://dtmilano.blogspot.com/"&gt;Блог автора&lt;/a&gt; так же содержит массу полезной информации.&lt;br/&gt;&lt;br/&gt;Официальная документация по встроенным средствам тестирования &lt;a href="http://developer.android.com/guide/developing/testing/index.html"&gt;Android Developers Guide. Testing&lt;/a&gt; (&lt;a href="http://anddev.ru/33/osnovyi-testirovaniya-android-prilozheniy.html"&gt;перевод&lt;/a&gt;).Из недостатков: Android testing API основано на стиле JUnit 3 и не поддерживает JUnit 4. Тесты запускаются и работают на эмуляторе/девайсе, т.е. запуск медленный.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://habrahabr.ru/blogs/android_development/113584/"&gt;Тестирование Android приложений&lt;/a&gt; - пример разработки небольшого Android-приложения с тестом на базе стандартного фреймворка android.test.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://stackoverflow.com/questions/522312/best-practices-for-unit-testing-android-apps"&gt;Best practices for unit testing Android apps&lt;/a&gt; - обсуждение инструментов тестирования на stackoverflow.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://www.gubatron.com/blog/2010/05/02/how-to-do-unit-testing-on-android-with-eclipse/"&gt;How to do Unit Testing on Android with Eclipse&lt;/a&gt; - видео, демонстрирующее создание тестового проекта в Eclipse.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://habrahabr.ru/blogs/testing/123026/"&gt;Автоматизированное тестирование мобильных приложений&lt;/a&gt; - обзор инструментов для тестирования интерфейса мобильных приложений.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://automated-testing.info/news/anons-serii-kursov-avtomatizacija-mobilnyh-prilozhenij"&gt;Серия курсов - Автоматизация мобильных приложений&lt;/a&gt; (в настоящее время курсов там мало, но анонс многообещающий).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://habrahabr.ru/blogs/android_development/131446/"&gt;Шаблоны проектирования при разработке под Android. Часть 2 — MVP и Unit tests. Путь Джедая&lt;/a&gt;, &lt;a href="http://habrahabr.ru/blogs/android_development/131652/"&gt;Шаблоны проектирования при разработке под Android. Часть 3 — Пользовательский интерфейс, тестирование, AndroidMock&lt;/a&gt; - пара статей по теме тестирования на хабре. Содержание местами спорное, но комментарии, как всегда, интересные.&lt;/br&gt;&lt;br/&gt;&lt;a href="http://www.slideshare.net/dtmilano/testing-on-android"&gt;Testing on android&lt;/a&gt; - весьма интересная презентация с полезными ссылками.&lt;/br&gt;&lt;br/&gt;Серия обзорных статей, посвященных тестированию мобильных приложений: &lt;a href="http://blogs.globallogic.com/mobile-application-testing"&gt;Mobile Application Testing - Part I&lt;/a&gt;, &lt;a href="http://blogs.globallogic.com/mobile-application-testing-part-ii"&gt;part II&lt;/a&gt;, &lt;a href="http://blogs.globallogic.com/mobile-application-testing-part-iii"&gt;part III&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Проблема скорости запуска тестов&lt;/h2&gt;При разработке под Android очень не удобно использовать короткие unit-тесты (&lt;a href="https://sites.google.com/site/androiddevtesting/"&gt;раз статья&lt;/a&gt;, &lt;a href="http://daverog.wordpress.com/2009/12/14/why-android-isnt-ready-for-tdd-and-how-i-tried-anyway/"&gt;два статья&lt;/a&gt;). Дело в том, что стандартные тесты работают только на эмуляторе или на устройстве (т.к. зависят от Android API). Запуск тестов, в этом случае, становится достаточно длительным.&lt;br/&gt;&lt;br/&gt;Для ускорения работы можно выделять Android-независимые тесты в отдельный Java проект и запускать их на JVM компьютера, но это, конечно же, не решение проблемы. Вот дискуссия на stackoverflow: &lt;a href="http://stackoverflow.com/questions/522312/best-practices-for-unit-testing-android-apps"&gt;Best practices for unit testing Android apps&lt;/a&gt;. Основные варианты: использовать библиотеки &lt;a href="http://pivotal.github.com/robolectric/"&gt;Robolectric&lt;/a&gt; и &lt;a href="http://code.google.com/p/robotium/"&gt;Robotium&lt;/a&gt; .&lt;br/&gt;&lt;br/&gt;Библиотека Robolectric действительно решает проблему скорости запуска теста. Тесты запускаются не на эмуляторе/устройстве, а прямо на JVM компьютера, что на порядок быстрее. Robolectric позволяет тестировать большую часть функциональности Android, включая layouts, GUI, сервисы, работу с сетью, виджеты. К тому же, Robolectric использует синтаксис junit4. В то же время следует отдавать себе отчет в том, что Roboelectric эмулирует  Android API. Точность и полнота такой эмуляции, естественно, не 100%.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://brainflush.wordpress.com/2010/01/10/introducing-calculon-a-java-dsl-for-android-activity-testing/"&gt;Introducing Calculon – a Java DSL for Android Activity testing&lt;/a&gt; - библиотека для удобного тестирования Activity.&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Mock-библиотеки под Android&lt;/h2&gt;Про то, что такое mock-объекты и как их использовать, есть статьи на хабре: &lt;a href="http://habrahabr.ru/blogs/java/136466/"&gt;JMock и EasyMock: сравнение и howto в примерах и не только&lt;/a&gt;, &lt;a href="http://habrahabr.ru/blogs/java/72617/"&gt;Глоток МоКито&lt;/a&gt;. Mock-библиотек, работающих под Android, существует несколько: &lt;a href="http://easymock.org/"&gt;EasyMonkey&lt;/a&gt;, &lt;a href="http://code.google.com/p/powermock/"&gt;PowerMock&lt;/a&gt;, &lt;a href="http://code.google.com/p/android-mock/"&gt;Android Mock&lt;/a&gt;, &lt;a href="http://code.google.com/p/mockito/"&gt;Mockito&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Автоматизация тестирования. Многократное воспроизведение записанного теста.&lt;/h2&gt;&lt;a href="http://developer.android.com/guide/developing/tools/monkeyrunner_concepts.html"&gt;MonkeyRunner&lt;/a&gt; - инструмент, входящий в состав Android SDK. С помощью MonkeyRunner можно писать программы на Питоне, способные установить Android-приложение, запустить его, послать ему последовательность нажатий клавиш, сделать и сохранить результирующий скриншот экрана.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://code.google.com/p/robotium/"&gt;Robotium&lt;/a&gt; - фреймворк, дающий возможность разрабатывать тесты "черного ящика" для Android приложений. Тесты пишутся на Java. Для тестирования создается стандартный тестовый проект, в который добавляется библиотека Robotium. Тестовый проект можно запускать как на эмуляторе, так и на девайсе. Robotium использует синтаксис JUnit3. Полезная статья: &lt;a href="http://automated-testing.info/knowledgebase/article/nastrojka-sredy-dlja-razrabotki-android-prilozhenij-i-avtomatizacii-na-robotiu"&gt;Настройка среды для разработки Android приложений и автоматизации на Robotium&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://www.gorillalogic.com/fonemonkey4android"&gt;FoneMonkey for Android&lt;/a&gt; - бесплатный open source инструмент для тестирования rich interface, разработанный компанией Gorilla Logic (см. &lt;a href="http://jaxenter.com/fonemonkey-for-android-q-a-39195.html"&gt;интервью с разработчиком FoneMonkey for Android&lt;/a&gt;, а так же статью в &lt;a href="http://drdobbs.com/open-source/231901614"&gt;DrDobbs&lt;/a&gt;, посвященную FoneMonkey for IOS). Программа умеет записывать высокоуровневые action-based test automation скрипты на Java/Java Script, которые можно редактировать и (при необходимости) писать вручную. &lt;br/&gt;&lt;br/&gt;&lt;a href="http://sikuli.org/"&gt;Sikuli&lt;/a&gt; - еще один бесплатный инструмент для автоматизации тестирования GUI. Особенность - скрипт, задающий последовательность действий, позволяет использовать скриншоты. Чтобы дать команду нажать кнопку, достаточно подставить в скрипт скриншот этой кнопки (используется специальная Sikuli IDE). &lt;a href="http://www.youtube.com/v/FxDOlhysFcM&amp;fs=1&amp;rel=0&amp;hd=1&amp;iframe=true&amp;width=640&amp;height=505"&gt;Видео на youtube&lt;/a&gt; наглядно демонстрирует процесс создания скрипта. А вот пример видео, где sikuli "играет" в &lt;a href="http://sikuli.org/blog/2011/08/15/sikuli-plays-angry-birds-on-google-games/"&gt;Angry birds&lt;/a&gt;. Вот &lt;a href="http://i-miss-erin.blogspot.com/2010/01/automated-test-in-android-by-sikuli.html"&gt;пример&lt;/a&gt; использования sikuli для тестирования Android GUI. Преимущества и недостатки использования Sikuli для тестирования мобильных приложений приведены в &lt;a href="http://www.siliconindia.com/events/siliconindia_events/Softec_Conf_Pune/Vinod_Doshi.pdf"&gt;презентации&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://www.t-plan.com/robot/"&gt;T-PLAN ROBOT (VNCRobot)&lt;/a&gt; - универсальный инструмент для тестирования "черных ящиков" (&lt;a href="http://www.t-plan.com/tplan_robot.html"&gt;видео&lt;/a&gt;, &lt;a href="http://www.t-plan.com/resources/t-planproductinfo/T-Plan_Robot_Mobile_Product_Information.pdf"&gt;mobile testing brochure (pdf)&lt;/a&gt;). Есть &lt;a href="http://www.t-plan.com/robot/docs/versions.html"&gt;платная и бесплатная open-source версия&lt;/a&gt;. &lt;br/&gt;&lt;br/&gt;На самом деле, инструментов, предназначенных для автоматизации тестирования приложений, очень много. Из платных отмечу: &lt;a href="http://www.testplant.com/products/eggplant/"&gt;Eggplants&lt;/a&gt;,&lt;a href="http://www.bsquare.com/android.aspx"&gt;Test Quest&lt;/a&gt;, &lt;a href="http://www.zap-fix.com/"&gt;ZPX&lt;/a&gt;. Есть еще &lt;a href="http://www.jamosolutions.com/Pages/Platforms/Android.html"&gt;Jamo Solutions: M-eux test&lt;/a&gt; и &lt;a href="www.experitest.com/download"&gt;SeeTest&lt;/a&gt; специально заточенные под мобильные приложения.&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Облачные сервисы&lt;/h2&gt;При разработке Android-приложений важнейшая проблема - протестировать работу приложения на различных устройствах. Возможных вариантов устройств сотни, все себе не купишь. А платформо-железозависимые баги, к сожалению, совсем не редкость.&lt;br/&gt;&lt;br/&gt;Специальные сервисы предоставляют доступ к стендам, содержащим множество разнообразных устройств, и позволяют провести тестирование на всех этих устройствах разом. Таких сервисов как минимум три: &lt;a href="https://www.perfectomobile.com/portal/cms/services/automated_testing.html"&gt;Perfecto Mobile&lt;/a&gt;, &lt;a href="http://www.deviceanywhere.com/"&gt;Device Anywhere&lt;/a&gt; и &lt;a href="http://testdroid.com/"&gt;TestDroid&lt;/a&gt;. Вот &lt;a href="http://mobiforge.com/testing/story/testing-physical-devices-made-easy"&gt;здесь&lt;/a&gt; описан принцип работы Device Anywhere и приведены скриншоты. Услуга удобная, но достаточно дорогая.&lt;br/&gt;&lt;br/&gt;TestDroid, кстати, позволяет записывать тесты в формате Robotium. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Сервисы для бета-тестирования&lt;/h2&gt;&lt;a href="http://www.utest.com/"&gt;uTest&lt;/a&gt; - сообщество из 45 тыс профессиональных тестеров из 180 стран. Реальные пользователи протестируют работу вашего приложения. Платный.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://thebetafamily.com/"&gt;The Beta Family&lt;/a&gt; - бесплатный сервис для тестирования приложения. Заводите аккаунт, заливаете бета-версию приложения, рассылаете приглашение на тестирование, обрабатываете результаты тестирования. На главной странице сайта написано, что сервис предназначен для тестирования iPhone/iPad/iPod приложений. Но Android так же поддерживается, о чем прямо написано в &lt;a href="http://thebetafamily.com/faq/for-beta-testers#which-mobile-platforms-do-you-support-for-beta-testing"&gt;FAQ&lt;/a&gt;. Можно выбрать тип бета-тестеров: private или public. Если public, то ваше приложение смогут тестировать все желающие.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://www.appaloosa-store.com/"&gt;Appaloosa&lt;/a&gt; - сервис для приватного распространения приложения (например, среди доверенных бета-тестеров или среди работников вашей компании). Заводите аккаунт, даете доступ вашим знакомым бета-тестерам. После авторизации бета-тестеры получают возможность скачивать приложения, которые вы выложили на Appaloosa. Бесплатен на стадии бета тестирования, потом станет платным.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://hockeykit.net/"&gt;Hockey Kit&lt;/a&gt; - еще один подобный сервис для распространения бета-версий среди своих бета-тестеров.&lt;br/&gt;&lt;br/&gt;&lt;a href="https://zubhium.com/"&gt;Zubhium&lt;/a&gt;. Предоставляет SDK, с помощью которого вы в свое приложение встраиваете код для автоматического сбора информации об ошибках. Выкладываете бету. Ее тестируют  в настоящий момент на стадии беты и поэтому - бесплатен. Бета тестеров нужно приглашать своих.&lt;br/&gt;&lt;br/&gt;&lt;a href="https://www.push-link.com/"&gt;PushLink&lt;/a&gt;. Еще один подобный сервис для приватного распространения приложений (например, бета версий). Добавляете в приложение немного кода для работы с PushLink. Собираете apk и отдаете бета-тестеру. Через некоторое время собираете новую версию apk и заливаете на PushLink. Пользователь автоматически получает уведомление о новой версии и скачивает ее с PushLink. Удобно - нет необходимости рассылать новые версии по email. Сервис бесплатен.&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Monkey и т.д.&lt;/h2&gt;&lt;a href="http://habrahabr.ru/blogs/android_development/131637/"&gt;Инструменты функционального тестирования — Monkey и MonkeyRunner&lt;/a&gt; - обзор двух стандартных инструментов: Monkey (стресс-тестирование) и MonkeyRunner("прокликивающие" тесты, сценарии тестов пишутся на Python).&lt;br/&gt;&lt;br/&gt;&lt;a href="http://dtmilano.blogspot.com/2011/11/android-using-monkey-from-java.html"&gt;Android: Using monkey from Java&lt;/a&gt; - статья в блоге Diego Torres Milano, посвященная стандартной библиотеке chimpchat, эквиваленту monkeyrunner.&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Code coverage&lt;/h2&gt;Code coverage инструменты определяют &lt;a href="http://en.wikipedia.org/wiki/Code_coverage"&gt;степень покрытия кода тестами&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Варианты для Android: &lt;a href="http://emma.sourceforge.net/"&gt;EMMA&lt;/a&gt;, &lt;a href="http://stackoverflow.com/questions/7887870/how-to-get-code-coverage-in-android-using-maven-android-maven-plugin"&gt;Robotium&lt;/a&gt;, &lt;a href="http://mojo.codehaus.org/sonar-maven-plugin/"&gt;Sonar&lt;/a&gt;, включающий Maven и &lt;a href="cobertura.sourceforge.net/"&gt;Cobertura&lt;/a&gt;. Так же функциональость code coverage есть в бесплатном &lt;a href="http://derevyanko.blogspot.com/2012/01/android.html"&gt;статическом анализаторе&lt;/a&gt; от Google - &lt;a href="http://code.google.com/javadevtools/codepro/doc/features/codecoverage/code_coverage.html"&gt;CodePro AnalytiX&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Сбор информации от пользователей&lt;/h2&gt;&lt;a href="http://code.google.com/p/acra/"&gt;Библиотека ACRA&lt;/a&gt; - пишет креш-репорты в документ Google Docs.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://www.bugsense.com/"&gt;BugSense&lt;/a&gt; - real-time bug tracking service. &lt;br/&gt;&lt;br/&gt;&lt;a href="http://derevyanko.blogspot.com/2011/07/android.html"&gt;Системы сбора статистики&lt;/a&gt; по работе приложения.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://code.google.com/p/android-log-collector/"&gt;Log collector&lt;/a&gt; - приложение для генерирования log-файла и отправки его по email. Удобно использовать в своих приложениях для реализации возможности отправки лога.&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Другие инструменты&lt;/h2&gt;&lt;a href="http://code.google.com/p/vogar/wiki/TestHistory"&gt;Vogar&lt;/a&gt; - запуск большого количества тестов с записью истории выполнения тестов.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://code.google.com/p/caliper/"&gt;Caliper&lt;/a&gt; - open-source фреймворк для создания микробенчмарков, их запуска и просмотра результатов.&lt;br/&gt;&lt;br/&gt;&lt;a href="https://github.com/jsankey/android-junit-report"&gt;Android Junit Report&lt;/a&gt; - замена стандартному InstrumentationTestRunner с возможностью генерации XML-отчетов.&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Тестировщику на заметку&lt;/h2&gt;Интересная &lt;a href="http://www.slideshare.net/qaclubkiev/mobile-testing-android-ios-blackberry"&gt;презентация&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Чек-лист для тестирования Android приложения: &lt;a href="http://www.unifiedtestinginitiative.org/Android-UTC"&gt;http://www.unifiedtestinginitiative.org/Android-UTC&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;&lt;a href="http://unifiedtestinginitiative.org/files/uti_best_practices_v1_final.pdf"&gt;Best Practice Guidelines for developing quality mobile applications&lt;/a&gt; &lt;br/&gt;&lt;br/&gt;&lt;a href="http://www.mutualmobile.com/wp-content/uploads/2011/04/ADG1.1.pdf"&gt;Android design guidelines&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Особенности окружающей среды&lt;/h2&gt;&lt;a href="http://www.mobileappstesting.com/2011/09/06/how-to-test-the-3g-or-wi-fi-connection-speed-on-iphone-and-android-smartphones/"&gt;How to test the 3G or Wi-Fi Connection speed on Iphone and Android Smartphones?&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://habrahabr.ru/blogs/android/136154/"&gt;Тестирование поведения приложения в условиях нехватки памяти&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://wanem.sourceforge.net/"&gt;Wide Area Network Emulator&lt;/a&gt; - позволяет разработчику эмулировать проблемы в сети - Network delay, Packet loss, Packet corruption, Disconnections, Packet re-ordering, Jitter и т.д. &lt;br/&gt;&lt;br/&gt;&lt;h2&gt;Методологии&lt;/h2&gt;Test Driven Development, TDD: &lt;a href="http://ru.wikipedia.org/wiki/Разработка_через_тестирование"&gt;Разработка через тестирование&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Behavior Driven Development, BDD:: &lt;a href="http://behaviour-driven.org"&gt;http://behaviour-driven.org&lt;/a&gt;, фреймверк для BDD: &lt;a href="http://jbehave.org"&gt;JBehave&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;Fitness: &lt;a href="www.fitnesse.org"&gt;www.fitnesse.org&lt;/a&gt;, &lt;a href="http://fit.c2.com"&gt;http://fit.c2.com&lt;/a&gt; - совместная работа клиентов, программистов и тестировщиков. &lt;a href="http://code.google.com/p/givwenzen/downloads/list"&gt;GivWenZen&lt;/a&gt; - запись сценариев тестов простым английским языком.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-6747884310945947964?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/6747884310945947964/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2012/02/android.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6747884310945947964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6747884310945947964'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2012/02/android.html' title='Тестирование Android приложений'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-8827549606340667105</id><published>2012-01-29T12:39:00.000+08:00</published><updated>2012-01-29T12:40:11.459+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Список языков с переводом названий (ISO 639-1).</title><content type='html'>Занимаюсь локализацией своего Android приложения &lt;a href="https://market.android.com/details?id=com.mobilityflow.awidget&amp;hl=en"&gt;Animated Widget Contact&lt;/a&gt;. Потребовалось найти список языков, поддерживаемых Android, с переводом названий. Т.е. в виде Ru-Russian-Русский, da-Danish-Dansk и т.д. Как ни странно, готовый список найти не удалось. Поэтому составил такой список самостоятельно, взяв за основу следующие источники: &lt;a href="http://codex.wordpress.org/WordPress_in_Your_Language#Korean_-_.ED.95.9C.EA.B5.AD.EC.96.B4_-_.28ko_KR.29"&gt;1&lt;/a&gt;, &lt;a href="http://api.drupal.ru/api/function/_locale_get_predefined_list/6"&gt;2&lt;/a&gt;, &lt;a href="http://unicode.org/cldr/version/1.3.html"&gt;3&lt;/a&gt;, &lt;a href="http://www.omegawiki.org/Meta:Main_Page/rus"&gt;4&lt;/a&gt;. В список включил только языки имеющие двухбуквенный код по стандарту ISO 639-1, т.к. именно они актуальны для Android приложений. Вот что в итоге получилось.&lt;a name='more'&gt;&lt;/a&gt;&lt;br/&gt;&lt;table border="1"&gt;&lt;tr&gt;&lt;th width="10%"&gt;ISO 639-1&lt;/th&gt;&lt;th width="50%"&gt;Название на английском&lt;/th&gt;&lt;th&gt;Перевод&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;aa&lt;/td&gt;&lt;td&gt;Afar&lt;/td&gt; &lt;TD&gt;Qafar (Afaraf)&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ab&lt;/td&gt;&lt;td&gt;Abkhazian&lt;/td&gt; &lt;TD&gt;аҧсуа бызшәа&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ae&lt;/td&gt;&lt;td&gt;Avestan&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;af&lt;/td&gt;&lt;td&gt;Afrikaans&lt;/td&gt; &lt;TD&gt;Afrikaans&lt;/TD&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ak&lt;/td&gt;&lt;td&gt;Akan&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;am&lt;/td&gt;&lt;td&gt;Amharic&lt;/td&gt; &lt;TD&gt;አማርኛ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;an&lt;/td&gt;&lt;td&gt;Aragonese&lt;/td&gt; &lt;TD&gt;Aragonés&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ar&lt;/td&gt;&lt;td&gt;Arabic&lt;/td&gt; &lt;TD&gt;عربي&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;as&lt;/td&gt;&lt;td&gt;Assamese&lt;/td&gt; &lt;TD&gt;অসমীয়া&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;av&lt;/td&gt;&lt;td&gt;Avaric&lt;/td&gt; &lt;TD&gt;Авар&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ay&lt;/td&gt;&lt;td&gt;Aymara&lt;/td&gt; &lt;TD&gt;Aymar aru&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;az&lt;/td&gt;&lt;td&gt;Azerbaijani&lt;/td&gt; &lt;TD&gt;azərbaycan&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ba&lt;/td&gt;&lt;td&gt;Bashkir&lt;/td&gt; &lt;TD&gt;Башҡортса&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;be&lt;/td&gt;&lt;td&gt;Belarusian&lt;/td&gt; &lt;TD&gt;Беларуская - Biełaruskaja&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;bg&lt;/td&gt;&lt;td&gt;Bulgarian&lt;/td&gt; &lt;TD&gt;Български&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;bh&lt;/td&gt;&lt;td&gt;Bihari languages&lt;/td&gt; &lt;TD&gt;भोजपुरी&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;bi&lt;/td&gt;&lt;td&gt;Bislama&lt;/td&gt; &lt;TD&gt;Bislama&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;bm&lt;/td&gt;&lt;td&gt;Bambara&lt;/td&gt; &lt;TD&gt;Bamanankan&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;bn&lt;/td&gt;&lt;td&gt;Bengali&lt;/td&gt; &lt;TD&gt;বাংলা&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;bo&lt;/td&gt;&lt;td&gt;Tibetan&lt;/td&gt; &lt;TD&gt;བོད་ཡིག&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;br&lt;/td&gt;&lt;td&gt;Breton&lt;/td&gt; &lt;TD&gt;Brezhoneg&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;bs&lt;/td&gt;&lt;td&gt;Bosnian&lt;/td&gt; &lt;TD&gt;Bosanski&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ca&lt;/td&gt;&lt;td&gt;Catalan; Valencian&lt;/td&gt; &lt;TD&gt;Català&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ce&lt;/td&gt;&lt;td&gt;Chechen&lt;/td&gt; &lt;TD&gt;Нохчийн&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ch&lt;/td&gt;&lt;td&gt;Chamorro&lt;/td&gt; &lt;TD&gt;Chamoru&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;co&lt;/td&gt;&lt;td&gt;Corsican&lt;/td&gt; &lt;TD&gt;Corsu&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;cr&lt;/td&gt;&lt;td&gt;Cree&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;cs&lt;/td&gt;&lt;td&gt;Czech&lt;/td&gt; &lt;TD&gt;Čeština&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;cu&lt;/td&gt;&lt;td&gt;Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic&lt;/td&gt; &lt;TD&gt;Словѣ́ньскъ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;cv&lt;/td&gt;&lt;td&gt;Chuvash&lt;/td&gt; &lt;TD&gt;Чӑвашла&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;cy&lt;/td&gt;&lt;td&gt;Welsh&lt;/td&gt; &lt;TD&gt;Cymraeg&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;da&lt;/td&gt;&lt;td&gt;Danish&lt;/td&gt; &lt;TD&gt;Dansk&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;de&lt;/td&gt;&lt;td&gt;German&lt;/td&gt; &lt;TD&gt;Deutsch&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;dv&lt;/td&gt;&lt;td&gt;Divehi; Dhivehi; Maldivian&lt;/td&gt; &lt;TD&gt;ދިވެހިބަސ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;dz&lt;/td&gt;&lt;td&gt;Dzongkha&lt;/td&gt; &lt;TD&gt;རྫོང་ཁ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ee&lt;/td&gt;&lt;td&gt;Ewe&lt;/td&gt; &lt;TD&gt;Eʋegbe&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;el&lt;/td&gt;&lt;td&gt;Greek, Modern (1453-)&lt;/td&gt; &lt;TD&gt;Ελληνικά&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;en&lt;/td&gt;&lt;td&gt;English&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;eo&lt;/td&gt;&lt;td&gt;Esperanto&lt;/td&gt; &lt;TD&gt;Esperanto &lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;es&lt;/td&gt;&lt;td&gt;Spanish; Castilian&lt;/td&gt; &lt;TD&gt;Español&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;et&lt;/td&gt;&lt;td&gt;Estonian&lt;/td&gt; &lt;TD&gt;Eesti&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;eu&lt;/td&gt;&lt;td&gt;Basque&lt;/td&gt; &lt;TD&gt;Euskera&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fa&lt;/td&gt;&lt;td&gt;Persian&lt;/td&gt; &lt;TD&gt;پارسی &lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ff&lt;/td&gt;&lt;td&gt;Fulah&lt;/td&gt; &lt;TD&gt;Fulfulde&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fi&lt;/td&gt;&lt;td&gt;Finnish&lt;/td&gt; &lt;TD&gt;Suomi&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fj&lt;/td&gt;&lt;td&gt;Fijian&lt;/td&gt; &lt;TD&gt;Na Vosa Vakaviti&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fo&lt;/td&gt;&lt;td&gt;Faroese&lt;/td&gt; &lt;TD&gt;føroyskt&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fr&lt;/td&gt;&lt;td&gt;French&lt;/td&gt; &lt;TD&gt;Français&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fy&lt;/td&gt;&lt;td&gt;Western Frisian&lt;/td&gt; &lt;TD&gt;Frysk&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ga&lt;/td&gt;&lt;td&gt;Irish&lt;/td&gt; &lt;TD&gt;Gaeilge&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;gd&lt;/td&gt;&lt;td&gt;Gaelic; Scottish Gaelic&lt;/td&gt; &lt;TD&gt;Gàidhlig&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;gl&lt;/td&gt;&lt;td&gt;Galician&lt;/td&gt; &lt;TD&gt;Galego&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;gn&lt;/td&gt;&lt;td&gt;Guarani&lt;/td&gt; &lt;TD&gt;Avañe'ẽ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;gu&lt;/td&gt;&lt;td&gt;Gujarati&lt;/td&gt; &lt;TD&gt;ગુજરાતી&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;gv&lt;/td&gt;&lt;td&gt;Manx&lt;/td&gt; &lt;TD&gt;Gaelg&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ha&lt;/td&gt;&lt;td&gt;Hausa&lt;/td&gt; &lt;TD&gt;هَوُسَ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;he&lt;/td&gt;&lt;td&gt;Hebrew&lt;/td&gt; &lt;TD&gt;עברית&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;hi&lt;/td&gt;&lt;td&gt;Hindi&lt;/td&gt; &lt;TD&gt;हिन्दी&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ho&lt;/td&gt;&lt;td&gt;Hiri Motu&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;hr&lt;/td&gt;&lt;td&gt;Croatian&lt;/td&gt; &lt;TD&gt;Hrvatski&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ht&lt;/td&gt;&lt;td&gt;Haitian; Haitian Creole&lt;/td&gt; &lt;TD&gt;Kreyòl ayisyen&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;hu&lt;/td&gt;&lt;td&gt;Hungarian&lt;/td&gt; &lt;TD&gt;Magyar&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;hy&lt;/td&gt;&lt;td&gt;Armenian&lt;/td&gt; &lt;TD&gt;Հայերեն&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;hz&lt;/td&gt;&lt;td&gt;Herero&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ia&lt;/td&gt;&lt;td&gt;Interlingua (International Auxiliary Language Association)&lt;/td&gt; &lt;TD&gt;Interlingua&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;id&lt;/td&gt;&lt;td&gt;Indonesian&lt;/td&gt; &lt;TD&gt;Bahasa Indonesia&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ie&lt;/td&gt;&lt;td&gt;Interlingue; Occidental&lt;/td&gt; &lt;TD&gt;Interlingue&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ig&lt;/td&gt;&lt;td&gt;Igbo&lt;/td&gt; &lt;TD&gt;Igbo&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ii&lt;/td&gt;&lt;td&gt;Sichuan Yi; Nuosu&lt;/td&gt; &lt;TD&gt;ꆇꉙ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ik&lt;/td&gt;&lt;td&gt;Inupiaq&lt;/td&gt; &lt;TD&gt;Iñupiak&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;io&lt;/td&gt;&lt;td&gt;Ido&lt;/td&gt; &lt;TD&gt;Ido&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;is&lt;/td&gt;&lt;td&gt;Icelandic&lt;/td&gt; &lt;TD&gt;Icelandic&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;it&lt;/td&gt;&lt;td&gt;Italian&lt;/td&gt; &lt;TD&gt;Italian&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;iu&lt;/td&gt;&lt;td&gt;Inuktitut&lt;/td&gt; &lt;TD&gt;ᐃᓄᒃᑎᑐᑦ/inuktitut&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ja&lt;/td&gt;&lt;td&gt;Japanese&lt;/td&gt; &lt;TD&gt;日本語&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;jv&lt;/td&gt;&lt;td&gt;Javanese&lt;/td&gt; &lt;TD&gt;Basa Jawa&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ka&lt;/td&gt;&lt;td&gt;Georgian&lt;/td&gt; &lt;TD&gt;ქართული&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;kg&lt;/td&gt;&lt;td&gt;Kongo&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ki&lt;/td&gt;&lt;td&gt;Kikuyu; Gikuyu&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;kj&lt;/td&gt;&lt;td&gt;Kuanyama; Kwanyama&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;kk&lt;/td&gt;&lt;td&gt;Kazakh&lt;/td&gt; &lt;TD&gt;Қазақ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;kl&lt;/td&gt;&lt;td&gt;Kalaallisut; Greenlandic&lt;/td&gt; &lt;TD&gt;Kalaallisut&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;km&lt;/td&gt;&lt;td&gt;Central Khmer&lt;/td&gt; &lt;TD&gt;ខ្មែរ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;kn&lt;/td&gt;&lt;td&gt;Kannada&lt;/td&gt; &lt;TD&gt;ಕನ್ನಡ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ko&lt;/td&gt;&lt;td&gt;Korean&lt;/td&gt; &lt;TD&gt;한국어&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;kr&lt;/td&gt;&lt;td&gt;Kanuri&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ks&lt;/td&gt;&lt;td&gt;Kashmiri&lt;/td&gt; &lt;TD&gt;कश्मीरी - (كشميري)&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ku&lt;/td&gt;&lt;td&gt;Kurdish&lt;/td&gt; &lt;TD&gt; وۆردپرێس بەکوردی&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;kv&lt;/td&gt;&lt;td&gt;Komi&lt;/td&gt; &lt;TD&gt;Коми&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;kw&lt;/td&gt;&lt;td&gt;Cornish&lt;/td&gt; &lt;TD&gt;kernewek&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ky&lt;/td&gt;&lt;td&gt;Kirghiz; Kyrgyz&lt;/td&gt; &lt;TD&gt;Кыргыз&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;la&lt;/td&gt;&lt;td&gt;Latin&lt;/td&gt; &lt;TD&gt;Latina&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;lb&lt;/td&gt;&lt;td&gt;Luxembourgish; Letzeburgesch&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;lg&lt;/td&gt;&lt;td&gt;Ganda&lt;/td&gt; &lt;TD&gt;Luganda&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;li&lt;/td&gt;&lt;td&gt;Limburgan; Limburger; Limburgish&lt;/td&gt; &lt;TD&gt;Limburgs&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ln&lt;/td&gt;&lt;td&gt;Lingala&lt;/td&gt; &lt;TD&gt;Lingála&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;lo&lt;/td&gt;&lt;td&gt;Lao&lt;/td&gt; &lt;TD&gt;ລາວ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;lt&lt;/td&gt;&lt;td&gt;Lithuanian&lt;/td&gt; &lt;TD&gt;Lietuvių&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;lu&lt;/td&gt;&lt;td&gt;Luba-Katanga&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;lv&lt;/td&gt;&lt;td&gt;Latvian&lt;/td&gt; &lt;TD&gt;Latviešu&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;mg&lt;/td&gt;&lt;td&gt;Malagasy&lt;/td&gt; &lt;TD&gt;Malagasy&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;mh&lt;/td&gt;&lt;td&gt;Marshallese&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;mi&lt;/td&gt;&lt;td&gt;Maori&lt;/td&gt; &lt;TD&gt;Māori&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;mk&lt;/td&gt;&lt;td&gt;Macedonian&lt;/td&gt; &lt;TD&gt;Македонски&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ml&lt;/td&gt;&lt;td&gt;Malayalam&lt;/td&gt; &lt;TD&gt;മലയാളം&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;mn&lt;/td&gt;&lt;td&gt;Mongolian&lt;/td&gt; &lt;TD&gt;Монгол хэл&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;mr&lt;/td&gt;&lt;td&gt;Marathi&lt;/td&gt; &lt;TD&gt;मराठी&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ms&lt;/td&gt;&lt;td&gt;Malay&lt;/td&gt; &lt;TD&gt;Bahasa Melayu&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;mt&lt;/td&gt;&lt;td&gt;Maltese&lt;/td&gt; &lt;TD&gt;Malt&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;my&lt;/td&gt;&lt;td&gt;Burmese&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;na&lt;/td&gt;&lt;td&gt;Nauru&lt;/td&gt; &lt;TD&gt;Dorerin Naoero&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nb&lt;/td&gt;&lt;td&gt;Bokmal, Norwegian; Norwegian Bokmal&lt;/td&gt; &lt;TD&gt;Bokmål&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nd&lt;/td&gt;&lt;td&gt;Ndebele, North; North Ndebele&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ne&lt;/td&gt;&lt;td&gt;Nepali&lt;/td&gt; &lt;TD&gt;नेपाली&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ng&lt;/td&gt;&lt;td&gt;Ndonga&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nl&lt;/td&gt;&lt;td&gt;Dutch; Flemish&lt;/td&gt; &lt;TD&gt;Nederlands&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nn&lt;/td&gt;&lt;td&gt;Norwegian Nynorsk; Nynorsk, Norwegian&lt;/td&gt; &lt;TD&gt;Nynorsk&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;no&lt;/td&gt;&lt;td&gt;Norwegian&lt;/td&gt; &lt;TD&gt;Norsk (bokmål)‬&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nr&lt;/td&gt;&lt;td&gt;Ndebele, South; South Ndebele&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nv&lt;/td&gt;&lt;td&gt;Navajo; Navaho&lt;/td&gt; &lt;TD&gt;Diné bizaad&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ny&lt;/td&gt;&lt;td&gt;Chichewa; Chewa; Nyanja&lt;/td&gt; &lt;TD&gt;Chi-Chewa&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;oc&lt;/td&gt;&lt;td&gt;Occitan&lt;/td&gt; &lt;TD&gt;Occitan&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;oj&lt;/td&gt;&lt;td&gt;Ojibwa&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;om&lt;/td&gt;&lt;td&gt;Oromo&lt;/td&gt; &lt;TD&gt;Oromoo&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;or&lt;/td&gt;&lt;td&gt;Oriya&lt;/td&gt; &lt;TD&gt;ଓଡ଼ିଆ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;os&lt;/td&gt;&lt;td&gt;Ossetian; Ossetic&lt;/td&gt; &lt;TD&gt;Ирон&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;pa&lt;/td&gt;&lt;td&gt;Panjabi; Punjabi&lt;/td&gt; &lt;TD&gt;ਪੰਜਾਬੀ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;pi&lt;/td&gt;&lt;td&gt;Pali&lt;/td&gt; &lt;TD&gt;पािऴ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;pl&lt;/td&gt;&lt;td&gt;Polish&lt;/td&gt; &lt;TD&gt;Polski&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ps&lt;/td&gt;&lt;td&gt;Pushto; Pashto&lt;/td&gt; &lt;TD&gt;پښتو&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;pt&lt;/td&gt;&lt;td&gt;Portuguese&lt;/td&gt; &lt;TD&gt;Português&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;qu&lt;/td&gt;&lt;td&gt;Quechua&lt;/td&gt; &lt;TD&gt;Runa Simi&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rm&lt;/td&gt;&lt;td&gt;Romansh&lt;/td&gt; &lt;TD&gt;Rumantsch&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rn&lt;/td&gt;&lt;td&gt;Rundi&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ro&lt;/td&gt;&lt;td&gt;Romanian; Moldavian; Moldovan&lt;/td&gt; &lt;TD&gt;Română&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ru&lt;/td&gt;&lt;td&gt;Russian&lt;/td&gt; &lt;TD&gt;Русский&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rw&lt;/td&gt;&lt;td&gt;Kinyarwanda&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sa&lt;/td&gt;&lt;td&gt;Sanskrit&lt;/td&gt; &lt;TD&gt;संस्कृत&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sc&lt;/td&gt;&lt;td&gt;Sardinian&lt;/td&gt; &lt;TD&gt;Sardu&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sd&lt;/td&gt;&lt;td&gt;Sindhi&lt;/td&gt; &lt;TD&gt;سنڌي&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;se&lt;/td&gt;&lt;td&gt;Northern Sami&lt;/td&gt; &lt;TD&gt;Sámegiella&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sg&lt;/td&gt;&lt;td&gt;Sango&lt;/td&gt; &lt;TD&gt;Sängö&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;si&lt;/td&gt;&lt;td&gt;Sinhala; Sinhalese&lt;/td&gt; &lt;TD&gt;සිංහල&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sk&lt;/td&gt;&lt;td&gt;Slovak&lt;/td&gt; &lt;TD&gt;Slovenčina&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sl&lt;/td&gt;&lt;td&gt;Slovenian&lt;/td&gt; &lt;TD&gt;Slovenščina&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sm&lt;/td&gt;&lt;td&gt;Samoan&lt;/td&gt; &lt;TD&gt;Gagana Samoa&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sn&lt;/td&gt;&lt;td&gt;Shona&lt;/td&gt; &lt;TD&gt;chiShona&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;so&lt;/td&gt;&lt;td&gt;Somali&lt;/td&gt; &lt;TD&gt;Soomaali&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sq&lt;/td&gt;&lt;td&gt;Albanian&lt;/td&gt; &lt;TD&gt;Shqip&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sr&lt;/td&gt;&lt;td&gt;Serbian&lt;/td&gt; &lt;TD&gt;Српски&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ss&lt;/td&gt;&lt;td&gt;Swati&lt;/td&gt; &lt;TD&gt;SiSwati&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;st&lt;/td&gt;&lt;td&gt;Sotho, Southern&lt;/td&gt; &lt;TD&gt;Sesotho&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;su&lt;/td&gt;&lt;td&gt;Sundanese&lt;/td&gt; &lt;TD&gt;Basa Sunda&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sv&lt;/td&gt;&lt;td&gt;Swedish&lt;/td&gt; &lt;TD&gt;Svenska&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sw&lt;/td&gt;&lt;td&gt;Swahili&lt;/td&gt; &lt;TD&gt;Kiswahili&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ta&lt;/td&gt;&lt;td&gt;Tamil&lt;/td&gt; &lt;TD&gt;தமிழ்&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;te&lt;/td&gt;&lt;td&gt;Telugu&lt;/td&gt; &lt;TD&gt;తెలుగు&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tg&lt;/td&gt;&lt;td&gt;Tajik&lt;/td&gt; &lt;TD&gt;Tajik&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;th&lt;/td&gt;&lt;td&gt;Thai&lt;/td&gt; &lt;TD&gt;ภาษาไทย&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ti&lt;/td&gt;&lt;td&gt;Tigrinya&lt;/td&gt; &lt;TD&gt;ትግርኛ&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tk&lt;/td&gt;&lt;td&gt;Turkmen&lt;/td&gt; &lt;TD&gt;Türkmençe&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tl&lt;/td&gt;&lt;td&gt;Tagalog&lt;/td&gt; &lt;TD&gt;Tagalog&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tn&lt;/td&gt;&lt;td&gt;Tswana&lt;/td&gt; &lt;TD&gt;Setswana&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;to&lt;/td&gt;&lt;td&gt;Tonga (Tonga Islands)&lt;/td&gt; &lt;TD&gt;lea faka-Tonga&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tr&lt;/td&gt;&lt;td&gt;Turkish&lt;/td&gt; &lt;TD&gt;Türkçe&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ts&lt;/td&gt;&lt;td&gt;Tsonga&lt;/td&gt; &lt;TD&gt;Xitsonga&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tt&lt;/td&gt;&lt;td&gt;Tatar&lt;/td&gt; &lt;TD&gt;Tatarça&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tw&lt;/td&gt;&lt;td&gt;Twi&lt;/td&gt; &lt;TD&gt;&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ty&lt;/td&gt;&lt;td&gt;Tahitian&lt;/td&gt; &lt;TD&gt;Reo Mā`ohi&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ug&lt;/td&gt;&lt;td&gt;Uighur; Uyghur&lt;/td&gt; &lt;TD&gt;ئۇيغۇرچە&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;uk&lt;/td&gt;&lt;td&gt;Ukrainian&lt;/td&gt; &lt;TD&gt;Українська&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ur&lt;/td&gt;&lt;td&gt;Urdu&lt;/td&gt; &lt;TD&gt;اردو&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;uz&lt;/td&gt;&lt;td&gt;Uzbek&lt;/td&gt; &lt;TD&gt;O‘zbekcha&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ve&lt;/td&gt;&lt;td&gt;Venda&lt;/td&gt; &lt;TD&gt;Tshivenda&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;vi&lt;/td&gt;&lt;td&gt;Vietnamese&lt;/td&gt; &lt;TD&gt;Tiếng Việt&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;vo&lt;/td&gt;&lt;td&gt;Volapuk&lt;/td&gt; &lt;TD&gt;Volapük&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;wa&lt;/td&gt;&lt;td&gt;Walloon&lt;/td&gt; &lt;TD&gt;Walon&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;wo&lt;/td&gt;&lt;td&gt;Wolof&lt;/td&gt; &lt;TD&gt;Wolof&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;xh&lt;/td&gt;&lt;td&gt;Xhosa&lt;/td&gt; &lt;TD&gt;isiXhosa&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;yi&lt;/td&gt;&lt;td&gt;Yiddish&lt;/td&gt; &lt;TD&gt;ייִדיש&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;yo&lt;/td&gt;&lt;td&gt;Yoruba&lt;/td&gt; &lt;TD&gt;Yorùbá&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;za&lt;/td&gt;&lt;td&gt;Zhuang; Chuang&lt;/td&gt; &lt;TD&gt;Vahcuengh&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;zh&lt;/td&gt;&lt;td&gt;Chinese&lt;/td&gt; &lt;TD&gt;中文 (zh_CN); 香港 (zh_HK); 台灣 (zh_TW)&lt;/TD&gt; &lt;/tr&gt;&lt;tr&gt;&lt;td&gt;zu&lt;/td&gt;&lt;td&gt;Zulu&lt;/td&gt; &lt;TD&gt;isiZulu&lt;/TD&gt; &lt;/tr&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-8827549606340667105?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/8827549606340667105/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2012/01/iso-639-1.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8827549606340667105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8827549606340667105'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2012/01/iso-639-1.html' title='Список языков с переводом названий (ISO 639-1).'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-6081940598694855942</id><published>2012-01-16T11:54:00.001+08:00</published><updated>2012-01-17T14:16:03.346+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='business'/><title type='text'>Сдача налоговой отчетности ИП за 2011 год</title><content type='html'>ИП на УСН без работников сдает за 2011 год в налоговую &lt;a href="http://www.nalogservice.ru/faq/document34366.htm"&gt;следующую отчетность:&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Сведения о среднесписочной численности работников за предшествующий календарный го (&lt;b&gt;не позднее 20 января&lt;/b&gt;)&lt;/li&gt;&lt;li&gt;Книга учета доходов и расходов организаций и индивидуальных предпринимателей, применяющих УСН (не позднее 31 марта) &lt;/li&gt;&lt;li&gt;Налоговая декларация по налогу, уплачиваемому в связи с применением УСН (не позднее 31 марта)&lt;/li&gt;&lt;/ul&gt;Рассмотрим, как эту отчетность можно подготовить.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;h4&gt;Софт&lt;/h4&gt;Скачиваем &lt;a href="http://www.gnivc.ru/software/free_software/software_ul_fl/"&gt;свежую версию программы Налогоплательщик ЮЛ&lt;/a&gt; (в настоящий момент, это версия &lt;a href="http://www.gnivc.ru/software/free_software/software_ul_fl/taxpayer_ul/722/"&gt;4.28&lt;/a&gt; + &lt;a href="http://www.gnivc.ru/software/free_software/software_ul_fl/taxpayer_ul/731/"&gt;обновления&lt;/a&gt;. Устанавливаем ее. &lt;br /&gt;&lt;br /&gt;Рекомендую устанавливать Налогоплательщик ЮЛ на виртуальной машине. Эта программа нужна один раз в год, смысла держать ее у себя на компьютере нет никакого. Программа поддерживает экспорт/импорт личных данных (&lt;code&gt;Сервис\Сохранение информации&lt;/code&gt;, &lt;code&gt;Сервис\Восстановление информации&lt;/code&gt;), так что: установил программу, загрузил в нее личные данные из бакапа, подготовил отчетность, выгрузил обновленные личные данные в бакап, снес программу. Виртуалка тут подходит идеально.&lt;br /&gt;&lt;br /&gt;Документы потребуется распечатать. На виртуальной машине проще всего печатать в PDF. Для этого нужно на виртуалке дополнительно установить &lt;a href="http://sourceforge.net/projects/pdfcreator/"&gt;PDFCreator&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Подготовка&lt;/h4&gt;Загрузили данные из бакапа или ввели данные о своем ИП впервые. Теперь надо выбрать отчетный период - год 2011.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Сведения о среднесписочной численности работников&lt;/h4&gt;Добраться до этого документа в Налогоплательщик ЮЛ можно следующим образом.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Документы\Налоговая отчетность&lt;/li&gt;&lt;li&gt;Добавить&lt;/li&gt;&lt;li&gt;Выбираем первый документ - КНД 1110018, Сведения о среднесписочной численности работников за предшествующий календарный год.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-DjtuxsnUSVQ/TxN7TUyxnlI/AAAAAAAAAHg/3nxs1HYf_4k/s1600/doc1.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="236" width="320" src="http://4.bp.blogspot.com/-DjtuxsnUSVQ/TxN7TUyxnlI/AAAAAAAAAHg/3nxs1HYf_4k/s320/doc1.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Выбираем, заполняем. Заполнить нужно два поля: дату (1 января 2012) и количество работников (если ИП без работников, ставим 0).&lt;br /&gt;&lt;br /&gt;Подписываем, распечатываем в двух экземплярах, экспортируем в XML файл, сохраняем его на флешку.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Декларация&lt;/h4&gt;В Налогоплательщик ЮЛ:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Документы\Налоговая отчетность&lt;/li&gt;&lt;li&gt;Добавить&lt;/li&gt;&lt;li&gt;Выбираем документ - КНД 1152017, Декларация по налогу, уплачиваемому в связи с применением упрощенной системы налогообложения.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Заполняем декларацию, распечатываем в двух экземплярах, экспортируем в XML файл, сохраняем его на флешку (налоговая требует XML). Ответы на вопросы по заполнению декларации лучше всего искать на &lt;a href="http://www.klerk.ru/"&gt;klerk.ru&lt;/a&gt;, народ на форуме там грамотный. Пример заполнения приведен &lt;a href="http://www.rnk.ru/journal/archives/2010/4/nalogovoe_administrirovanie/nalogooblozhenie_malogo_biznesa/po_itogam129892.phtml"&gt;здесь.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Книга доходов и расходов&lt;/h4&gt;Книги доходов и расходов (КДР) в Налогоплательщик ЮЛ нет. Поэтому берем &lt;a href="http://www.nalogservice.ru/faq/document28480.htm"&gt;бланк&lt;/a&gt; и заполняем ее в Excel. &lt;br /&gt;&lt;br /&gt;Порядок заполнения КДР рассмотрен &lt;a href="http://www.delo-press.ru/articles.php?n=7279"&gt;здесь&lt;/a&gt;. 5 лист нужен только тем, у кого в качестве объекта налогообложения выбраны "доходы, уменьшенные на величину расходов". 4 лист нужен тем, кто уплачивает &lt;a href="http://ru.wikipedia.org/wiki/Единый_налог_на_вменённый_доход"&gt;единый налог&lt;/a&gt;. Остальным достаточно заполнить первые три листа.&lt;br /&gt;&lt;br /&gt;Книгу доходов и расходов распечатываем в двух экземплярах. Согласно законодательству, каждый экземпляр должен быть прошит и опечатан. Протыкаем в КДР три дырки, на расстоянии ~ 1 см от левого края. Сшиваем ниткой, концы нитки выводим на обратную сторону последнего листа КДР. Заклеиваем концы нитки бумажкой, на которой пишем: "Прошито, пронумеровано, скреплено печатью 3 (три) листа". Подпись, ФИО, дата. Естественно, если печати нет, то "скреплено печатью" писать не нужно.  Вот &lt;a href="http://www.ippnou.ru/images/article/0701/2922_6.gif"&gt;пример опечатанного документа&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Идем в налоговую&lt;/h4&gt;Берем с собой:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Два экземпляра сведений о среднесписочной численности + &lt;b&gt;электронная версия в виде XML на флешке&lt;/b&gt;.&lt;/li&gt;&lt;li&gt;Два экземпляра декларации + электронная версия в виде XML файла на флешке.&lt;/li&gt;&lt;li&gt;Два прошитых и опечатанных экземпляра КДР.&lt;/li&gt;&lt;/ul&gt;Один экземпляр каждого документа отдаем в налоговую, один заверяем в налоговой и оставляем себе.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-6081940598694855942?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/6081940598694855942/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2012/01/2011.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6081940598694855942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6081940598694855942'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2012/01/2011.html' title='Сдача налоговой отчетности ИП за 2011 год'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-DjtuxsnUSVQ/TxN7TUyxnlI/AAAAAAAAAHg/3nxs1HYf_4k/s72-c/doc1.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-2901442823884408321</id><published>2012-01-06T14:55:00.008+08:00</published><updated>2012-01-16T18:34:18.511+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Статические анализаторы кода для Android-приложения.</title><content type='html'>Одним из путей улучшения качества кода приложения является &lt;a href="http://habrahabr.ru/blogs/code_review/135234/#habracut"&gt;регулярное&lt;/a&gt; применение статических анализаторов кода. Статический анализ кода позволяет находить ошибки в коде, отслеживать несоблюдение стандарта кодирования, находить "плохой" код, который может привести к проблемам в будущем, а так же неэффективный код, негативно сказывающийся на производительности в настоящем, строить метрики кода, находить полностью или частично одинаковые фрагменты кода и так далее. &lt;br /&gt;&lt;br /&gt;Для разработчиков, ведущих кодирование в одиночку, статические анализаторы вещь вообще неоценимая. Ведь им некому показать код своего приложения. Так что "электронный помощник", способный грамотно выполнить "code review", может им здорово пригодится.&lt;br /&gt;&lt;br /&gt;Вопрос - какие статические анализаторы кода доступны разработчику Android приложений? Для Java статических анализаторов существует множество (см. список &lt;a href="http://en.wikipedia.org/wiki/Lint_(software)"&gt;lint&lt;/a&gt; приложений для Java в википедии), но не все они умеют работать с Android-приложениями и, тем более, учитывать их специфику.&lt;br /&gt;&lt;br /&gt;Мне удалось отыскать следующие статические анализаторы кода, которые умеют работать с Android-приложениями:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://tools.android.com/tips/lint"&gt;Lint&lt;/a&gt;. "Родной" анализатор кода для Android, входит в состав Android SDK начиная с r16.&lt;/li&gt;&lt;li&gt;&lt;a href="http://findbugs.sourceforge.net/"&gt;FindBugs&lt;/a&gt; (бесплатный). &lt;/li&gt;&lt;li&gt;&lt;a href="http://checkstyle.sourceforge.net/"&gt;Checkstyle&lt;/a&gt; (бесплатный). &lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/intl/ru-RU/javadevtools/codepro/doc/index.html"&gt;CodePro Analytix&lt;/a&gt; (бесплатный). &lt;/li&gt;&lt;li&gt;&lt;a href="http://pmd.sourceforge.net/"&gt;PMD&lt;/a&gt; (бесплатный)&lt;/li&gt;&lt;li&gt;Motodev App Validator (бесплатный). Входит в состав среды разработки &lt;a href="http://developer.motorola.com/docstools/motodevstudio/"&gt;MOTODEV Studio for Android&lt;/a&gt;. Есть &lt;a href="http://developer.motorola.com/testing/app-validator/"&gt;online&lt;/a&gt; вариант.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.klocwork.com/products/solo/"&gt;Klocwork Solo&lt;/a&gt; (платный). Доступна триальная версия&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.parasoft.com/jsp/products/jtest.jsp"&gt;JTest&lt;/a&gt; от Parasoft (платный). &lt;/li&gt;&lt;li&gt;&lt;a href="http://juliasoft.com/"&gt;Julia&lt;/a&gt; (платный). Доступен бесплатный &lt;a href="http://julia.scienze.univr.it/"&gt;online&lt;/a&gt; вариант.&lt;/li&gt;&lt;/ul&gt;Далее представлены результаты сравнения всех этих анализаторов - какой анализатор что умеет, для чего предназначен, насколько удобно пользоваться, какие ошибки находит, насколько он полезен на практике. &lt;br /&gt;&lt;br /&gt;Чтобы было интереснее сравнивать, я "натравил" каждый из этих анализаторов на одно и то же тестовое приложение. В качестве такового я взял код одной из ранних версий моего приложения &lt;a href="https://market.android.com/details?id=com.mobilityflow.awidget&amp;hl=ru"&gt;Animated Widget Contact Launcher&lt;/a&gt;. Версию я выбрал годичной давности, багов и проблем в ней было вагон (с FindBugs на тот момент я был не знаком). Так что "что поискать" в ней точно было.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Lint&lt;/h3&gt;&lt;a href="http://tools.android.com/tips/lint"&gt;Lint&lt;/a&gt; - анализатор кода, который поставляется вместе с Android SDK. Он появился совсем недавно, в версии r16. &lt;br /&gt;&lt;br /&gt;Lint нацелен на поиск проблем, связанных с ресурсами. Неиспользуемые ресурсы, ненужные или наоборот, отсутствующие, аттрибуты, неоптимальные конструкции в layout и т.д. Список проблем, которые ищет Lint, приведен &lt;a href="http://tools.android.com/tips/lint-checks"&gt;здесь&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Работать с Lint очень удобно. Вызывать и настраивать его можно прямо из Eclipse. Для найденных ошибок показывается объяснение - в чем состоит ошибка и как ее исправить. Ошибки делятся на 3 уровня (error, warning, information). &lt;br /&gt;&lt;br /&gt;При проверке тестового приложения был выдан 101 warning. Все по существу - ложных срабатываний не было, за исключением пары сообщений об неиспользуемости ресурсов (и это при отсутствии библиотек; если же библиотеки используются, то разработчики прямо предупреждают о возможности ложных сообщений, так что будьте осторожны при чистке кода).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-UJ2JPXSVb9s/TwVtraTmfMI/AAAAAAAAAFQ/R32F2qPO1uk/s1600/lint2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="252" width="320" src="http://3.bp.blogspot.com/-UJ2JPXSVb9s/TwVtraTmfMI/AAAAAAAAAFQ/R32F2qPO1uk/s320/lint2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;FindBugs&lt;/h3&gt;FindBugs я пользуюсь уже почти год и очень доволен. Находит реальные ошибки в коде. Вот полный &lt;a href="http://findbugs.sourceforge.net/bugDescriptions.html"&gt;список проблем&lt;/a&gt;, которые он умеет находить.&lt;br /&gt;&lt;br /&gt;В декабре 2011, после длительного перерыва, FindBugs обновился с версии 1.3.9 до 2.0. Субъективно новая версия стала точнее, удобнее и быстрее предыдущей. Настраивается она теперь прямо из Eclipse (важно: в свойствах проекта, а не в свойствах workspace). Плагин для eclipse устанавливается с помощью location URL: &lt;code&gt;http://findbugs.cs.umd.edu/eclipse/&lt;/code&gt; (в Eclipse выбрать &lt;code&gt;Help\Install new software&lt;/code&gt;, ввести location URL в поле Work with).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-dKUxauuPo5s/TwV0S48r2zI/AAAAAAAAAFc/0cjP_W2wTuM/s1600/findbugs_settings.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="218" width="320" src="http://2.bp.blogspot.com/-dKUxauuPo5s/TwV0S48r2zI/AAAAAAAAAFc/0cjP_W2wTuM/s320/findbugs_settings.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;FindBugs нашел в тестовом приложении 12 ошибок при дефолтном уровне чувствительности. При максимальной чувствительности ошибок нашлось 137. При этом ложных срабатываний  было всего 10 (анализатору не понравился способ именования классов в автоматические генерируемом файле ресурсов; этот файл нужно просто исключить из обработки в настройках FindBugs). Подавляющее большинство остальных ошибок я исправил в более поздних версиях.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-hybSpfAErHs/TwV0q3gJQhI/AAAAAAAAAFo/Q_9jti2d1oI/s1600/findbugs_sample.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="227" width="320" src="http://4.bp.blogspot.com/-hybSpfAErHs/TwV0q3gJQhI/AAAAAAAAAFo/Q_9jti2d1oI/s320/findbugs_sample.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Приведу примеры ошибок, которые нашел FindBugs:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Should be a static inner class&lt;/code&gt; (вложенный класс не сделан статичным, хотя доступ к внешнему классу ему не нужен).&lt;/li&gt;&lt;li&gt;&lt;code&gt;Should this field be static?&lt;/code&gt; (константа не объявлена как static)&lt;/li&gt;&lt;li&gt;&lt;code&gt;Switch statement found where one case falls through to the next case&lt;/code&gt; (пропущен break)&lt;/li&gt;&lt;li&gt;&lt;code&gt;Comparison of String parameter using == or != in&lt;/code&gt; (строки в Java небходимо сравнивать через equal)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Специфичных для Android правил в FindBugs пока нет. Но, возможно, со временем они появятся - разработчики &lt;a href="http://groups.google.com/group/openintents/browse_thread/thread/b3a520a90254ce7f?pli=1"&gt;готовы идти навстречу пожеланиям пользователей.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Checkstyle&lt;/h3&gt;&lt;a href="http://checkstyle.sourceforge.net/"&gt;Checkstyle&lt;/a&gt; нацелен на проверку соблюдения стандарта кодирования. Проверка соблюдения правил именования, правил расстановки скобочек, правил оформления кода и т.д. - вот цель этого анализатора. Полный список правил можно посмотреть в &lt;a href="http://checkstyle.sourceforge.net/config_coding.html"&gt;документации&lt;/a&gt;. Плагин для eclipse &lt;a href-"http://eclipse-cs.sourceforge.net/downloads.html"&gt;устанавливается&lt;/a&gt; с помощью location URL: &lt;code&gt;http://eclipse-cs.sf.net/update/&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;После анализа тестового приложения Checkstyle выдал 8741(!) ворнинг. Я внимательно просмотрел все типы ворнингов.. и не нашел для себя ничего полезного. На мой взгляд - это совершенно не тот инструмент, который необходим индивидуальным разработчикам и небольшим командам. С другой стороны, его активно используют и &lt;a href="http://sevntu-checkstyle.github.com/sevntu.checkstyle/"&gt;дорабатывают&lt;/a&gt;, так что видимо область применения у него все же есть.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-2S9FFxInHYo/TwV4wSDKRhI/AAAAAAAAAF0/xlOO_lzrCSc/s1600/checkstyle_settings.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="290" src="http://3.bp.blogspot.com/-2S9FFxInHYo/TwV4wSDKRhI/AAAAAAAAAF0/xlOO_lzrCSc/s320/checkstyle_settings.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;CodePro Analytix&lt;/h3&gt;&lt;a href="http://code.google.com/intl/ru-RU/javadevtools/codepro/doc/index.html"&gt;CodePro Analytix&lt;/a&gt; - разработка компании Instantiations. Google купил эту компанию и &lt;a href="http://googlewebtoolkit.blogspot.com/2010/09/google-relaunches-instantiations.html"&gt;сделал эти продукт бесплатным&lt;/a&gt;, за что ему большое спасибо.&lt;br /&gt;&lt;br /&gt;Заявленные &lt;a href="http://code.google.com/intl/ru-RU/javadevtools/codepro/doc/index.html"&gt;возможности CodePro Analytix&lt;/a&gt; впечатляют. &lt;a href="http://code.google.com/intl/ru-RU/javadevtools/codepro/doc/features/audit/audit.html"&gt;Аудит кода&lt;/a&gt; на основе нескольких различных наборов правил ("The Elements of Java Style", "Effective Java", "Potential Errors and Refactoring", "Security" и т.д.). Измерение кода с помощью различных метрик. Поиск "дублей" в коде - результатов копипаста. И т.д.&lt;br /&gt;&lt;br /&gt;Провел аудит кода тестового приложения используя все возможные наборы правил. Получил: 152 предупреждения с уровнем серьезности High, 1246 - medium, 1062 - low. Для сравнения - анализ с помощью единственного набора правил "Code Pro Core" выдал 64 medium и 4 low предупреждения.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-RD18joLpJgo/TwWG7-5R8EI/AAAAAAAAAGM/l3WFRuLTkG8/s1600/codepro_audit.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="268" width="320" src="http://4.bp.blogspot.com/-RD18joLpJgo/TwWG7-5R8EI/AAAAAAAAAGM/l3WFRuLTkG8/s320/codepro_audit.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Что-же нашел Code Pro? Частично, результаты поиска пересеклись с результатами FindBugs и Lint. Но только частично. Большую часть ошибок составили минорные проблемы в коде, которые не были обнаружены другими анализаторами. Например:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;String literal can be replaced by a character literal&lt;/code&gt;. В StringBuilder вместо append("\n") использовать append('\n').&lt;/li&gt;&lt;li&gt;&lt;code&gt;Badly located array declarators&lt;/code&gt;. Массив объявлен как "String abc[]" вместо "String[] abc".&lt;/li&gt;&lt;li&gt;&lt;code&gt;Constant on right side of comparison&lt;/code&gt;. Код "if (a == 5) { .. }" следует переписать так "if (5 == a") { .. }"&lt;/li&gt;&lt;li&gt;&lt;code&gt;Switch statements should include all possible enumeration constants&lt;/code&gt;. В перечислимом типе 3 возможных значения, а в switch задействовано только 2 из них.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Do not divide by powers of 2: use "&gt;&gt; 1" rather than "/ 2"&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Invalid string literal: "Yes"&lt;/code&gt;. Строки следует хранить в ресурсах, а не кодировать их жестко в код.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Use charAt() rather than startsWith() when the constant is a single character string&lt;/code&gt;. Код skinName.startsWith("/") можно переписать skinName.charAt(0) == '/'&lt;/li&gt;&lt;li&gt;&lt;code&gt;Define the initial capacity of StringBuilder instances&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;Если не полениться и настроить Code Pro под себя, то инструмент оказывается весьма полезным. Возможно, некоторые рекомендации похожи на "ловлю блох".. но почему бы не привыкнуть писать более оптимальный код, если это возможно?&lt;br /&gt;&lt;br /&gt;Кстати, юзабилити у Code Pro на высоте. Находясь в списке ошибок можно:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;автоматически исправить ошибку (естестенно, автоматический рефакторинг кода поддерживается не для всех типов правил);&lt;/li&gt;&lt;li&gt;проигнорировать ошибку - она перестанет появляться в списке ошибок;&lt;/li&gt;&lt;li&gt;отключить правило, по которому была найдена ошибка;&lt;/li&gt;&lt;li&gt;настроить (!) параметры правила,&lt;/li&gt;&lt;/ul&gt;и так далее.&lt;br /&gt;&lt;br /&gt;Кроме аудита, очень понравилось, как Code Pro ищет дубли. Незаменимая функция. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-yKmH44qnn_o/TwWG1iYOocI/AAAAAAAAAGA/6Feo9xpPQ9Y/s1600/codepro_compare.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="211" width="320" src="http://4.bp.blogspot.com/-yKmH44qnn_o/TwWG1iYOocI/AAAAAAAAAGA/6Feo9xpPQ9Y/s320/codepro_compare.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;PMD&lt;/h3&gt;&lt;a href="http://pmd.sourceforge.net/"&gt;PMD&lt;/a&gt; - еще один анализатор кода, типа Code Pro. Основное назначение - поиск неоптимального кода, проблем с производительностью, нарушений стиля кодирования, дублей в коде и т.д. Плагин PMD устанавливается с помощью location URL: &lt;code&gt;http://pmd.sourceforge.net/rules/android.html&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Примечательно, что в PMD реализовано &lt;a href="http://pmd.sourceforge.net/rules/android.html"&gt;несколько правил, специфичных для Android&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Для тестового приложения PMD выдал около 2000 предупреждений. Примеры:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Substitute calls to size() == 0 (or size() != 0) with calls to isEmpty()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Private field ''{0}'' could be made final; it is only initialized in the declaration or constructor.&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;UnnecessaryCaseChange: Using equalsIgnoreCase() is cleaner than using toUpperCase/toLowerCase().equals()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;UnusedFormalParameter: Avoid unused {0} parameters such as ''{1}''. Avoid passing parameters to methods or constructors and then not using those parameters.&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;InefficientEmptyStringCheck: String.trim().length()==0 is an inefficient way to validate an empty String.&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;UseIndexOfChar: String.indexOf(char) is faster than String.indexOf(String).&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;BooleanInversion: Use bitwise inversion to invert boolean values&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;Несмотря на то, что PMD генерирует множество лишних предупреждений, следует отметить, что и полезных подсказок он выдает множество. Так же как и Code Pro, PMD следует тщательно настраивать.&lt;br /&gt;&lt;br /&gt;В PMD, так же как в Code Pro, реализована функция поиска повторяющихся участков кода. Визуализация найденных фрагментов, правда, похуже. Зато результаты поиска - другие, так что программы дополняют друг друга. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-07qFCT9CevQ/TwWOUNRVzfI/AAAAAAAAAGY/EBMZ6YGA2-U/s1600/pmd.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="227" width="320" src="http://1.bp.blogspot.com/-07qFCT9CevQ/TwWOUNRVzfI/AAAAAAAAAGY/EBMZ6YGA2-U/s320/pmd.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3&gt;Motodev App Validator&lt;/h3&gt;Motodev App Validator входит в состав среды разработки &lt;a href="http://developer.motorola.com/docstools/motodevstudio/"&gt;MOTODEV Studio for Android&lt;/a&gt;. Прочитать про него можно в презентации &lt;a href="http://assets.en.oreilly.com/1/event/68/Static%20Analysis%20For%20Improved%20Application%20Performance%20And%20Quality%20Presentation.pdf"&gt;Static Analysis For Improved Application Performance And Quality Presentation (PDF)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Те, кто не использует MOTODEV Studio, могут попробовать &lt;a href="http://developer.motorola.com/testing/app-validator/"&gt;online вариант Motodev App Validator&lt;/a&gt; (требуется зарегистрироваться на сайте). &lt;br /&gt;&lt;br /&gt;Online вариант Motodev App Validator принимает на вход apk-файл приложения. К сожалению, 400 килобайтную apk-шку тестового приложения online вариант не заглотил - выдал ошибку. Мелкие приложение в 30-40 кб анализирует, а с "крупным" не работает. Пришлось &lt;a href="http://developer.motorola.com/docstools/motodevstudio/download/"&gt;качать Motodev Studio&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Скачал, запустил. Для тестового приложения Application Validator выдал 101 предупреждение. Из них:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;75 - &lt;code&gt;"Drawable "XXX.png" does not exist in the "drawable-ldpi" folder. Add the appropriate "XXX.png" drawable to the "drawable-ldpi" folder."&lt;/code&gt; - ничего нового.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;13 - &lt;code&gt;"Default layout file YYY.xml does not have an xlarge-specific version. This app may not have the look and feel expected on devices with extra large screens. Define an xlarge layout for YYY.xml or add &amp;lt;supports-screens android:xlargeScreens="false"&amp;gt; to the manifest file."&lt;/code&gt; - так же ничего нового.&lt;/li&gt;&lt;li&gt;4 - &lt;code&gt;"The import android.util.DisplayMetrics is never used ZZZ.java"&lt;/code&gt; - ошибка, на которую умеет указывать сам Eclipse.&lt;/li&gt;&lt;li&gt;1 - &lt;code&gt;"The value of the local variable AAA is not used"&lt;/code&gt; - эту ошибку умеют находить многие анализаторы.&lt;/li&gt;&lt;li&gt;3 - &lt;code&gt;"The static field A.B should be accessed in a static way ClassA.B"&lt;/code&gt; - и эту тоже.&lt;/li&gt;&lt;/ul&gt;Интересным оказалось лишь одно сообщение - &lt;code&gt;"The application declares the permission android.permission.CALL_PHONE which implies the unsupported feature android.hardware.telephony..."&lt;/code&gt;. Честно говоря, результаты работы не впечатлили.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-NHo6NN7u7ZY/TwZeDM5UqkI/AAAAAAAAAGw/EfpMA2g-Ong/s1600/motodev_settings.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="319" width="320" src="http://1.bp.blogspot.com/-NHo6NN7u7ZY/TwZeDM5UqkI/AAAAAAAAAGw/EfpMA2g-Ong/s320/motodev_settings.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Впрочем, судя по списку правил, Application Validator умеет находить и более интересные вещи: незакрытые курсоры, недочеты Android Manifest, отсутствие перевода строки в локализованных ресурсах, ненужные или наоборот, пропущенные permissions и т.д.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Klocwork Solo for Java&lt;/h3&gt;&lt;a href="http://www.klocwork.com/products/solo/"&gt;Klocwork Solo&lt;/a&gt; - платный статический анализатор кода. По заверениям разработчиков, способен находить более 200 типов проблем, связанных с уязвимостью и надежностью кода. Анализатор умеет находить проблемы, специфичные для Android-приложений. Интегрируется с EClipse. Триальная 30-дневная версия доступна после регистрации на сайте. У триальной версии ограничение - не более 300 файлов в проекте.&lt;br /&gt;&lt;br /&gt;Полный список правил, которые проверяет Kockwork Solo, можно посмотреть в &lt;a href="http://download.klocwork.com/docs/issuehelp/index.html"&gt;документации&lt;/a&gt;. На настоящий момент правил, специфичных для Android, всего девять.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-EX-wZ8D7340/TwWqBFoC-LI/AAAAAAAAAGk/aaRqJPj4WPM/s1600/klocwork_settings.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="315" width="320" src="http://2.bp.blogspot.com/-EX-wZ8D7340/TwWqBFoC-LI/AAAAAAAAAGk/aaRqJPj4WPM/s320/klocwork_settings.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Для тестового приложения Kockwork Solo выдал 240 предупреждений. Неожиданно, очень интересных. Примеры:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;NPE.RET : Null pointer dereference of 'xy' where null is returned from a method&lt;/code&gt;. В приложении имеется такой код: &lt;code&gt;Point xy = m_SA.getShortcutPosition(); Rect r = new Rect(0, 0, xy.x, xy.y);&lt;/code&gt;. Функция getShortcutPosition() в отдельных случаях может возвращать null. Но в вызывающей функции проверки на null нет. Это типичная ошибка.&lt;/li&gt;&lt;li&gt;&lt;code&gt;NPE.COND : Null pointer dereference of 'xxx' where null comes from condition&lt;/code&gt;. Объект xxx используется без проверки на null, а между тем, он запросто может быть равен null.&lt;/li&gt;&lt;li&gt;&lt;code&gt;RTC.CALL : Type cast from 'android.view.View' to 'android.widget.ImageButton' is redundant because method 'setTag' is defined in 'android.view.View'&lt;/code&gt;. В приложении код такой: &lt;code&gt;((ImageButton)findViewById(R.id.hs_image)).setTag(combination)&lt;/code&gt;. Т.е. анализатор обнаружил ненужное приведение типа.&lt;/li&gt;&lt;li&gt;&lt;code&gt;REDUN.FINAL : Redundant 'final' modifier&lt;/code&gt; - приватный метод объявлен как final. Приватные методы всегда final, так что объявление излишне.&lt;/li&gt;&lt;li&gt;&lt;code&gt;JD.SYNC.IN : Field 'VarName' synchronized inconsistently.&lt;/code&gt; - доступ к переменной VarName синхронизирован частично. Это ошибка - доступ должен либо всегда синхронизироваться, либо никогда.&lt;/li&gt;&lt;li&gt;&lt;code&gt;ANDROID.NPE : Null pointer dereference of 'window' in an Android application&lt;/code&gt;. Код в приложении следующий: &lt;code&gt;Window window = activity.getWindow(); window.getDecorView().getWindowVisibleDisplayFrame(rect)&lt;/code&gt; Т.е. не выполняется проверка window на null&lt;/li&gt;&lt;/ul&gt;Так что Kockwork Solo находит реальные ошибки, причем иные, чем FindBugs  (списки дефектов, которые они обнаруживают, пересекаются незначительно). Лично меня очень заинтересовали ошибки типа NPE.RET и Android.NPE. Такие ошибки, судя по всему, не находит ни один другой анализатор.&lt;br /&gt;&lt;br /&gt;Юзабилити у продукта не понравилось. В списке ошибок есть фильтрация, есть возможность указать статус ошибки. Но если указать статус "Ignored", то ошибка пропадает и не ясно, как ее вернуть. Если отфильтровать список, то часть ошибок из списка пропадает и опять же не ясно, как сбросить фильтр. Не интуитивно все как-то. В CodePro сделано удобнее.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Lmwoy5r95EE/TwZ1c_3J7AI/AAAAAAAAAG8/YJ0REyWSlCQ/s1600/klocwok_screen.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="232" width="320" src="http://1.bp.blogspot.com/-Lmwoy5r95EE/TwZ1c_3J7AI/AAAAAAAAAG8/YJ0REyWSlCQ/s320/klocwok_screen.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;JTest от Parasoft&lt;/h3&gt;Триальную версию &lt;a href="http://www.parasoft.com/jsp/products/jtest.jsp"&gt;Jtest&lt;/a&gt; просто так скачать не дают. Так что этот анализатор я в работе проверить не смог. Судя по описанию - функциональность очень интересная. Например, JTest, так же как и Code Pro, умеет самостоятельно рефакторить код.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Julia&lt;/h3&gt;Julia - коммерческий анализатор кода Java / Android приложений. "Семантический инструмент, основанный на &lt;a href="http://www.juliasoft.com/public/Biblioteca/cade11.pdf"&gt;математической теории&lt;/a&gt;". &lt;br /&gt;&lt;br /&gt;Как указано на сайте разработчика, протестировать работу анализатора можно &lt;a href="http://julia.scienze.univr.it/"&gt;online&lt;/a&gt;, загрузив в него jar-файл. У android приложения нет jar файла, есть apk (бинарный формат - другой). APK-файл анализатор принял.. но ни один из доступных способов анализа (Nullness, Termination, Check) ничего не выдал. Пишет - "there are no warnings". &lt;br /&gt;&lt;br /&gt;Загрузил первый попавшийся JAR - работает. Судя по всему, там неплохой анализ на "отсутствие проверки на null". Жаль, что apk-шки не грузятся.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;: Написал разработчикам. Они мне подсказали, что можно экспортировать Android приложение в JAR-файл прямо из Eclipse. Экспортировал, загрузил в Julia.&lt;br /&gt;&lt;br /&gt;Nullness analysis и Termination analysis результатов не выдали. Дело в том, что в демонстрационной online-версии для данных типов анализа существует ограничение: общее количество методов не должно превышать 10000 и 8500 соответственно. В тестовом приложении количество методов оказалось большим, так что процедура анализа принудительно останавливалась до завершения работы.&lt;br /&gt;&lt;br /&gt;Третий тип анализа - "Checks", - выдал 286 предупреждений. По большей части, вот таких:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;[Classcast] A you sure that this cast from classA into classB is always legal?&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;[BadNames] Method XXX has a bad name&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;[Deadcode] Methos XXX is not reachable&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;[BadEq] Inefficient comparison with the empty string. Use isEmpty() instead&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;[Approximation] Unsafe comparison beween non-integral numbers.&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;[StaticFieldAccess] Modification of static field from a non-static context&lt;/code&gt; (это что-то новенькое; другие анализаторы на такие ошибки не ругались).&lt;/li&gt;&lt;li&gt;&lt;code&gt;[Unused class] Class YYY is not used.&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;[Field access] Field XXX is never reach in reachable code.&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;[Useless call] Useless call toString()&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;[BadEq] Suspicious use of == rather then equals() to compare two java.lang.String&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;В принципе, кое что интересно есть. Но: пользоваться очень неудобно (результаты выдаются в виде странички на флеш), поскольку сервис online, приходится "отдавать" исходники приложения в виде jar, что не всегда приемлемо.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update2&lt;/b&gt;. Попросил разработчиков Julia провести анализ тестового приложения и прислать мне результаты. Они любезно согласились.&lt;br /&gt;&lt;br /&gt;Termination analysis - это анализ кода на наличие бесконечных циклов и рекурсий в коде. Он выдал одно предупреждение. Анализатору не понравился код &lt;br /&gt;&lt;code&gt;&lt;br /&gt;private void expungeStaleEntries() {&lt;br /&gt;Reference&lt;? extends V&gt; sv;&lt;br /&gt;while ((sv = queue.poll()) != null) {&lt;br /&gt;hash.remove(reverseLookup.remove(sv));&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;из реализации SoftHashMap, взятой &lt;a href="http://www.javaspecialists.eu/archive/Issue098.html"&gt;отсюда&lt;/a&gt;. Опасения анализатора понятны. Тем не менее, код правильный.&lt;br /&gt;&lt;br /&gt;Nullness analysis проверяет в коде отсутствие необходимых и наличие излишних проверок на null. Таких предупреждений было выдано 187. Примеры ошибок:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;X1.java:57: is the return value of getY1 non-null?&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;X2.java:57: is the 0th actual parameter of parseInt non-null?&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;X3.java:69,70,72,78,105,109,178: is the return value of getY2 non-null?&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;X4.java:227,261,309: is the value of field mX1 non-null?&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;X5.java:84: is this nullness check useless?&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;X6.java:318,361: is the receiver of the call to iterator non-null?&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;X7.java:34: is the formal parameter srcRect non-null?&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;Отмечу, что Kockwork Solo выдал всего 43 предупреждения, связанных с проверкой на null. Подробнее результаты, полученные Julia и Kockwork Solo, сравниваются ниже.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Напоследок. Сравнительные тесты&lt;/h3&gt;Ну как же без тестов... Многие анализаторы обнаруживают одни и те же типы проблемы в коде. Сравним, кто что находит.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Неверное сравнение строк&lt;/h4&gt;В Java строки надо сравнивать через equal. Между тем, по старой сишной привычке, можно сравнить строки через != и == и получить ошибку. Этот баг настолько распространен, что его ищут чуть ли не все анализаторы.&lt;br /&gt;&lt;br /&gt;В тестовом приложении оказалось семь таких багов. Вот они:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;1. widget_name == ""&lt;br /&gt;2. title != adapter.getItem(i).Title&lt;br /&gt;3. srcStr == "S"&lt;br /&gt;4. m_K != ""&lt;br /&gt;5. contact_name != m_B.getB().GetR(this)&lt;br /&gt;6. contact_name == ""&lt;br /&gt;7. name == "" ? "" : name + ": "&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Вот как справились с поиском этого бага анализаторы кода:&lt;br /&gt;&lt;table border="1"&gt;&lt;tr&gt;  &lt;th&gt;&lt;/th&gt;  &lt;th&gt;1&lt;/th&gt;  &lt;th&gt;2&lt;/th&gt;  &lt;th&gt;3&lt;/th&gt;  &lt;th&gt;4&lt;/th&gt;  &lt;th&gt;5&lt;/th&gt;  &lt;th&gt;6&lt;/th&gt;  &lt;th&gt;7&lt;/th&gt; &lt;/tr&gt;&lt;tr&gt;  &lt;th&gt;Kockwork Solo&lt;/th&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt;  &lt;th&gt;CodePro&lt;/th&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt;  &lt;th&gt;FindBugs&lt;/th&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;0&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;0&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;0&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt;  &lt;th&gt;PMD&lt;/th&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;0&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;0&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt;  &lt;th&gt;Checkstyle&lt;/th&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;0&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;0&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt;  &lt;th&gt;Julia&lt;/th&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt;  &lt;td&gt;1&lt;/td&gt; &lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;h4&gt;Проблемы со switch&lt;/h4&gt;Для switch характерны следующие проблемы:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;1. Отсутствие default&lt;/li&gt;&lt;li&gt;2. Пропущен break&lt;/li&gt;&lt;li&gt;3. Switch выполняется по значению перечислимого типа. В "case" используются не все значения, входящие в перечислимый тип.&lt;/li&gt;&lt;li&gt;4. Switch слишком маленький - например, в нем одно или два значения. Можно обойтись if/else.&lt;/li&gt;&lt;li&gt;5. В двух case используется одинаковый код.&lt;/li&gt;&lt;/ul&gt;Далеко не всегда эти проблемы являются реальными ошибками. Тем не менее, анализаторы кода как правило считают своим долгом о них сообщить.&lt;br /&gt;&lt;br /&gt;Разные анализаторы работают с разными типами проблем. Итак, вот как отработали анализаторы на тестовом приложении.&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;  &lt;th&gt;1&lt;/th&gt;  &lt;th&gt;2&lt;/th&gt;  &lt;th&gt;3&lt;/th&gt; &lt;th&gt;4&lt;/th&gt; &lt;th&gt;5&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Kockwork Solo&lt;/th&gt;  &lt;th&gt;&lt;/th&gt;  &lt;th&gt;&lt;/th&gt;  &lt;th&gt;&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;th&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;CodePro&lt;/th&gt;  &lt;th&gt;32&lt;/th&gt;  &lt;th&gt;4&lt;/th&gt;  &lt;th&gt;51&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;th&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;FindBugs&lt;/th&gt;  &lt;th&gt;18&lt;/th&gt;  &lt;th&gt;1&lt;/th&gt;  &lt;th&gt;&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;th&gt;0&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;PMD&lt;/th&gt;  &lt;th&gt;32&lt;/th&gt;  &lt;th&gt;8&lt;/th&gt;  &lt;th&gt;&lt;/th&gt; &lt;th&gt;9&lt;/th&gt; &lt;th&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Checkstyle&lt;/th&gt;  &lt;th&gt;32&lt;/th&gt;  &lt;th&gt;&lt;/th&gt;  &lt;th&gt;&lt;/th&gt; &lt;th&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Julia&lt;/th&gt;  &lt;th&gt;&lt;/th&gt;  &lt;th&gt;&lt;/th&gt;  &lt;th&gt;&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;th&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;h4&gt;NP-анализ&lt;/h4&gt;Отсутствие проверки на null и, как результат, "NUll pointer exception" - одна из наиболее частых ошибок при разработке под Android. Другая сторона медали - излишние проверки. &lt;br /&gt;&lt;br /&gt;К слову сказать, FindBugs успешно отлавливает такую ошибку: вначале у объекта вызывается функция, а затем объект проверяется на null. &lt;br /&gt;&lt;br /&gt;Klocwork и Julia "заточены" под Android. И эта "заточенность" проявляется, прежде всего, в возможности NP-анализа. Как я писал вышел, Klocwork для тестового приложения выдал 43 NP-предупреждения, Julia - 187. Сравним, кто что нашел. &lt;br /&gt;&lt;br /&gt;&lt;h5&gt;NP анализ. Файл 1&lt;/h5&gt;Проблема 1.1. &lt;code&gt;String action = intent.getAction(); if (action.equals("abc")) {..};&lt;/code&gt; Переменная action может быть null. Проблему обнаружили обе программы.&lt;br /&gt;Проблема 1.2. &lt;code&gt;intent.getExtras().getInt(...);&lt;/code&gt; Результат getExtras() не проверяется на null. Проблему обнаружили обе программы. &lt;br /&gt;Проблема 1.3. &lt;code&gt;Uri uri = intent.getData(); Integer.parseInt(uri.getQueryParameter("widget_id"))&lt;/code&gt;. Оба анализатора сообщили об использовании uri без проверки uri на null. Julia дополнительно сообщила о том, что результаты работы uri.getQueryParameter на null не проверяются и в parseInt может быть передан null.&lt;br /&gt;Проблема 1.4. &lt;code&gt;WidgetContent wc = WidgetFabric.getWidgetContent(); wc.getFlagValue();&lt;/code&gt;. Оба анализатора сообщили, что wc может быть null. В реализации getWidgetContent() действительно есть ветка кода, которая возвращает null.&lt;br /&gt;Проблема 1.5. &lt;code&gt;((Singleton)context.getApplicationContext()).getABC().Register();&lt;/code&gt; Об этой проблеме сообщила только Julia. Функция getABC() может вернуть null и вызывать Register() нельзя. На самом деле, объект ABC создается в &lt;a href="http://derevyanko.blogspot.com/2010/12/android_26.html"&gt;синглетоне, реализованном поверх класса Application.&lt;/a&gt; Инициализируется он в момент запуска приложения и null быть не может. Однако, инициализация ABC проводится не в конструкторе синглетона, а в функции onCreate. Анализатор не знает про тонкости создания синглетона и предупреждает об ошибке, которая вряд ли возможна.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;NP анализ. Файл 2&lt;/h5&gt;Проблема 2.1. &lt;code&gt;CheckBoxPreference pref = (CheckBoxPreference)getPreferenceScreen().findPreference("pref_name");&lt;/code&gt;. findPreference может возвращать null.&lt;br /&gt;Проблема 2.2. &lt;code&gt;List&lt;skininfo&gt; list_skins = ((Singleton)this.getApplicationContext()).getSkinManager().getListSkins("skin_name");&lt;/code&gt; Проблема аналогична 1.5. Функция getSkinManager может возвращать null, но только теоретически.&lt;br /&gt;Проблема 2.3. &lt;code&gt;int len_skins = list_skins.size();&lt;/code&gt; Переменная list_skins действительно может быть null.&lt;br /&gt;Проблема 2.4. &lt;code&gt;for (int j = 0; j &lt; len_skins; ++j) { array_skins[j] = list_skins.get(j).SkinTag; }&lt;/code&gt;  Результаты get не проверяются на null. Такая проверка необходима, если list_skins может содержать null. В приложении такого быть не может, так что проверка не нужна.&lt;br /&gt;Проблема 2.5. &lt;code&gt;String skin_name = (String) p.getEntries()[p.findIndexOfValue(skin_tag)];&lt;/code&gt; Результат работы getEntries() не проверяется на null.&lt;br /&gt;Проблема 2.6. &lt;code&gt;CharSequence[] titles = res.getTextArray(idTitles); if (values[0].equals(svalue)) {...};&lt;/code&gt; Значение values[0] не проверяется на null.&lt;br /&gt;Julia сообщила обо всех этих проблемах. Klocwork - только о 2.3.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;NP анализ. Файл 3&lt;/h5&gt;Проблема 3.1. &lt;code&gt;m_AsyncTask = new AppInfoReceiverTask(mUtils.getList());&lt;/code&gt; Эту ошибка нашла только Julia - переменная mUtils может быть null. На самом деле, mUtils инициализируется в onCreate (речь идет о наследнике Activity) и null быть не может.&lt;br /&gt;Проблема 3.2. &lt;code&gt;protected void onActivityResult(int requestCode, int resultCode, Intent data) { data.getExtras().getString(...);&lt;/code&gt; Эту проблему нашли оба анализатора: data.getExtra может вернуть null.&lt;br /&gt;Проблема 3.3. &lt;code&gt;(LauncherContent)WidgetFabric.getWidgetContent()&lt;/code&gt; Эту проблему нашел Klocwork. Julia ее пропустила.&lt;br /&gt;Проблема 3.4. &lt;code&gt;SpinnerItem selected_item = (SpinnerItem)spinner.getSelectedItem(); String s = selected_item.Title;&lt;/code&gt; Функция getSelectedItem может вернуть null согласно документации. Проблему нашла только Julia&lt;br /&gt;Проблема 3.5. &lt;code&gt;ListView list_view = (ListView) dialog.findViewById(R.id.listview); list_view.setAdapter(new SelectImageAdapter(list_images) );&lt;/code&gt; Теоретически, findViewById может вернуть null. На проблему указала только Julia.&lt;br /&gt;Проблема 3.6. &lt;code&gt;private final void remove_shortcut(AppInfo appInfo) {  m_List.remove(appInfo); }&lt;/code&gt; В функцию может быть передан null, appInfo на null не проверяется. Проблему нашла только Julia.&lt;br /&gt;Проблема 3.7. &lt;code&gt;for (SkinManager.SkinInfo skin : skins) { ... }&lt;/code&gt; Переменная skins может содержать null. Проблему нашли оба анализатора.&lt;br /&gt;&lt;br /&gt;Резюме. Julia находит почти все NP-ошибки, которые обнаруживает Klocwork, плюс еще ряд дополнительных. Одновременно, она генерирует довольно много ложных сообщений, которые на практике придется просто игнорировать. Самая большая проблема на мой взгляд - она не учитывает, что в Activity переменные инициализируются в onCreate, а не в конструкторе.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Итоги&lt;/h3&gt;В итоге получается следующая картина. В настоящее время существует восемь доступных статических анализаторов кода для Android приложений: Lint, FindBugs, Checkstyle, CodePro, PMD, Motodev App Validator, Klocwork Solo, Julia. Основная специализация у них следующая:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Lint и Motodev App Validator - поиск проблем в ресурсах Android приложения;&lt;/li&gt;&lt;li&gt;FindBugs, Klocwork Solo и Julia - поиск ошибок в коде приложения;&lt;/li&gt;&lt;li&gt;Checkstyle - проверка соблюдения правил оформления кода и стандарта кодирования;&lt;/li&gt;&lt;li&gt;CodePro и PMD - поиск неоптимального кода, проблемного кода, поиск дублей.&lt;/li&gt;&lt;/ul&gt;FindBugs и Lint прекрасно работают "с нуля" - настраивать их практически не требуется. CodePro и PMD требуют тонкой настройки. Но если отключить в них ненужные проверки, они приносят реальную пользу. Motodev App Validator и Checkstyle мне показались не слишком полезными - на любителя. Klocwork Solo - находит ряд ошибок, которые не находят другие анализаторы. Он платный, но и триальную версию можно вполне успешно использовать для небольших приложений. Julia находит ряд NP-ошибок, которые не находит Klocwork, но при этом генерирует еще и множество ложных предупреждений (ошибка возможна, но только теоретически). К сожалению, воспользоваться бесплатной версией практически не возможно.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-2901442823884408321?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/2901442823884408321/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2012/01/android.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/2901442823884408321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/2901442823884408321'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2012/01/android.html' title='Статические анализаторы кода для Android-приложения.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-UJ2JPXSVb9s/TwVtraTmfMI/AAAAAAAAAFQ/R32F2qPO1uk/s72-c/lint2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-5665656673628968153</id><published>2011-12-17T13:27:00.000+08:00</published><updated>2011-12-17T13:27:26.145+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Диалог выбора на Android или контекстное меню с картинками.</title><content type='html'>Задача выбора элемента из списка возникает в Android-приложениях регулярно. В принципе, для выбора элемента можно воспользоваться &lt;a href="http://developer.android.com/guide/topics/ui/menus.html"&gt;стандартным контекстным меню&lt;/a&gt;. Но у контекстного меню есть ряд ограничений:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;В контекстном меню функции вызова (&lt;code&gt;onCreateContextMenu&lt;/code&gt;) и обработки результатов(&lt;code&gt;onContextItemSelected&lt;/code&gt;) реализуются в Activity.&lt;/li&gt;&lt;li&gt;Нельзя выбрать несколько элементов.&lt;/li&gt;&lt;li&gt;В контекстном меню показывается только название элемента. Картинки не отображаются.&lt;/li&gt;&lt;/ul&gt;Наиболее напрягает необходимость реализации функций в Activity. Типичная ситуация - вам нужна вспомогательная функция, которая предлагает пользователю что-нибудь выбрать из списка и затем выполняет действие над выбранным элементом. Такая функция может вызываться из &lt;b&gt;множества&lt;/b&gt; разных Activity. И что, мне в каждой Activity реализовывать функции &lt;code&gt;onCreateContextMenu&lt;/code&gt; и &lt;code&gt;onContextItemSelected&lt;/code&gt;? Так не пойдет. &lt;br /&gt;&lt;br /&gt;Лучше сделать диалог. А поскольку подобные диалоги нужны сплошь и рядом, нужно сделать более-менее универсальный диалог, который подойдет для выбора элементов в большинстве случаев. О реализации такого диалога и пойдет речь.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;h3&gt;Постановка задачи&lt;/h3&gt;Итак, задача - создать гибкий диалог выбора элементов из списка. Диалог, который идеально подходит для случаев, когда есть фиксированный список произвольных элементов (не обязательно строк) и нужно дать возможность пользователю выбрать один или несколько элементов. Диалог должен "уметь" показывать название, картинку и описание элемента, причем показ картинки и описания должен быть опциональным (просто потом, что картинка и описание требуются далеко не всегда). &lt;br /&gt;&lt;br /&gt;В сложных случаях подобного диалога, естественно, не хватит. Например, при выборе списка контактов потребуется возможность фильтрации списка, фоновая подгрузка картинок, возможность вызова меню и т.д. Но в простейших случаях его будет за глаза. &lt;br /&gt;&lt;br /&gt;Еще одним требованием к диалогу является возможность передать в него функцию обработчик полученного результата. Таким образом, появится возможность реализовывать функцию обработчик в анонимном классе прямо в точке вызова диалога. &lt;br /&gt;&lt;br /&gt;Ну и последнее требование - простота использования. &lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Реализация UniDialog&lt;/h3&gt;Исходные коды получившегося диалога (я его назвал UniDialog) можно &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoSelectDialogs/"&gt;посмотреть здесь&lt;/a&gt;. Реализация включает три файла: &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoSelectDialogs/src/com/kbitubit/android/demo/UniDialogMultySelector.java"&gt;UniDialogMultySelector&lt;/a&gt; - диалог для выбора нескольких элементов, &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoSelectDialogs/src/com/kbitubit/android/demo/UniDialogSingleSelector.java"&gt;UniDialogSingleSelector&lt;/a&gt; - диалог для выбора одного элемента, &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoSelectDialogs/src/com/kbitubit/android/demo/UniDialogUtils.java"&gt;UniDialogUtils&lt;/a&gt; - вспомогательные классы. Кроме того, для работы диалог требует файлы ресурсов: &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoSelectDialogs/res/layout/unidialog_selector_row.xml"&gt;unidialog_selector_row.xml&lt;/a&gt;, &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoSelectDialogs/res/layout/unidialog_selector.xml"&gt;unidialog_selector.xml&lt;/a&gt;, &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoSelectDialogs/res/values/colors.xml"&gt;colors.xml&lt;/a&gt; и &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoSelectDialogs/res/drawable/unidialog_listview_background.xml"&gt;unidialog_listview_background.xml&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Далее, опуская детали реализации, рассмотрим несколько примеров вызова диалога.&lt;br /&gt;&lt;h3&gt;Пример 1. Список строк&lt;/h3&gt;&lt;pre class="java"&gt;/** Список строк - элементов списка */&lt;br /&gt;private static String[] items = {"Item 1"&lt;br /&gt; , "Item 2"&lt;br /&gt; , "Item 3"&lt;br /&gt; , "Item 4"};&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;//подготавливаем данные для диалога&lt;br /&gt;DialogConfig dc = new DialogConfig("Main title"&lt;br /&gt; , false //картинки не поддерживаются&lt;br /&gt; , false //описания элементов отсутствуют&lt;br /&gt;);&lt;br /&gt;ArrayList&lt;String&gt; list = new ArrayList&lt;String&gt;();&lt;br /&gt;for (String s: items) {&lt;br /&gt; list.add(s);&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;//вызываем диалог &lt;br /&gt;UniDialogSingleSelector.showDialog(this&lt;br /&gt;  , dc //конфигурация диалога&lt;br /&gt;  , list //список элементов диалога&lt;br /&gt;  , new UniDialogSingleSelector.SingleItemReceiver&lt;String&gt;() {&lt;br /&gt;    @Override public void onSelectItem(String selectedItem) {&lt;br /&gt;      if (selectedItem == null) {&lt;br /&gt;         //пользователь отменил выбор&lt;br /&gt;      } else {&lt;br /&gt;         //пользователь выбрал selectedItem&lt;br /&gt;      }&lt;br /&gt;   }    &lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;В примере выводится список строк, из которого пользователь должен выбрать одну строку. Код делится на две части: подготовка данных для диалога и вызов диалога. &lt;br /&gt;&lt;br /&gt;Подготовка данных включает создание объекта DialogConfig, задающего конфигурацию диалога: название и два флага, указывающих требуется ли показывать картинки и описания элементов. В нашем случае элементы - это обычные строки, у них нет картинок и описаний, поэтому флаги выставлены в false.&lt;br /&gt;&lt;br /&gt;Диалог вызывается статической функцией &lt;code&gt;UniDialogSingleSelector.showDialog&lt;/code&gt;, в которую передается исходная Activity, конфигурация диалога, список элементов и обработчик результатов. Обработчик задается в виде анонимного класса, реализующего функцию onSelectItem. Если пользователь отменил выбор, то в onSelectItem будет передат null. Если пользователь выбрал элемент - то в onSelectItem будет передан выбранный элемент. &lt;br /&gt;&lt;br /&gt;Функция UniDialogSingleSelector.showDialog является параметризированной. Параметром является тип элементов списка.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Пример 2. Список элементов с картинками и описанием&lt;/h3&gt;Более сложный пример. Введем класс для хранения элементов списка:&lt;br /&gt;&lt;pre class="java"&gt;class CustomDataItem implements UniDialogUtils.ItemImageProvider, ItemDescriptionProvider  {&lt;br /&gt; public final String Title;&lt;br /&gt; private final Bitmap Image;&lt;br /&gt; public final String Description;&lt;br /&gt; public CustomDataItem(String title, Bitmap image, String description) {&lt;br /&gt;   this.Title = title;&lt;br /&gt;   this.DrawableId = drawableId;   &lt;br /&gt;   this.Description = description;&lt;br /&gt; }&lt;br /&gt; @Override public Bitmap getImage() {&lt;br /&gt;   return this.Image;&lt;br /&gt; }&lt;br /&gt; @Override public String toString() {&lt;br /&gt;   return this.Title;&lt;br /&gt; }&lt;br /&gt; @Override public String getDescription() {&lt;br /&gt;   return this.Description;&lt;br /&gt; } &lt;br /&gt;}&lt;/pre&gt;Класс реализует два вспомогательных интерфейса - &lt;code&gt;ItemImageProvider&lt;/code&gt; и &lt;code&gt;ItemDescriptionProvider&lt;/code&gt;. Через эти интерфейсы диалога получает от элементов списка картинки и описания.&lt;br /&gt;&lt;pre class="java"&gt;UniDialogUtils.DialogConfig dc = new UniDialogUtils.DialogConfig(items[2]&lt;br /&gt;  , true //CustomDataItem supports images&lt;br /&gt;  , true //CustomDataItem supports desctiptions&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;//предполагаем, что массивы ITEMS, IMAGES и DESCRIPTIONS&lt;br /&gt;//содержат необходимые названия, картинки и описания&lt;br /&gt;ArrayList&lt;CustomDataItem&gt; list = new ArrayList&lt;CustomDataItem&gt;();&lt;br /&gt;for (int i = 0; i &lt; ITEMS.length; ++i) {&lt;br /&gt;  list.add(new CustomDataItem(ITEMS[i], IMAGES[i], DESCRIPTIONS[i]));&lt;br /&gt;}&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;UniDialogMultySelector.showDialog(this&lt;br /&gt; , dc&lt;br /&gt; , list&lt;br /&gt; , new UniDialogMultySelector.MultyItemsReceiver&lt;CustomDataItem&gt;() {&lt;br /&gt;  @Override public void OnSelectItems(List&lt;CustomDataItem&gt; selectedItems) {&lt;br /&gt;    if (selectedItems == null) {&lt;br /&gt;      //выбор отменен&lt;br /&gt;    } else {&lt;br /&gt;      //пользователь выбрал элементы&lt;br /&gt;    }   &lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; , true //все элементы выделены по умолчанию&lt;br /&gt;);  &lt;br /&gt;&lt;/pre&gt;Здесь мы используем диалог мульти-выбора - &lt;code&gt;UniDialogMultySelector&lt;/code&gt;. Он отличается от &lt;code&gt;UniDialogSingleSelector&lt;/code&gt; наличием дополнительного (последнего) параметра в showDialog, задающего набор выбранных по умолчанию элементов. Кроме того, используется другой класс для обработки результатов - MultyItemsReceiver. Этот класс реализует функцию OnSelectItems, принимающую список выбранных элементов или null, если выбор отменен.&lt;br /&gt;&lt;br /&gt;Вместо CustomDataItem можно использовать "стандартный" класс UniDialogUtils.SimplestDataItem, который дополнительно позволяет сохранять для элементов теги. А можно использовать сторонние классы, применяемые в других местах приложения - иногда это оказывается очень удобно. Единственное ограничение - если требуется показывать картинки и/или описания, то классы должны реализовывать один-два дополнительных интерфейса. &lt;br /&gt;&lt;br /&gt;В showDialog последним параметром вместо true/false можно явно передать список элементов, которые должны быть по умолчанию выделены.&lt;br /&gt;&lt;br /&gt;На картинке показан вид получившегося диалога.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-US9wbmNE7Ps/TuwPCebPAAI/AAAAAAAAAE4/JcIbQuX5a1M/s1600/dialog_screen.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="192" src="http://3.bp.blogspot.com/-US9wbmNE7Ps/TuwPCebPAAI/AAAAAAAAAE4/JcIbQuX5a1M/s320/dialog_screen.png" /&gt;&lt;/a&gt;&lt;/div&gt;Редактируя файлы ресурсов, можно подогнать вид диалога под "стандартный" для приложения вид.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Заключение&lt;/h3&gt;Выше я привел два примера использования диалога. В демонстрационном приложении можно посмотреть и другие примеры. &lt;br /&gt;&lt;br /&gt;Я активно использую данный диалог в своих приложения - удобно. Надеюсь, пригодится кому-нибудь еще. Пожелания и предложения, а так же замечания об ошибках - приветствуются :)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/dvsrc/downloads/detail?name=20111217DemoSelectDialog.7z&amp;can=2&amp;q="&gt;Скачать&lt;/a&gt; демонстрационный проект и apk. &lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoSelectDialogs/"&gt;Просмотреть&lt;/a&gt; исходные коды приложения DemoSelectDialog.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-5665656673628968153?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/5665656673628968153/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/12/android.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/5665656673628968153'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/5665656673628968153'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/12/android.html' title='Диалог выбора на Android или контекстное меню с картинками.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-US9wbmNE7Ps/TuwPCebPAAI/AAAAAAAAAE4/JcIbQuX5a1M/s72-c/dialog_screen.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-6579077743363153299</id><published>2011-10-28T11:44:00.000+08:00</published><updated>2011-10-28T11:44:02.753+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='туризм'/><category scheme='http://www.blogger.com/atom/ns#' term='о жизни'/><title type='text'>Три недели в Као-Лаке.Отдых в Таиланде в октябре 2011.</title><content type='html'>Несколько дней назад вернулся из Таиланда. Отдыхали всей семьей в курортном городе &lt;a href="http://www.ukrest.ru/ru/?m=1023"&gt;Као-Лак&lt;/a&gt; (Khaolak, 80 км от Пхукета) в течении трех недель. Отдохнули хорошо, хотя и не без проблем.&lt;br /&gt;&lt;br /&gt;Путешествовали мы самостоятельно, без турагентства. На Као-Лак летели через Бангкок. Из Бангкока мы успели уехать до начала наводнения, так что обошлось без экстрима. &lt;br /&gt;&lt;br /&gt;Ребенок у нас еще совсем маленький - менее двух лет. Так что отдых наш был несколько специфичным: пляж, отель, море, прогулки вблизи отеля, никаких экскурсий. Тем не менее, о чем рассказать - есть.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Подготовка и первоначальные расходы&lt;/h4&gt;Из Красноярска в Бангкок есть прямые регулярные рейсы авиакомпании &lt;a href="http://www.s7.ru/"&gt;S7&lt;/a&gt;. Билеты покупали за 4 мес. до вылета. Стоимость билета туда-обратно ~23 т.р. на взрослого и 2110 руб на ребенка до 2 лет.&lt;br /&gt;&lt;br /&gt;Из Бангкока на Као-Лак и обратно летели рейсами &lt;a href="http://airasia.com/"&gt;AirAsia&lt;/a&gt;. Билеты туда и обратно обошлись ~10 т.р.&lt;br /&gt;&lt;br /&gt;Отели бронировали через &lt;a href="http://www.agoda.ru"&gt;agoda.ru&lt;/a&gt;. Отель в Као-Лаке - &lt;a href="http://www.agoda.ru/asia/thailand/khao_lak_phang_nga/khaolak_laguna_resort.html"&gt;Khaolak Laguna Resort&lt;/a&gt; (вилла с завтраком, ~44 т.р). Отель в Бангкоке - &lt;a href="http://www.agoda.ru/asia/thailand/bangkok/pratunam_park_hotel.html"&gt;Pratunam Park Hotel&lt;/a&gt; (~3600 руб). &lt;br /&gt;&lt;br /&gt;Медицинскую страховку оформили в Ингосстрахе - 768 руб при страховой сумме в $15000.&lt;br /&gt;&lt;br /&gt;В процессе подготовки много полезной информации о Таиланде почерпнули &lt;a href="http://www.rino4ka.ru/"&gt;здесь&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Путешествие с ребенком. Особенности&lt;/h4&gt;Мы путешествовали с ребенком первый раз. Естественно, была куча вопросов: как везти коляску, как везти детское питание, сколько одежды брать и т.д.&lt;br /&gt;&lt;br /&gt;По факту, с коляской проблем не возникло вообще. Довозишь ребенка на коляске до входа в самолет. Возле самолета коляску у тебя забирают. Получаешь ее потом вместе с багажом. При регистрации билета нужно сообщить о коляске и получить для нее багажную квитанцию. Интересно, что когда мы летели в Бангкок, у нас коляску не забрали - я пронес ее в самолет как ручную кладь и ехала она на полке над сиденьем.&lt;br /&gt;&lt;br /&gt;Кстати, несмотря на то, что ребенок дома в коляске давно не ездит, в путешествии коляска очень пригодилась. Не представляю, как можно было бы обойтись без нее. Перемещаться приходилось много, ребенка на руках - не натаскаешься. Дождь, ты тащишь кучу чемоданов, а ребенок спит себе в коляске и нет проблем... У нас была коляска &lt;a href="http://krasbaby.ru/item1046.html"&gt;Baby Care NEW YORK&lt;/a&gt;. Легкая, компактно складывается, отлично амортизирует (ребенка не трясет даже на неровной дороге). Корзина внизу, накидка на ноги и (особенно!) дождевик были незаменимы. Рекомендую.&lt;br /&gt;&lt;br /&gt;Детского питания мы брали не слишком много, благо, ребенок уже давно лопает обычную еду. Часть питания мы сложили в чемоданы, часть - в ручную кладь. В ручную кладь питание брать нужно обязательно, т.к. всегда есть риск, что багаж потеряется, а оставить ребенка без еды никак нельзя. Когда мы летели в Бангкок, ручную кладь не взвешивали. А вот когда летели обратно - взвешивали, так что мне пришлось объяснять, что сумка такая тяжелея именно из-за детского питания. Стеклянные банки с питанием, сок в тетрапаках, маленькие бутылочки с водой - с ребенком пропускают все.&lt;br /&gt;&lt;br /&gt;Еще одна проблема - сколько брать с собой одежды? Мы приехали в Као-Лак в октябре, до начала сезона, и нам довелось увидеть, что такое тропические ливни. Влажность - чудовищная. Белье сохнет неделю. Так что либо брать для ребенка достаточное количество одежды, либо докупать одежду на месте. У влажности есть еще один неприятный эффект - когда собираешься ехать домой, вся одежда влажная.. и гораздо более тяжелая, чем обычно. &lt;br /&gt;&lt;br /&gt;Имеет смысл взять ребенку теплую кофточку. Во многих магазинах, ресторанах, в McDonalds работают кондиционеры - там достаточно прохладно, ребенок без кофточки может простудиться. Не повредит и рубашка с длинным рукавом, чтобы можно было закрыть ребенку руки от солнца. Впрочем, мы такую рубашку без проблем купили на месте - легкую, южную, у нас в Сибири таких не продают.&lt;br /&gt;&lt;br /&gt;Во всех ресторанах есть детские стульчики. В отелях предлагают (бесплатно) детские кроватки. Правда кроватки а-ля манеж, совершенно не удобные.&lt;br /&gt;&lt;br /&gt;Особенность Тайланда - феноменальный интерес к европейским детям. Куда бы ты не шел - куча народа хочет подойти к ребенку, заговорить с ним, притронуться к нему. Мы научили ребенка махать ручкой таким надоедливым типам, это нас и спасало. Помашет - таец заулыбается, покудахчет, помашет в ответ и пойдет дальше.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Khao Lak&lt;/h4&gt;Местечко Као-Лак - это, по большому счету, одна единственная улица, расположенная между горой и морем. Весь берег моря занят отелями. На горе - лес. На улице - куча ресторанов, магазинов (с одеждой, сувенирами и пляжными принадлежностями) оптик, салонов массажа, туристических агентств (предлагающих разнообразные экскурсии) и великих портных, готовых вам за небольшие деньги сшить костюм от Армани. &lt;br /&gt;&lt;br /&gt;Есть один большой супермаркет Nang Thong, плюс пара мелких. Имеется Макдональдс. Три аптеки на противоположной от Макдональдса стороне. &lt;br /&gt;&lt;br /&gt;Фрукты продаются на рынке, который работает три раза в неделю - понедельник, среда, суббота. Рынок расположен километрах в 5 от отеля, туда надо ехать на минибасе или такси. В принципе и пешком дойти не сложно, но на коляске туда не проехать - тротуары кончаются возле Nang Thong, дальше только шоссе, дорожки для пешеходов не предусмотрено. Стоимость минибаса до рынка 100 бат, такси - 150 бат (можно и за 100-120 договориться). &lt;br /&gt;&lt;br /&gt;Спиртное можно покупать в Nang Thong - дешевле чем у нас в России раза в 2. Бутылка 0.7 л London Dry Gin стоит ~550 бат, капитанского джина - 350 бат. Стоимость банки местного пива 0.33 на уровне 35-40 бат.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Погода&lt;/h4&gt;Мы отдыхали в Тайланде со 2 по 22 октября. А сезон там начинается в ноябре..&lt;br /&gt;&lt;br /&gt;В день приезда в Као-Лаке лили ливни. Льет час, перерыв на 10-15 минут и опять льет. Мы поначалу расстроились - ничего себе отдыхать приехали... И не только мы. Напротив немцы заселились - глаза у них натурально квадратные были.&lt;br /&gt;&lt;br /&gt;Впрочем, погода очень быстро наладилась. На следующей день дождь стал идти реже, а через пару дней появилось солнце. И стало так нещадно жарить, что мы с тоской вспоминали непогоду и очень радовались редким дождичкам. &lt;br /&gt;&lt;br /&gt;Первоначально пасмурная погода помогла нам адаптироваться к местному солнцу и не сгореть. Если бы мы приехали сразу в солнечную погоду - сгорели бы обязательно. С 11:00 до 15:00 носу из отеля не высунуть, такое пекло..&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Отель Khaolak Laguna Resort&lt;/h4&gt;От отеля осталось двойственное впечатление: есть как плюсы, так и минусы.&lt;br /&gt;&lt;br /&gt;В отеле, в основном, живут немцы. Русских за все время нашего пребывания была лишь одна пара. Попадались так же шведы, японцы.. &lt;br /&gt;&lt;br /&gt;Отель расположен достаточно удачно. Если встать лицом к отелю, то слева дорога поднимается в гору, справа, метрах в 200 от отеля, начинаются магазины. Отели слева уже располагаются на горе и менее удобны. &lt;br /&gt;&lt;br /&gt;Прямо напротив Khaolak Laguna Resort расположено несколько ресторанов - Gold Elephant, Sea &amp; Sand 2, Everyday, - в любом из них можно вкусно поесть за сумму в два раза меньшую, чем в отеле (500-700 бат против 1000-1300 бат). Нам особенно понравился Sea &amp; Sand 2, в котором есть не только тайская еда, но и европейские блюда. &lt;br /&gt;&lt;br /&gt;В самом Khaolak Laguna Resort куча лестниц. Номера расположены у моря, ресепшен и ресторан - на пригорке, так что на коляске на ресепшен и в ресторан не заехать. В остальном перемещение по отелю на коляске проблем не вызывает - предусмотрены съезды (персонал перемещается по территории отеля на мотоциклах).&lt;br /&gt;&lt;br /&gt;Ресторан в отеле хороший. Завтраки вкусные, более менее разнообразные. Персонал доброжелательный, повар веселый, здоровается с приезжающими на их языке (по русски не плохо говорит "Доброе утро" и "Спасибо"). &lt;br /&gt;&lt;br /&gt;Море было чистое (но не прозрачное), вода очень теплая. Большую часть времени на море были волны, иногда довольно большие - накупались всласть, гораздо интереснее, чем с спокойное море. Никаких медуз, ежей и т.д. - не встречали. &lt;br /&gt;&lt;br /&gt;Пляж отличный, песок мелкий. Народа на пляже нет - загорают все на лежаках, в отеле. Лежаки выше всяких похвал. На лежаках лежат матрасы. Приходишь - тебе тут же приносят махровую простынь на матрас, очень удобно. &lt;br /&gt;&lt;br /&gt;Бассейны в отеле отличные. Два взрослых бассейна, два детских - все с пресной водой. Во взрослых бассейнах есть джакузи. Бассейны чистые, вода проточная.&lt;br /&gt;&lt;br /&gt;Сам отель представляет из себя парк флоры и фауны. Вокруг домиков рассажено множество разнообразных фикусуов, кротонов, пальм и т.д. По всему отелю - бассейны с рыбками. По территории бассейна протекает река, в которой так же плавает множество рыб (по виду - здоровенные такие караси.. но кухни в домиках не предусмотрено). Есть несколько прудов с лягушками. Мелких ящериц вокруг - не счесть. Встречаются и здоровенные ящерицы, сантиметров 60 длинной, но их рассмотреть сложно, близко они не подпускают - убегают. В отеле "проживает" множество птичек. Народ рыбок и птичек активно кормит - в основном, вкусным белым хлебом, который дают на завтраке. &lt;br /&gt;&lt;br /&gt;Теперь о плохом. Самая большая проблема, на мой взгляд, это неудачный дизайн домиков, который портит все впечатление от отеля.&lt;br /&gt;&lt;br /&gt;Виллы натыканы очень близко друг к другу. Три стены в комнате - это окна и двери, так что вечером, когда включаешь свет - сидишь как в телевизоре. Надо закрывать шторы. Шторы римские, окон - штук 10. Не комфортно совершенно.&lt;br /&gt;&lt;br /&gt;Виллы тесные. Комната примерно 4 x 6 метров, в ней стоит вся мебель - кровать, диван, два кресла, столик с тумбочками и холодильником, телевизор на столике, маленький столик, пара прикроватных тумбочек. В принципе, с теснотой можно было бы смириться, если бы ни кондиционер.&lt;br /&gt;&lt;br /&gt;Кондиционер у них расположен прямо над кроватью. Воздух идет над кроватью, утыкается в стену и далее идет прямиком в кровать. Включил кондиционер - спать не возможно, сквозняк. У кондиционера три режима вентилятора. Даже если выставить минимальный уровень, сквозняк слишком сильный, чтобы можно было оставить кондишен включенным на ночь. Мы купили в супермаркете лист пластика, сделали из него короб, закрыли кондиционер и направили воздух прочь от кровати. Стало гораздо лучше. Но комната слишком тесная, потоки воздуха над кроватью все равно гуляют. Для взрослого может и ничего, для ребенка - недопустимо. &lt;br /&gt;&lt;br /&gt;В итоге, пришлось спать с выключенным кондиционером. Открывали окна на сквозняк, периодически ночью включали кондиционер, когда совсем туго становилось... Мало того, что жарко, так еще и москиты одолели. Не знаю - климат у них такой, или пруды с рыбками и лягушками дают о себе знать, - но москитов там дофига. Купленный аналог фумитокса не помог. Пришлось покупать в аптеке крем OFF от москитов, мазать себя и ребенка и спать намазанными. Так и жили.&lt;br /&gt;&lt;br /&gt;Забавно, что в более дешевых номерах (не виллах) кондиционеры расположены не над кроватями. Точнее, в половине номеров - не над кроватями, в другой половине - над кроватями. Тот, кто проектировал номера, явно не задумывался об этой проблеме.&lt;br /&gt;&lt;br /&gt;Еще пара мелких недостатков. В туалете нет вентиляции. Тусклое освещение. Крайне неудобная входная дверь. &lt;br /&gt;&lt;br /&gt;В комнате есть чайник и фен. Ежедневно выдают бесплатно две бутылки питьевой воды. Сейфа нет, но это не проблема - бесплатный сейф есть на ресепшене.&lt;br /&gt;&lt;br /&gt;Когда я резервировал отель, то выбирал отель с бесплатным Wi-Fi. В Khaolak Laguna Resort WI-FI в номерах практически не ловится (очень редко, в шкафу, в дождливую погоду, можно ненадолго подсоединиться). WI-FI работает на ресепшене, в ресторане, около бассейна и на море. Когда я пришел на ресепшен с жалобой на неработающий WI-FI, мне предложили подключить бесплатную кабельную сеть. Но для смартфонов кабельная сеть не актуальна.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Страховой случай&lt;/h4&gt;Ребенок у нас таки простыл от кондишена и сквозняков. Как-то ночью, примерно через две недели после начала путешествия, у ребенка поднялась температура 39. Мы были вынуждены позвонить в Москву, в Ингосстрах. Натолкнулись неожиданно на проблемы с билайном - звонок обошелся крайне дорого, утекло ~1500 руб. Плюс дозвониться не могли - какое-то время не было соединения. &lt;br /&gt;&lt;br /&gt;В остальном все прошло гладко. В 4 утра позвонили. В 7:30 приехал местный доктор, который улыбался так же широко, как доктор Ливси. Измерил ребенку температуру дистанционного, послушал ребенка, выяснил какие лекарства мы давали. Объяснил нам, что у ребенка кашель, бронхит и инфекция, что бывает очень часто у таких маленьких детей в таком климате. Выдал лекарства - антибиотик, жаропонижающее и два лекарства от кашля. Взял копию паспорта ребенка и листок страхового полиса. Выставил счет на 6 тыс. бат, я его подписал. Сказал, что температура должна упасть в течении 3 дней, если не упадет, снова звонить в Москву и вызывать его повторно.&lt;br /&gt;&lt;br /&gt;Лекарства помогли, ребенок быстро выздоровел. Кашель окончательно прошел примерно через неделю. &lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Перелет на Пхукет и обратно на Air Asia&lt;/h4&gt;Последний раз я летал AirAsia 5 лет назад. С тех пор они внедрили ряд усовершенствований.&lt;br /&gt;&lt;br /&gt;Во-первых, теперь при покупке билета явно указываешь, &lt;a href="http://www.airasia.com/my/en/flightinfo/supersizesave.page"&gt;сколько багажа с собой будет&lt;/a&gt;. Довольно удобно, можно брать до 30 кг на человека. &lt;br /&gt;&lt;br /&gt;При покупке билета желательно указать правильный вес чемоданов. Если будет перевес, будешь доплачивать. Причем у них на сайте явно указано, что в аэропорту цены гораздо выше, чем при оплате багажа заранее. &lt;br /&gt;&lt;br /&gt;Мы на это налетели. Когда мы летели в Пхукет, у нас был перевес 15 кг. Я доплатил ~500 бат - приемлемо. Когда мы летели обратно, у нас был перевес 13 кг. С нас запросили ~5000 бат. Естественно, мы отказались платить. Вместо этого - перепаковали чемодан, кое-что выкинули, сделали себе вторую сумку в ручную кладь. Улетели так, ничего не доплачивая. В AirAsia есть требования к ручной клади - ограничения на килограммы (7 кг) и на размер (56cm X 36cm X 23cm). Но по факту ее никто не взвешивает, и народ тащит внутрь довольно здоровые сумки. &lt;br /&gt;&lt;br /&gt;Во-вторых, теперь можно регистрироваться на рейс самостоятельно - через Web Check In. По сути, сам себе распечатываешь посадочный талон. Если регистрироваться в аэропорту, то будешь платить дополнительные деньги. &lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Банког, Pratunum Park Hotel&lt;/h4&gt;Последние два дня нашего путешествия мы провели в Банкоге. Это было 21-22 октября, наводнение в Банкоге еще не началось. По улицам были разложены баррикады из мешков с песком - вот и все, что свидетельствовало о чрезвычайной ситуации. Рынки и магазины работали на полную катушку.&lt;br /&gt;&lt;br /&gt;Мы остановились в отеле Pratunum Park Hotel. Отель понравился. Персонал приветливый, кондиционер не над кроватью:), питьевую воду дают, в комнате есть сейф. На мой взгляд - нормальный трехзвездочный отель, само то, чтобы перекантоваться пару дней. Бесплатный Wi-Fi уверенно работает в номерах и на ресепшене. Сеть закрытая, нужно брать у администратора пароли.&lt;br /&gt;&lt;br /&gt;Добираться до отеля просто. В аэропорту спускаешься на нулевой этаж, садишься на City Line. Доезжаешь до станции Ratchaprarop. Если смотреть со станции, отель находится за близстоящим небоскребом - примерно в 400 м от станции. Задача - обойти небоскреб.&lt;br /&gt;&lt;br /&gt;Спускаемся со станции, встаем лицом к дороге. Идем направо, доходим до первого широкого (подворотни не считаются) поворота направо - метров 50-100. Поворачиваем. Идем до первого поворота налево - тоже метров 100 (справа увидите несколько индийских ресторанов). Поворачиваем. За спиной небоскреб, впереди небоскреб, вокруг - рынок... Проходим метров 50, сворачиваем в первый поворот направо. Именно на этой улице стоит отель, справа. До него метров 100-150.&lt;br /&gt;&lt;br /&gt;Мы поначалу немножко поплутали там по улицам. Спустились со станции на улицу - а там не город, а рынок. Тротуары метр шириной, и на этом метре еще три лотка стоит со всякой всячиной. С коляской и чемоданами передвигаться очень не просто... Бангкок - это трущебы с небоскребами. &lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Шоппинг в Бангкоке&lt;/h4&gt;Мы сходили в местный "всемирный торговый центр" &lt;a href="http://ptnpark.com/ptn_map_big.gif"&gt;(Central World Plazza)&lt;/a&gt;, расположенный в 15 минутах хотьбы от отеля. Там два торговых центра, расположенных напротив друг друга. Нам больше всего понравился Setan. Там можно купить все что угодно. Забавно, цены на сувениры в таком огромном торговом центре ничуть не выше, чем в магазинчиках в Као-Лаке. &lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Dirty Free в аэропорте&lt;/h4&gt;Богатый Dirty Free, есть что выбрать. Спиртное стоит так же как в Као-Лаке. Хороший выбор парфюма. Вот только курс доллара там безумно невыгодный. Если вы планируете что-нибудь покупать в dirty free, лучше обменять доллары на баты в обменнике и тратить баты. &lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Общие впечатления&lt;/h4&gt;Очень понравились море и пустынный пляж в Као-Лак. Собственно, за этим мы туда и ехали. Огорчила проблема с кондиционером в достаточно недешевом отеле. Не ожидали. О Бангкоке сложилось впечатление как о большом рынке. В целом, Тайланд показался гораздо менее цивилизованным, чем та же Малайзия.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-6579077743363153299?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/6579077743363153299/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/10/2011.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6579077743363153299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6579077743363153299'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/10/2011.html' title='Три недели в Као-Лаке.Отдых в Таиланде в октябре 2011.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-7615109631442213163</id><published>2011-09-15T22:01:00.001+08:00</published><updated>2011-11-09T18:15:51.485+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Книга Version Control by Example в свободном доступе.</title><content type='html'>Eric Sink &lt;a href="http://www.ericsink.com/vcbe/index.html"&gt;раздает&lt;/a&gt; на своем сайте свою новую книжку, посвященную системам контроля верстки: Version Control by Example (SVN, GIT, Mercurial, Veracity). Раздает бесплатно, причем можно даже заказать в &lt;b&gt;бумажном&lt;/b&gt; виде. &lt;br /&gt;&lt;br /&gt;Книжка интересная, так что заказал. Бумажный вариант читать все-таки на порядок удобнее, чем PDF.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 9.11.2011&lt;/b&gt; Пришла книжка. Почитаем...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-7615109631442213163?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/7615109631442213163/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/09/version-control-by-example.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/7615109631442213163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/7615109631442213163'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/09/version-control-by-example.html' title='Книга Version Control by Example в свободном доступе.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-3300886082260178644</id><published>2011-09-12T22:50:00.008+08:00</published><updated>2012-02-13T09:53:39.109+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='о жизни'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Книги которые стоит прочитать. Разные версии топов.</title><content type='html'>Хороших книг мало, и выловить их из книжного моря - не просто. То ли дело фильмы - в этой области работает &lt;a href="www.exler.ru"&gt;Экслер&lt;/a&gt;, рецензии которого позволяют составить более менее внятное представление о фильмах, режиссерах, актерах и состоянии дел в кинематографе. И всегда подобрать что-нибудь для души.&lt;br /&gt;&lt;br /&gt;С книгами все не так. Авторов и книг на несколько порядков больше, чем режиссеров и фильмов. "Объективно" оценить книгу, похоже, гораздо сложнее, чем фильм. Сплошь и рядом - рецензия отличная, интригует; начинаешь читать - туфта. Бывает и наоборот. &lt;br /&gt;&lt;br /&gt;Хорошо помогают сориентироваться в книжном многообразии списки "лучших" книг. Просишь кого-нибудь составить список самых лучших, на его взгляд, книжек. Просматриваешь - если со многими позициями согласен, то есть неплохие шансы, что достойны прочтения и неизвестные тебе книги/авторы из этого списка. Разумеется, желательно чтобы "кто-нибудь" был ни "абы кто".&lt;br /&gt;&lt;br /&gt;В интернете выложено множество подобных списков от самых разных составителей. Попробую в этом топике собрать ссылки на самые интересные списки. А заодно, на интересные книжные блоги.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;h4&gt;Списки на русском&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.yojo.ru/?p=4303"&gt;100 книг, которые стоит прочитать - на портале YOJO.ru&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://rekomenda.ru/recomendation/StrugatskyBoris/"&gt;Отзывы Бориса Стругацкого о книгах&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.afisha.ru/article/peresortica/"&gt;Как сортировать нон-фикшн&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.exler.ru/blog/item/7906/"&gt;Рекомендованные книги в одной табличке&lt;/a&gt; - плод коллективных усилий читателей блога Экслера.&lt;/li&gt;&lt;li&gt;&lt;a href="http://habrahabr.ru/company/mosigra/blog/110797/"&gt;N+1 полезных книг о бизнесе (Хабрахабр)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Топик в тему на &lt;a href="http://dirty.ru/comments/294334#5829396"&gt;dirty.ru&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Списки 10 лучших научпоп-книг о математике: &lt;a href="http://www.afisha.ru/article/sci-fi-mathematics/?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+afisha_books+%28Afisha%3A+Books%29"&gt;на руссом&lt;/a&gt;, &lt;a href="http://m.guardian.co.uk/books/2012/jan/18/ian-stewart-top-10-popular-mathematics?cat=books&amp;type=article"&gt;на английском.&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Владимир Путин &lt;a href="http://www.ng.ru/politics/2012-01-23/1_national.html"&gt;предложил&lt;/a&gt; составить список из 100 книг, которые будут обязательными к прочтению для выпускников школ. &lt;a href="http://newsru.com/russia/23jan2012/putinmigrants.html"&gt;Вот что предложили&lt;/a&gt; деятели культуры, &lt;a href="http://www.superjob.ru/community/literary/62702/"&gt;вот народная версия списка&lt;/a&gt;, а вот и &lt;a href="http://www.pro-goroda.ru/perm/news/100-knig-dlya-putina.html"&gt; альтернативный вариант&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.afisha.ru/article/the-book-about-the-greatest-books/?utm_source=feedburner"&gt;Главные книжки всех времен и народов по мнению писателей&lt;/a&gt; - "результаты опроса 125 крупнейших современных англоязычных писателей на тему — Главные Книжки Всех Времен и Народов". &lt;a href="http://dirty.ru/comments/337556"&gt;Обсуждение&lt;/a&gt; на dirty.ru&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;Списки на английском&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.telegraph.co.uk/culture/books/3672376/110-best-books-The-perfect-library.html"&gt;Daily Telegraph: 110 best books: The perfect library&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.telegraph.co.uk/culture/books/4248401/100-novels-everyone-should-read.html"&gt;Daily Telegraph: 100 novels everyone should read&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.telegraph.co.uk/news/uknews/1544033/The-top-100-books.html"&gt;Daily Telegraph: The top 100 books&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Daily Telegraph: 100 books every child should read - &lt;a href="http://www.telegraph.co.uk/culture/books/3670595/100-books-every-child-should-read-Pt-1.html"&gt;part 1&lt;/a&gt;, &lt;a href="http://www.telegraph.co.uk/arts/main.jhtml?xml=/arts/2008/01/19/bokidsbooks319.xml"&gt;middle years&lt;/a&gt;, &lt;a href="http://www.telegraph.co.uk/arts/main.jhtml?xml=/arts/2008/01/19/bokidsbooks419.xml"&gt;early teens&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.time.com/time/specials/packages/completelist/0,29569,2088856,00.html"&gt;All-TIME 100 Best Nonfiction Books&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.bbc.co.uk/arts/bigread/top200.shtml"&gt;BBC - The Big Read - Top 200 Books (august 2004)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://100bestbiz.com/more-on-the-100-best/"&gt;The 100 Best Business Books of All Time&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;Книжные блоги&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.afisha.ru/daily/books/"&gt;Афиша. Книги&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;Специализированные списки&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.dejurka.ru/web-design/25-best-books-for-web-designer/"&gt;25 лучших книг для веб-дизайнеров и разработчиков по итогам 2011 года&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-3300886082260178644?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/3300886082260178644/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/09/blog-post.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/3300886082260178644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/3300886082260178644'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/09/blog-post.html' title='Книги которые стоит прочитать. Разные версии топов.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-7386477766313479377</id><published>2011-07-11T17:50:00.007+08:00</published><updated>2012-01-31T22:23:42.408+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Производительность Android приложения.</title><content type='html'>Очень часто приходится слышать о проблемах с производительностью Android приложений. Начинаешь разрабатывать собственное приложение - точно, скорости не хватает, какие-то непонятные тормоза лезут... По моему опыту, вопросом - "что можно сделать, чтобы поднять производительность приложения на Android", - задаешься очень быстро. Особенно после первого релиза своего первого приложения :)&lt;br /&gt;&lt;br /&gt;Традиционно в низкой производительности Android приложений винят Java. Но только ли Java виноват? Не только. Во многих случаях виноват вовсе не он.&lt;br /&gt;&lt;br /&gt;Производительность приложения зависит от многих аспектов. И в первую очередь, от грамотности использования имеющихся в вашем распоряжении инструментов - языка программирования, библиотек, утилит. Повышение производительности - это комплексный вопрос, и подходить к нему нужно разносторонне. &lt;br /&gt;&lt;br /&gt;Цель этой статьи - перечислить основные моменты, влияющие на производительность. Моменты, на которые стоит обратить самое пристальное внимание как при кодировании, так и при проектировании приложения.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;h4&gt;Общие рекомендации Google&lt;/h4&gt;Первое, что следует прочитать - раздел &lt;a href="http://developer.android.com/guide/practices/design/performance.html"&gt;Designing for Performance&lt;/a&gt; (&lt;a href="http://habrahabr.ru/blogs/android_development/123707/"&gt;перевод&lt;/a&gt;) в Android developers guide. В нем перечислены основные правила, которые следует соблюдать при кодировании на Java под Android.&lt;br /&gt;&lt;br /&gt;Ключевая рекомендация - избегайте создание ненужных объектов. Чем меньше объектов создается, тем реже вызывается сборщик мусора, тем меньше используется память. Идея очевидна, но вот избегать создания лишних объектов ой как не просто...&lt;br /&gt;&lt;br /&gt;Отмечу рекомендацию касающуюся циклов "for-each": для ArrayList классический цикл с счетчиком примерно в 3 раза быстрее, чем "for-each", а для прочих коллекций, "for-each" предпочтительнее. Лично я использовал "for-each" везде, опрометчиво считая, что "компилятор сам разберется". Оказывается это не так. Так что для ArrayList следует писать:&lt;br /&gt;&lt;pre class="brush:java"&gt;int size = list.size();&lt;br /&gt;for (int i = 0; i &amp;lt; size; ++i) {&lt;br /&gt;  ItemType item = list.get(i);&lt;br /&gt;....&lt;br /&gt;}&lt;/pre&gt;а для прочих коллекций&lt;pre class="brush:java"&gt;for (ItemType item : list) {&lt;br /&gt;....&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;Грамотная работа с UI&lt;/h4&gt;В использовании Android UI есть определенные тонкости, о которых желательно знать. Подробно о них рассказывается в докладе &lt;a href="http://www.youtube.com/watch?v=N6YdwzAvwOA"&gt;Google I/O 2009 - ...Make your Android UI Fast and Efficient&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Затрагиваются следующие вопросы:&lt;ul&gt;&lt;li&gt;Эффективная реализация адаптеров для ListView, использующая ViewHolder.&lt;/li&gt;&lt;li&gt;Масштабирование картинок - дорогая операция, в реальном времени лучше его избегать - "runtime scaling is very expensive"&lt;/li&gt;&lt;li&gt;Если бакграунт не нужен, его прорисовку следует исключить (по умолчанию бакраунд рисуется, даже если он полностью закрыт контролами; отключается через стиль "Theme.NoBackground").&lt;/li&gt;&lt;li&gt;Invalidate очень дорог, если возможно, лучше использовать Invalidate(Rect)&lt;/li&gt;&lt;li&gt;Чем больше видов (view) в активити, тем медленнее работа. Существует ряд приемов, позволяющих снизить количество используемых view: compound drawable, ViewStub, тег &lt;a href="http://android-developers.blogspot.com/2009/03/android-layout-tricks-3-optimize-by.html"&gt;merge&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Корректная реализация custom views и custom layouts&lt;/li&gt;&lt;li&gt;Список функций, в которых крайне не рекомендуется выделять память.&lt;/li&gt;&lt;li&gt;Использование SoftReferences для кешей и WeakReferences для исключения утечек памяти.&lt;/li&gt;&lt;/ul&gt;(&lt;b&gt;Update&lt;/b&gt;: см. так же раздел &lt;a href="http://developer.android.com/training/improving-layouts/index.html"&gt;Improving Layout Performance&lt;/a&gt; в &lt;a href="http://developer.android.com/training/index.html"&gt;Android Training&lt;/a&gt;).&lt;br /&gt;Отмечу несколько моментов. Во первых, в докладе представлен список функций, которые желательно делать максимально легковесными и в которых, соответственно, желательно не выделять память:&lt;ul&gt;&lt;li&gt;Measurement: onMeasure()&lt;/li&gt;&lt;li&gt;Layout: onLayout()&lt;/li&gt;&lt;li&gt;Drawing: draw(); dispatchDraw(); onDraw();&lt;/li&gt;&lt;li&gt;Events handling: dispatchTouchEvent(); onTouchEvent();&lt;/li&gt;&lt;li&gt;Adapters: getView(); bindView();&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Во-вторых, на одном из слайдов (48:15) показан интересный прием. Предположим, мы пишем супер производительную функцию, в которой нигде не должна выделяться память. Как убедиться в том, что память действительно не выделяется? Вот так:&lt;br /&gt;&lt;pre class="brush:java"&gt;int prevLimit = -1;&lt;br /&gt;try {&lt;br /&gt;  //Limit the number allocated objects&lt;br /&gt;  prevLimit = Debug.setAllocationLimit(0);&lt;br /&gt;&lt;br /&gt;  //Execute code that shouldn't perform&lt;br /&gt;  //any allocations&lt;br /&gt;} finally {&lt;br /&gt; Debug.setAllocationLimit(prevLimit);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;При попытке выделить память будет сгенерировано исключение.&lt;br /&gt;&lt;br /&gt;В третьих, на одном из листингов (53:25) приведен пример использования SoftReference для организации кеша битмапок. Более полные варианты реализации представлены &lt;a href="http://www.javaspecialists.eu/archive/Issue098.html"&gt;здесь&lt;/a&gt; и &lt;a href="http://code.google.com/p/parleys-androidclient/source/browse/trunk/ParleysClient/src/com/parleys/helper/SoftHashMap.java?r=45"&gt;здесь&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Наконец, обратите внимание на слайд (30:43). Здесь рассказывается про "Compound drawable" - возможность заменить layout, содержащий TextView и ImageView, на единственный TextView. Чем меньше view, тем быстрее приложение. Лично я не знал про такую возможность и использовал более громоздкий код, чем требовалось.&lt;br /&gt;&lt;h4&gt;Снижение времени отклика приложения&lt;/h4&gt;Пользователь считает, что приложение "тормозит", если время отклика приложения превышает 100-200 мс. Еще хуже, когда задержка становится слишком большой, Android решает, что приложение зависло и появляется диалог ANR с предложением закрыть приложение. &lt;br /&gt;&lt;br /&gt;Как уменьшить время отклика? Кое-какие рекомендации есть в руководстве разработчика, в разделе &lt;a href="http://developer.android.com/guide/practices/design/responsiveness.html"&gt;Designing for Responsiveness&lt;/a&gt;. Основная идея - длительные операции нужно выносить из главного потока во второстепенные.&lt;br /&gt;&lt;br /&gt;Несколько больше информации в докладе &lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=c4znvD-7VDA"&gt;Google I/O 2010 - Writing zippy Android apps&lt;/a&gt;. Здесь затрагиваются вопросы производительности sqllite (в ряде случаев обычный файл предпочтительнее), рассматриваются варианты использования AsyncTask и intentService, демонстрируются возможности профайлера TraceView.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Корректное использование памяти&lt;/h4&gt;Правильно ли ваше приложение использует память? Нет ли утечек? Работа с памятью рассмотрена в докладе &lt;a href="http://www.youtube.com/watch?v=_CruQY55HOk"&gt;Google I/O 2011: Memory management for Android Apps&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;В частности, объясняется механизм возникновения утечек памяти. Типичная причина - создание долгоживущих ссылок на Context (в руководстве разработчика этот момент так же детально &lt;a href="http://developer.android.com/resources/articles/avoiding-memory-leaks.html"&gt;рассмотрен&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Здесь же приведена интересная информация про Bitmap. До Honeycomb битмапки создавались в неуправляемой памяти. Для освобождения памяти, занимаемой битмапкой, необходимо было ждать вызова финалайзера или вручную вызывать метод recycle (вы его вызваете?). Начиная с Honeycomb битмапки создаются в управляемой памяти.&lt;br /&gt;&lt;br /&gt;Для поиска утечек памяти автор доклада рекомендует использовать &lt;a href="http://eclipse.org/mat/downloads.php"&gt;Eclipse Memory Analyzer (MAT)&lt;/a&gt;. Подробности в блоге &lt;a href="http://kohlerm.blogspot.com/"&gt;Java Performance blog&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Производительность в играх&lt;/h4&gt;Вопросам производительности игр посвящены доклады&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=7-62tRHLcHk"&gt;Google I/O 2010 - Writing real-time games for Android redux&lt;/a&gt; и &lt;a href="http://www.youtube.com/watch?v=U4Bk5rmIpic"&gt;Google I/O 2009 - Writing Real-Time Games for Android&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;&lt;h4&gt;SparseArray вместо HashMap&lt;/h4&gt;Вместо HashMap&amp;lt;Integer, E&amp;gt; можно использовать &lt;a href="http://developer.android.com/reference/android/util/SparseArray.html"&gt;android.utils.SparseArray&amp;lt;E&amp;gt;&lt;/a&gt;. У этого класса почти такой же интерфейс, как у HashMap, но с важным отличием: методы get и put работают с int, а не с Integer. Таким образом, при использовании SparseArray исключаются лишние boxing операции. В многих случаях, исключение создания лишних Integer-объектов более важно, чем небольшое (потенциальное) снижение в скоростях доступа.&lt;br /&gt;&lt;br /&gt;В android.utils есть еще пара аналогичных классов - &lt;a href="http://developer.android.com/reference/android/util/SparseIntArray.html"&gt;android.utils.SparseIntArray&lt;/a&gt; и &lt;a href="http://developer.android.com/reference/android/util/SparseBooleanArray.html"&gt;SparseBooleanArray&lt;/a&gt; - замена для HashMap&amp;lt;Integer, Integer&amp;gt; и HashMap&amp;lt;Integer, Boolean&amp;gt; соответственно.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Выводы&lt;/h4&gt;При разработке приложения под Android существует множество приемов, особенностей и тонкостей, напрямую влияющих на производительность. Уверен, что вышеприведенный список далеко не полон. Потихоньку постараюсь его пополнять.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt; Имеет смысл периодически проверять приложение на предмет неиспользуемых ресурсов... и удалять их. Проверять можно, &lt;a href="http://stackoverflow.com/questions/3760033/find-out-if-resource-is-used"&gt;например&lt;/a&gt;, с помощью утилиты &lt;a href="http://code.google.com/p/android-unused-resources/"&gt;Android Unused Resource Detector&lt;/a&gt; (начиная с Android SDK версии r16 для этих целей можно использовать встроенный статический анализатор &lt;a href="http://tools.android.com/tips/lint"&gt;Lint&lt;/a&gt;. Так же как Android Unused Resource Detector, Lint порой выдает ложные сообщения о неиспользуемых ресурсах, так что нужно быть очень внимательным, чтобы не удалить лишнего).&lt;br /&gt;&lt;br /&gt;Полезная статья &lt;a href="http://www.curious-creature.org/2009/03/04/speed-up-your-android-ui/"&gt;Speed up your Android UI&lt;/a&gt; про отключение ненужной прорисовки фона.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt; Многие &lt;a href="http://derevyanko.blogspot.com/2012/01/android.html"&gt;статические анализаторы&lt;/a&gt; умеют отлавливать неоптимальный код, у них есть чему поучиться. FindBugs, CodePro, PMD, Lint - у всех у них есть правила типа Performance или Optimization.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update, январь 2012&lt;/b&gt; Скоро выйдет интересная книжка по теме: &lt;a href="http://www.amazon.com/Pro-Android-Apps-Performance-Optimization/dp/1430239999/ref=sr_1_2?s=books&amp;ie=UTF8&amp;qid=1327230013&amp;sr=1-2"&gt;Hervé Guihot. Pro Android Apps Performance Optimization&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-7386477766313479377?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/7386477766313479377/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/07/android_11.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/7386477766313479377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/7386477766313479377'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/07/android_11.html' title='Производительность Android приложения.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-8225881619214197860</id><published>2011-07-06T17:40:00.006+08:00</published><updated>2011-07-09T10:58:49.455+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='о жизни'/><title type='text'>Мультфильмы для самых маленьких</title><content type='html'>Какие мультики показывать малышам в возрасте 1-3 года? Большинство обычных мультфильмов им не интересны - они до них еще не доросли. Малышам нужны мультфильмы попроще... и, желательно, с обучающим эффектом. Ниже - список таких мультфильмов.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;h4&gt;Мультфильмы студии Aardman&lt;/h4&gt;У английской студии &lt;a href="http://ru.wikipedia.org/wiki/Aardman_Animations"&gt;Aardman_Animations&lt;/a&gt;, создателей Уоллеса и Громмита, есть два отличных мульт-сериала для малышей: &lt;a href="http://ru.wikipedia.org/wiki/Барашек_Шон"&gt;Shaun the Sheep (Барашек Шон)&lt;/a&gt; и &lt;a href="http://rutracker.org/forum/viewtopic.php?t=2832388"&gt;Timmy Time (Время барашка Тимми)&lt;/a&gt;. Мультфильмы про барашка Шона с удовольствием посмотрят и взрослые. Кстати, сейчас студия делает &lt;a href="http://www.lenta.ru/news/2011/01/28/shaun/"&gt;полнометражный фильм про Барашка Шона&lt;/a&gt;, который выйдет через пару лет.&lt;br /&gt;&lt;h4&gt;Иностранные мультсериалы&lt;/h4&gt;&lt;a href="http://rutracker.org/forum/viewtopic.php?t=3318123"&gt;Hutos ("Малыши")&lt;/a&gt; - коррейское развлекательно-познавательное шоу для детей дошкольного возраста, аналог английских "Телепузиков" (которых в этот список, пожалуй, включать &lt;a href="http://www.mr7.ru/news/society/story_19523.html"&gt;не стоит&lt;/a&gt;). Сериал идет на канале Радость моя.&lt;br /&gt;&lt;br /&gt;Американский обучающий сериал &lt;a href="http://rutracker.org/forum/viewtopic.php?t=3295042"&gt;"Mickey Mouse Clubhouse" ("Клуб Микки Мауса")&lt;/a&gt;, для детей от 2 лет (в комментариях пишут что и годовички смотрят с удовольствием).&lt;br /&gt;&lt;br /&gt;Мультсериалы для маленьких от канала TiJi Россия:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://rutracker.org/forum/viewtopic.php?t=2479463"&gt;Бали / Bali&lt;/a&gt; - приключения пёсика Бали и его друзей&lt;/li&gt;&lt;li&gt;корейский сериал &lt;a href="http://en.wikipedia.org/wiki/Pororo_the_Little_Penguin"&gt;"Pororo the Little Penguin"&lt;/a&gt; ("Пингвиненок пороро"), для детей до 3х лет&lt;/li&gt;&lt;li&gt;Французский сериал &lt;a href="http://rutracker.org/forum/viewtopic.php?t=2354196"&gt;Little Hippo / Малыш Хиппо&lt;/a&gt; - приключения бегемотика Хиппо&lt;/li&gt;&lt;li&gt;&lt;a href="http://rutracker.org/forum/viewtopic.php?t=2718203"&gt;Ныряй глубже, Олли! / Dive Olly Dive&lt;/a&gt; - приключения маленькой подводной лодки Олли.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;Методика Глена Домана&lt;/h4&gt;Карточки Глена Домана давно перенесли на компьютер. Есть как минимум два варианта: набор презентаций в Power Point, называется &lt;a href="http://www.ozon.ru/context/detail/id/4869711/"&gt;Вундеркинд с пеленок&lt;/a&gt; (на разных языках: английский, русский, украинский и т.д.), а так же &lt;a href="http://www.ozon.ru/context/detail/id/5212178/"&gt;видеовариант&lt;/a&gt;. Малышам очень нравится. Только не следует забывать, что по методики Глена Домана с ребенком &lt;a href="http://victorygame.info/news/Раннее-развитие-малыша-по-методике-Домана-Маниченко-1"&gt;следует заниматься&lt;/a&gt;, а не просто включать ему видео как обычный мультик.&lt;br /&gt;&lt;h4&gt;Обучающие мультфильмы Роберта Саакянца&lt;/h4&gt;Все помнят серию мультфильмов - "Три синих синих озера малинового цвета", "Ишь ты, Масленица!", "Ух ты, говорящая рыба!" и т.д? Режиссер - &lt;a href="http://ru.wikipedia.org/wiki/Саакянц,_Роберт_Аршавирович"&gt;Роберт Саакянц&lt;/a&gt;, - выпустил еще и ряд обучающих мультфильмов для малышей: "Азбука для малышей", "Арифметика для малышей", "Английский язык для малышей", "Геометрия для самых маленьких", "Астрономия для самых маленьких" и так далее. Мультфильмы расчитаны на детей от 2 до 8 лет. Продаются в интернет магазинах, в частности, в &lt;a href="http://www.labirint.ru/search/Роберт%20Саакянц/?genre="&gt;Лабиринте&lt;/a&gt;. Имейте ввиду, что в некоторых мультиках есть неточности, &lt;a href="http://rutracker.org/forum/viewtopic.php?t=1542612"&gt;см. комментарии&lt;/a&gt;.&lt;br /&gt;&lt;h4&gt;Развивающие мультфильмы&lt;/h4&gt;Сериал &lt;a href="http://en.wikipedia.org/wiki/Baby_Einstein"&gt;Baby Einstein&lt;/a&gt; предназначен для детей от 3х месяцев до 3х лет.&lt;br /&gt;Обучающие видеопрограммы &lt;a href="http://rutracker.org/forum/viewtopic.php?t=1104853"&gt;"Baby Signing Time" ("Будем объясняться жестами")&lt;/a&gt; (аналог Baby Einstein).&lt;br /&gt;Мультфильмы &lt;a href="http://rutracker.org/forum/viewtopic.php?t=3229717"&gt;Tiny Love&lt;/a&gt;. &lt;br /&gt;Сериал &lt;a href="http://ru.wikipedia.org/wiki/Даша-следопыт"&gt;"Даша путешественница" ("Dora the Explorer")&lt;/a&gt;. &lt;br /&gt;Мультфильм &lt;a href="http://rutracker.org/forum/viewtopic.php?t=2487829"&gt;"Умный ребенок. Первые детские слова" ("Baby IQ. Baby`s first words")&lt;/a&gt;.&lt;br /&gt;&lt;h4&gt;Российские мультфильмы&lt;/h4&gt;Мультипликационный сериал &lt;a href="http://ru.wikipedia.org/wiki/Мультипликационный сериал"&gt;Маша и медведь&lt;/a&gt; студии &lt;a href="http://www.animaccord.ru/?page_id=31"&gt;Анимаккорд&lt;/a&gt;.&lt;br /&gt;Мультипликационный сериал &lt;a href="http://ru.wikipedia.org/wiki/Смешарики"&gt;Смешарики&lt;/a&gt;, созданный студией &lt;a href="http://ru.wikipedia.org/wiki/Петербург_(студия_анимации)"&gt;СКА "Петербург"&lt;/a&gt; (кстати, они сейчас делают полнометражный фильм &lt;a href="http://ru.wikipedia.org/wiki/Смешарики._Начало"&gt;"Смешарики. Начало"&lt;/a&gt;).&lt;br /&gt;Мультипликационный сериал &lt;a href="http://ru.wikipedia.org/wiki/Приключения_Лунтика_и_его_друзей"&gt;Приключения Лунтика и его друзей&lt;/a&gt;, созданный студией &lt;a href="http://ru.wikipedia.org/wiki/Студия_анимационного_кино_«Мельница»"&gt;Мельница&lt;/a&gt; (той самой, что делает мультики про богатырей).&lt;br /&gt;&lt;h4&gt;Советские мультфильмы&lt;/h4&gt;Мы их все хорошо знаем, перечислять не буду :)&lt;br /&gt;&lt;h4&gt;В итоге&lt;/h4&gt;Естественно, список субъективный и неполный. Со временем буду его пополнять.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-8225881619214197860?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/8225881619214197860/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/07/blog-post.html#comment-form' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8225881619214197860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8225881619214197860'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/07/blog-post.html' title='Мультфильмы для самых маленьких'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-8911206756938694730</id><published>2011-07-04T10:40:00.004+08:00</published><updated>2012-02-02T09:30:34.444+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Сбор статистики в приложении на Android</title><content type='html'>Всегда интересно знать, как пользователи работают с вашим приложением: какие функции наиболее востребованы, какие кнопки нажимаются, какие настройки меняются, какие ошибки совершаются. Неплохо так же представлять себе, что это за пользователи - какая версия прошивки у их девайсов, где они географически расположены и т.д. &lt;br /&gt;&lt;br /&gt;Такая информация неоценима для разработчика, ведь с ее помощью можно оптимизировать юзабилити, определиться с приоритеностью функционала, найти пути повышения продаж. Вопрос только как такую статистику собирать.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Самый простой путь - воспользоваться готовой системой сбора аналитической информации. Таких систем сейчас более чем достаточно (&lt;a href="http://www.foundersspace.com/tech/whats-the-best-analytics-service-for-apps/"&gt;1&lt;/a&gt;, &lt;a href="http://www.quora.com/What-is-the-best-Mobile-Analytics-service-for-iOS-apps-and-why"&gt;2&lt;/a&gt;). Например:&lt;ul&gt;&lt;li&gt;&lt;a href="http://apsalar.com/"&gt;Apsalar (бесплатная)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://hub.buzzbox.com/"&gt;BuzzBox (бесплатная)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.flurry.com/"&gt;Flurry (бесплатная)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/intl/ru-RU/mobile/analytics/docs/"&gt;Google Analytics (бесплатная)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://mixpanel.com/"&gt;Mixpanel (платная)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.localytics.com/"&gt;Localitics (платная)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://bango.com/mobileanalytics/"&gt;Bango (платная)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;У каждой системы есть свои изюминки: real-time обновление статистики (Localitics), суперточность с отслеживанием уникальных ID каждого пользователя (Bango), наличие средств для проведения опроса пользователей (Apsalar), наличие средств для регулярной отправки уведомлений пользователям (BuzzBox) и т.д. Естественно, есть и море отличий: в интерфейсе, в средствах анализа, в наличии дополнительных API, в стоимости, в наборе поддерживаемых платформ и т.п. &lt;br /&gt;&lt;br /&gt;Принцип работы у всех систем примерно одинаков. Регистрируемся на сайте системы, получаем SDK и уникальный ключ. В приложение подключаем стороннюю библиотеку (единственный jar-файл из SDK). Добавляем код вызова функций, регистрирующих события в приложении. Релизим приложение. Ждем некоторое время, пока пользователи поработают в программе. Заходим на сайт системы и смотрим разнообразные аналитические отчеты. &lt;br /&gt;&lt;br /&gt;Вот, например, как ведется сбор данных в Apsalar:&lt;pre class="brush:java"&gt;import com.apsalar.sdk.Apsalar;&lt;br /&gt;&lt;br /&gt;public void onCreate(Bundle savedInstanceState) {&lt;br /&gt;...&lt;br /&gt;Apsalar.startSession(this, "myapikey", "mypassword");&lt;br /&gt;..&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/** уровень level пройден за countSeconds */&lt;br /&gt;public void onLevelPassed(int level, int countSeconds) {&lt;br /&gt;...&lt;br /&gt;  Apsalar.event("passed", "level", level, "duration", countSeconds);&lt;br /&gt;...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;API, как видите, тривиальное, так что технически добавить код для фиксации событий в приложении совсем не сложно. Количество событий и количество атрибутов у каждого события не ограничено, поэтому детализация собираемой информации может быть весьма высокой. &lt;br /&gt;&lt;br /&gt;Конечно, подобные системы сбора информации - это, прежде всего, инструмент аналитика, желающего увеличить доход от продажи приложения. И накапливаемая статистика событий в приложении - всего лишь первичный материал для анализа. Тот же Apsalar позволяет, например, проводить &lt;a href="http://apsalar.com/apscience/features/"&gt;Funnel Analysis, Engagment Analysis, Trending Analysis, Path Analysis, Event Analysis, Operational Analysis&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Взять к примеру Funnel Analysis (воронку продаж). Если в вашем приложении пользователь должен сделать несколько шагов к совершению покупки (последовательно открыть несколько страниц, нажать несколько кнопок), то Funnel Analysis позволит увидеть, сколько пользователей (и каких именно) отсеивается на каждом шаге. Любопытно, что существуют системы монетизации, активно &lt;a href="http://moneynews.ru/AuthorsArticle/14844/"&gt;использующие&lt;/a&gt; подобную информацию.&lt;br /&gt;&lt;br /&gt;Резюме. Готовых систем сбора аналитической информации для Android приложений сейчас множество, выбор широк. Многие из них бесплатные и, одновременно, обладают достойным функционалом. Подключаются подобные системы тривиально, весят "копейки" (10-30 kb), а пользу разработчику могут принести немалую. Единственное но: не у всех пользователей неограниченный трафик. Так что пользователи должны иметь возможность сбор статистики отключать.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;: Полезная статья на хабре &lt;a href="http://habrahabr.ru/blogs/android_development/122510/"&gt;Flurry Analytics. Как держать руку на пульсе приложения&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://apsalar.com/blog/"&gt;Блог&lt;/a&gt; компании Apsalar.&lt;br /&gt;Еще пара сервисов: &lt;a href="http://www.appoxee.com/"&gt;Appoxee&lt;/a&gt;, &lt;a href="http://www.appfigures.com/"&gt;AppFiguers&lt;/a&gt;. По поводу Appoxee см. так же &lt;a href="http://appsmarketing.mobi/deliver-post-app-download-engagement-appoxee-mobile/"&gt;how to create post app download engagement.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-8911206756938694730?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/8911206756938694730/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/07/android.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8911206756938694730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8911206756938694730'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/07/android.html' title='Сбор статистики в приложении на Android'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-6791313568999289496</id><published>2011-05-12T19:56:00.000+08:00</published><updated>2011-05-14T04:43:38.660+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Конвертация даты из внутреннего формата MSSQL</title><content type='html'>Переводил базу из MsSQL в MYSQL. Стандартные инструменты из &lt;a href="http://dev.mysql.com/doc/migration-toolkit/en/"&gt;MySQL Migration Toolkit&lt;/a&gt; оказались бессильны перед этой базой - кодировка портилась, хоть тресни. Пришлось конвертить ручками, через SQL скрипты. Все было хорошо.. пока не обнаружилось, что в MSSQL-ном скрипте в &lt;i&gt;некоторых&lt;/i&gt; таблицах данные типа DateTime оказались записаны в виде:&lt;pre&gt;CAST(0x00008F5800000000 AS DateTime) //2000-06-21 00:00:00&lt;br /&gt;CAST(0x00008F5900000000 AS DateTime) //2000-06-22 00:00:00&lt;br /&gt;&lt;/pre&gt;Это - внутренний формат даты в MSSQL. Вопрос - как из внутреннего формата преобразовать дату в нормальный вид?&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Оказалось, проблема известна, и народ этот формат уже &lt;a href="http://www.sql-server-performance.com/articles/dev/datetime_datatype_p1.aspx"&gt;расковырял&lt;/a&gt;. Привожу функцию на C#, для конвертации внутреннего формата даты MSSQL в стандартный DateTime:&lt;br /&gt;&lt;pre class="brush:c#"&gt; public static DateTime ConvertSqlBinaryToDate(String value) {&lt;br /&gt;  const String HEXADECIMAL_PREFIX = "0x";&lt;br /&gt;&lt;br /&gt;  if (value == null) {&lt;br /&gt;      throw new Exception("value is null");&lt;br /&gt;  }&lt;br /&gt;  if (value.Length != 18) {&lt;br /&gt;      throw new Exception("value should have length 18 bytes");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (!value.StartsWith(HEXADECIMAL_PREFIX)) {&lt;br /&gt;      throw new Exception(String.Format("value should starts from {0}", HEXADECIMAL_PREFIX));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  String first_part = value.Substring(2, 8);&lt;br /&gt;  String second_part = value.Substring(10, 8);&lt;br /&gt;&lt;br /&gt;  Double first_part_value = long.Parse(first_part, System.Globalization.NumberStyles.HexNumber);&lt;br /&gt;  Double second_part_value = long.Parse(second_part, System.Globalization.NumberStyles.HexNumber);&lt;br /&gt;&lt;br /&gt;  if (first_part_value &amp;amp;gt; Int32.MaxValue) {&lt;br /&gt;      first_part_value = UInt32.MaxValue - first_part_value;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  //Each unit of the value of the second 4 bytes matching a clock-tick&lt;br /&gt;  //Equivalent to 3.33 milliseconds.         &lt;br /&gt;  const double MILLISECONDS_PRECISION = 3.33333333;&lt;br /&gt;&lt;br /&gt;  DateTime result = new DateTime(1900, 1, 1);&lt;br /&gt;  result = result.AddDays(first_part_value);&lt;br /&gt;&lt;br /&gt;  result = result.AddMilliseconds(second_part_value * MILLISECONDS_PRECISION);&lt;br /&gt;&lt;br /&gt;  return result;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Оригинал на VB по ссылке выше, в комментариях.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-6791313568999289496?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/6791313568999289496/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/05/mssql.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6791313568999289496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6791313568999289496'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/05/mssql.html' title='Конвертация даты из внутреннего формата MSSQL'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-51103114797081890</id><published>2011-05-10T14:54:00.003+08:00</published><updated>2011-05-10T14:59:55.315+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Перенос данных с Firebird 1.5 на Firebird 2.5</title><content type='html'>Появилась необходимость перетащить базу с Firebird 1.5 на Firebird 2.5. После полуторки были еще FB 2.0 и FB 2.1, структура базы поменялась значительно. Почитал форумы - выяснилось, что наиболее надежный способ переноса базы - заново пересоздавать базу из скриптов на FB2.5. &lt;br /&gt;&lt;br /&gt;С переносом метаданных все понятно - с помощью IBExpert извлекаем метаданные (команда Extract Metadata) из базы под FB 1.5 и создаем новенькую пустую базу на FB 2.5. А вот как перетаскивать сами данные? &lt;br /&gt;&lt;br /&gt;Несмотря на то, что в IBExpert есть множество &lt;a href="http://www.ibexpert.info/ibe/index.php?n=Doc.ImportAndExportUsingIBExpert2#MovingData"&gt;средств импорта/экспорта данных&lt;/a&gt;, готового инструмента для переноса данных между базами - нет. К счастью, IBExpert поддерживает мощный скриптовый язык &lt;a href="http://www.ibexpert.info/ibe/index.php?n=Doc.IBEBlock"&gt;IBEBlock&lt;/a&gt;. И на нем достаточно просто создать скрипт с требуемой функциональностью. Далее расскажу подробнее о получившемся скрипте и опишу итоговую процедуру переноса базы с FB1.5 на FB2.5.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;h4&gt;Расширенный INSERT INTO в SQL Editor&lt;/h4&gt;Честно говоря, начал я не с IBEBlock, а с расширенного синтаксиса INSERT INTO, который поддерживает IBExpert (см. раздел &lt;a href="http://www.ibexpert.info/ibe/index.php?n=Doc.ImportAndExportUsingIBExpert2#MovingData"&gt;Moving data between databases&lt;/a&gt; в документации IBExpert). SQL Editor поддерживает такой синтаксис:&lt;br /&gt;&lt;pre class="brush:sql"&gt;INSERT INTO &amp;lt;database_alias&amp;gt;.&amp;lt;table_name&amp;gt;[(&amp;lt;columns_list&amp;gt;)]&amp;lt;select_statement&amp;gt;&lt;/pre&gt;С помощью этой команды можно перенести таблицу из одной базы в другую. Недолго думая, я написал скрипт вида:&lt;br /&gt;&lt;pre class="brush:sql"&gt;INSERT INTO [db25].TABLE1 SELECT * FROM TABLE1;&lt;br /&gt;INSERT INTO [db25].TABLE2 SELECT * FROM TABLE2;&lt;br /&gt;...&lt;br /&gt;INSERT INTO [db25].TABLE120 SELECT * FROM TABLE120;&lt;/pre&gt;и сразу воткнулся в три проблемы.&lt;ul&gt;&lt;li&gt;Расширенный вариант INSERT INTO &amp;lt;database_alias&amp;gt;.&amp;lt;table_name&amp;gt; работает только в SQL Editor и не работает в SQL Executor. На практике это означает, что за раз можно выполнить только одну команду INSERT INTO. А выполнить все команды оптом - нельзя. Крайне неудобно, если перенос данных между базами требуется провести не раз и не два.&lt;/li&gt;&lt;li&gt;Расширенный вариант INSERT INTO не учитывает вычисляемые read-only поля, созданные через &lt;code&gt;computed by&lt;/code&gt;. Если в таблице есть вычисляемое поле, INSERT INTO выдает ошибку.&lt;/li&gt;&lt;li&gt;Многие таблицы связаны между собой зависимостями через foreign keys. Поэтому переноситься они должны в определенном порядке. Так, чтобы при загрузке очередной таблицы, все данные, на которые она ссылается, в базе уже были. Порядок загрузки таблиц нужно определить - либо экспериментальным путем, либо теоретическим, изучив структуру базы. &lt;i&gt;В худшем случае зависимости могут оказаться циклическими и тут без танцев с бубном не обойтись - простого способа автоматизации переноса данных в этом случае нет, придется переносить данные частыми, создавать Update-скрипты и т.д. В моем случае таких проблем, к счастью, не возникло.&lt;/i&gt;&lt;/li&gt;&lt;/ul&gt;Таким образом, INSERT INTO годится для одноразового переноса данных между базами - например, чтобы определить правильный порядок переноса таблиц. А для автоматизации процесса переноса данных этот способ не подходит.&lt;h4&gt;Скрипт на IBEBlock&lt;/h4&gt;А вот IBEBlock подходит для переноса данных идеально.  В документации так и написано - &lt;a href="http://www.ibexpert.info/ibe/index.php?n=Doc.IBEBlockEXECUTEIBEBLOCK"&gt;With EXECUTE IBEBLOCK you will be able to: ... Move (copy) data from one database to another&lt;/a&gt;. Более того. В примерах использования IBEBlock есть функция &lt;a href="http://www.ibexpert.info/ibe/index.php?n=Doc.CopyTable"&gt;Copy Table&lt;/a&gt;, предназначенная для копирования таблиц из базы в базу (она, кстати, прекрасно распознает вычисляемые поля и исключает их из INSERT запросов).&lt;br /&gt;&lt;br /&gt;Я взял эту функцию за основу и, немного повозившись, получил скрипт с нужным функционалом:&lt;pre class="brush:sql"&gt;EXECUTE IBEBLOCK&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;FuncCopyTableData = 'execute ibeblock (&lt;br /&gt;   SrcObjectName variant = '''' comment ''Table name to be copied'',&lt;br /&gt;   DestObjectName variant = '''' comment ''Destination table name, leave empty if no changes need'')&lt;br /&gt;as&lt;br /&gt;begin&lt;br /&gt;   SrcDBPassword = ''masterkey'';&lt;br /&gt;   SrcDBUserName = ''SYSDBA'';&lt;br /&gt;   DestDBUserName = ''SYSDBA'';&lt;br /&gt;   DestDBPassword = ''masterkey'';&lt;br /&gt;   SrcDBCharset = ''WIN1251'';&lt;br /&gt;   DestDBCharset = ''WIN1251'';&lt;br /&gt;   SrcDBClientLib = ''C:\Program Files (x86)\Firebird\Firebird_1_5\bin\fbclient.dll'';&lt;br /&gt;   DestDBClientLib = ''C:\Program Files (x86)\Firebird\Firebird_2_5\bin\fbclient.dll'';&lt;br /&gt;   SrcDBConnStr = ''127.0.0.1:z:\db\db15.fdb'';&lt;br /&gt;   DestDBConnStr = ''127.0.0.1/3051:z:\db\db25.fdb'';&lt;br /&gt;&lt;br /&gt;   Time1 = ibec_GetTickCount(); &lt;br /&gt;&lt;br /&gt;   CRLF = ibec_CRLF();&lt;br /&gt;   BS = ibec_Chr(8);&lt;br /&gt;   Success = BS + '' Successfull.'';&lt;br /&gt;   Failed = BS + '' FAILED!'';&lt;br /&gt;&lt;br /&gt;   SrcTableName = SrcObjectName;&lt;br /&gt;   DestTableName = DestObjectName; &lt;br /&gt;&lt;br /&gt;   SrcDBParams = ''DBName='' + SrcDBConnStr + '';'' +&lt;br /&gt;                 ''User='' + SrcDBUserName + '';'' +&lt;br /&gt;                 ''Password='' + SrcDBPassword + '';'' +&lt;br /&gt;                 ''Names='' + SrcDBCharset + '';'' +&lt;br /&gt;                 ''ClientLib='' + SrcDBClientLib;&lt;br /&gt;&lt;br /&gt;   DestDBParams = ''DBName='' + DestDBConnStr + '';'' +&lt;br /&gt;                  ''User='' + DestDBUserName + '';'' +&lt;br /&gt;                  ''Password='' + DestDBPassword + '';'' +&lt;br /&gt;                  ''Names='' + DestDBCharset + '';'' +&lt;br /&gt;                  ''ClientLib='' + DestDBClientLib;&lt;br /&gt;&lt;br /&gt;   try&lt;br /&gt;     try&lt;br /&gt;       ibec_Progress(''Connecting to '' + SrcDBConnStr + ''...'');&lt;br /&gt;       SrcDB = ibec_CreateConnection(__ctFirebird, SrcDBParams);&lt;br /&gt;       ibec_Progress(Success);&lt;br /&gt;       SrcDBSQLDialect = ibec_GetConnectionProp(SrcDB, ''DBSQLDialect'');&lt;br /&gt;     except&lt;br /&gt;       ibec_Progress(Failed);&lt;br /&gt;       raise;&lt;br /&gt;       Exit;&lt;br /&gt;     end;&lt;br /&gt;&lt;br /&gt;     try&lt;br /&gt;       ibec_Progress(''Connecting to '' + DestDBConnStr + ''...'');&lt;br /&gt;       DestDB = ibec_CreateConnection(__ctFirebird, DestDBParams);&lt;br /&gt;       ibec_Progress(Success);&lt;br /&gt;       DestDBSQLDialect = ibec_GetConnectionProp(DestDB, ''DBSQLDialect'');&lt;br /&gt;     except&lt;br /&gt;       ibec_Progress(Failed);&lt;br /&gt;       raise;&lt;br /&gt;       Exit;&lt;br /&gt;     end; &lt;br /&gt;&lt;br /&gt;     ibec_UseConnection(SrcDB); &lt;br /&gt;&lt;br /&gt;     select rdb$relation_name, rdb$system_flag, rdb$external_file, rdb$description&lt;br /&gt;            from rdb$relations&lt;br /&gt;            where (rdb$relation_name = :SrcTableName) and (rdb$view_blr is null)&lt;br /&gt;            into :SrcTableData;&lt;br /&gt;&lt;br /&gt;     if (SrcTableData[''RDB$RELATION_NAME''] is null) then&lt;br /&gt;       exception cant_find_table ''There is no such table ('' + :SrcTableName + '') in the source database.'';&lt;br /&gt;     IsSys = SrcTableData[''RDB$SYSTEM_FLAG''] = 1;&lt;br /&gt;     if (IsSys) then&lt;br /&gt;       exception cant_copy_system_table ''Cannot copy a system table.'';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;     if ((DestTableName is null) or (DestTableName = ''''))  then&lt;br /&gt;       DestTableName = SrcTableName; &lt;br /&gt;&lt;br /&gt;     DestTableNameFmt = ibec_IIF(DestDBSQLDialect = 3, ibec_QuotedStr(:DestTableName, ''"''), ibec_AnsiUpperCase(:DestTableName));&lt;br /&gt;     SrcTableNameFmt = ibec_IIF(SrcDBSQLDialect = 3, ibec_QuotedStr(:SrcTableName, ''"''), ibec_AnsiUpperCase(:SrcTableName)); &lt;br /&gt;&lt;br /&gt;     select rdb$field_name&lt;br /&gt;            from rdb$relation_fields&lt;br /&gt;            where (rdb$relation_name = ''RDB$FIELDS'') and&lt;br /&gt;                  (rdb$field_name = ''RDB$FIELD_PRECISION'')&lt;br /&gt;            into :bPrecision;&lt;br /&gt;     bPrecision = ibec_IIF(:bPrecision is NULL, FALSE, TRUE);&lt;br /&gt;&lt;br /&gt;     SelStmt = ''select rf.rdb$field_name as fld_name,'' +&lt;br /&gt;                      ''rf.rdb$field_source as fld_domain,'' +&lt;br /&gt;                      ''rf.rdb$null_flag as fld_null_flag,'' +&lt;br /&gt;                      ''rf.rdb$default_source as fld_default,'' +&lt;br /&gt;                      ''rf.rdb$description as fld_description,'' +&lt;br /&gt;                      ''f.rdb$field_type as dom_type,'' +&lt;br /&gt;                      ''f.rdb$field_length as dom_length,'' +&lt;br /&gt;                      ''f.rdb$field_sub_type as dom_subtype,'' +&lt;br /&gt;                      ''f.rdb$field_scale as dom_scale,'' +&lt;br /&gt;                      ''f.rdb$null_flag as dom_null_flag,'' +&lt;br /&gt;                      ''f.rdb$character_length as dom_charlen,'' +&lt;br /&gt;                      ''f.rdb$segment_length as dom_seglen,'' +&lt;br /&gt;                      ''f.rdb$system_flag as dom_system_flag,'' +&lt;br /&gt;                      ''f.rdb$computed_source as dom_computedby,'' +&lt;br /&gt;                      ''f.rdb$default_source as dom_default,'' +&lt;br /&gt;                      ''f.rdb$dimensions as dom_dims,'' +&lt;br /&gt;                      ''f.rdb$description as dom_description,'' +&lt;br /&gt;                      ''ch.rdb$character_set_name as dom_charset,'' +&lt;br /&gt;                      ''ch.rdb$bytes_per_character as charset_bytes,'' +&lt;br /&gt;                      ''dco.rdb$collation_name as dom_collation,'' +&lt;br /&gt;                      ''fco.rdb$collation_name as fld_collation'';&lt;br /&gt;     if (bPrecision) then&lt;br /&gt;       SelStmt = SelStmt + '', f.rdb$field_precision as dom_precision''; &lt;br /&gt;&lt;br /&gt;     SelStmt = SelStmt + CRLF +&lt;br /&gt;               ''from rdb$relation_fields rf '' + CRLF +&lt;br /&gt;               ''left join rdb$fields f on rf.rdb$field_source = f.rdb$field_name'' + CRLF +&lt;br /&gt;               ''left join rdb$character_sets ch on f.rdb$character_set_id = ch.rdb$character_set_id'' + CRLF +&lt;br /&gt;               ''left join rdb$collations dco on ((f.rdb$collation_id = dco.rdb$collation_id) and (f.rdb$character_set_id =  dco.rdb$character_set_id))'' + CRLF +&lt;br /&gt;               ''left join rdb$collations fco on ((rf.rdb$collation_id = fco.rdb$collation_id) and (f.rdb$character_set_id =  fco.rdb$character_set_id))'' + CRLF +&lt;br /&gt;               ''where rf.rdb$relation_name = '' + ibec_QuotedStr(:SrcTableName, '''''''') + CRLF +&lt;br /&gt;               ''order by rf.rdb$field_position'';&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;     ibec_Progress(''Collecting fields info...'');&lt;br /&gt;     i = 0;&lt;br /&gt;     iUserDomainCount = 0;&lt;br /&gt;     for execute statement SelStmt into :FldData&lt;br /&gt;     do&lt;br /&gt;     begin&lt;br /&gt;       s = ibec_Trim(FldData[''FLD_DOMAIN'']);&lt;br /&gt;       aDomains[i] = ibec_IIF(ibec_Copy(s, 1, 4) = ''RDB$'', null, s);&lt;br /&gt;       if (aDomains[i] is not null) then&lt;br /&gt;         iUserDomainCount = iUserDomainCount + 1; &lt;br /&gt;&lt;br /&gt;       aFields[i] = ibec_Trim(FldData[''FLD_NAME'']); &lt;br /&gt;&lt;br /&gt;       sType = ibec_IBTypeToStr(FldData[''DOM_TYPE''],&lt;br /&gt;                                FldData[''DOM_SUBTYPE''],&lt;br /&gt;                                FldData[''DOM_LENGTH''],&lt;br /&gt;                                FldData[''DOM_SCALE''],&lt;br /&gt;                                FldData[''DOM_SEGLEN''],&lt;br /&gt;                                FldData[''DOM_CHARLEN''],&lt;br /&gt;                                FldData[''DOM_PRECISION''],&lt;br /&gt;                                DestDBSQLDialect);&lt;br /&gt;       aTypes[i] = sType;&lt;br /&gt;&lt;br /&gt;       aFieldsNotNull[i] = ibec_IIF(FldData[''FLD_NULL_FLAG''] = 1, '' NOT NULL'', '''');&lt;br /&gt;       aFieldsDefault[i] = ibec_IIF(FldData[''FLD_DEFAULT''] is null, '''', '' '' + ibec_Trim(FldData[''FLD_DEFAULT'']));&lt;br /&gt;       aFieldsComment[i] = FldData[''FLD_DESCRIPTION''];&lt;br /&gt;       aFieldsCharset[i] = ibec_IIF(FldData[''DOM_CHARSET''] is null, '''', ibec_Trim(FldData[''DOM_CHARSET'']));&lt;br /&gt;       aFieldsCollate[i] = ibec_IIF(FldData[''FLD_COLLATION''] is null, '''', ibec_Trim(FldData[''FLD_COLLATION'']));&lt;br /&gt;&lt;br /&gt;       aDomainsComputedBy[i] = FldData[''DOM_COMPUTEDBY''];&lt;br /&gt;       i = i + 1;&lt;br /&gt;     end&lt;br /&gt;     ibec_UseConnection(DestDB);&lt;br /&gt;&lt;br /&gt;     -------------------------------------------------------------&lt;br /&gt;     -- TRANSFER TABLE DATA --------------------------------------&lt;br /&gt;     -------------------------------------------------------------&lt;br /&gt;   sFields = '''';&lt;br /&gt;   sValues = '''';&lt;br /&gt;   foreach (aFields as FldName key FldKey) do begin&lt;br /&gt;     if (aDomainsComputedBy[FldKey] is null) then&lt;br /&gt;     begin&lt;br /&gt;       if (sFields &amp;lt;&amp;gt; '''') then&lt;br /&gt;       begin&lt;br /&gt;         sFields .= '', '';&lt;br /&gt;         sValues .= '', '';&lt;br /&gt;       end;&lt;br /&gt;       FldNameFmt = ibec_IIF(DestDBSQLDialect = 3, ibec_QuotedStr(:FldName, ''"''), ibec_AnsiUpperCase(:FldName));&lt;br /&gt;       sFields .= FldNameFmt;&lt;br /&gt;       sValues .= '':'' + FldNameFmt;&lt;br /&gt;     end;&lt;br /&gt;   end;&lt;br /&gt;&lt;br /&gt;   SelectStmt = ''SELECT '' + sFields + '' FROM '' + SrcTableNameFmt;&lt;br /&gt;   InsertStmt = ''INSERT INTO '' + DestTableNameFmt + '' ('' + sFields + '') VALUES ('' + sValues + '')'';&lt;br /&gt;&lt;br /&gt;   ibec_UseConnection(SrcDB);&lt;br /&gt;   i = 0;&lt;br /&gt;   ibec_Progress(''Copying table data...'');&lt;br /&gt;   for execute statement :SelectStmt into :Data&lt;br /&gt;   do&lt;br /&gt;   begin&lt;br /&gt;     ibec_UseConnection(DestDB);&lt;br /&gt;     execute statement :InsertStmt values :Data;&lt;br /&gt;     i = i + 1;&lt;br /&gt;     if (ibec_mod(i, 500) = 0) then&lt;br /&gt;     begin&lt;br /&gt;       commit;&lt;br /&gt;       ibec_Progress(''    '' + ibec_cast(i, __typeString) + '' records copied...'');&lt;br /&gt;     end;&lt;br /&gt;   end;&lt;br /&gt;   ibec_Progress(''Totally '' + ibec_cast(i, __typeString) + '' records copied.'');&lt;br /&gt;   ibec_UseConnection(DestDB);&lt;br /&gt;   commit;&lt;br /&gt;&lt;br /&gt;  finally&lt;br /&gt;     if (SrcDB is not null) then begin&lt;br /&gt;       ibec_Progress(''Closing connection to '' + SrcDBConnStr + ''...'');&lt;br /&gt;       ibec_CloseConnection(SrcDB);&lt;br /&gt;     end;&lt;br /&gt;     if (DestDB is not null) then&lt;br /&gt;     begin&lt;br /&gt;       ibec_Progress(''Closing connection to '' + DestDBConnStr + ''...'');&lt;br /&gt;       ibec_CloseConnection(DestDB);&lt;br /&gt;     end;&lt;br /&gt;     Time2 = ibec_GetTickCount();&lt;br /&gt;     sTime = ibec_div((Time2 - Time1), 1000) || ''.'' ||ibec_mod((Time2 - Time1), 1000);&lt;br /&gt;     ibec_Progress(''Finished.'');&lt;br /&gt;     ibec_Progress(''Total time spent: '' || sTime || '' seconds'');&lt;br /&gt;     ibec_Progress(''That''''s all, folks!'');&lt;br /&gt;   end;&lt;br /&gt;end;';&lt;br /&gt;&lt;br /&gt;EXECUTE IBEBLOCK FuncCopyTableData ('TABLE1', 'TABLE1');&lt;br /&gt;EXECUTE IBEBLOCK FuncCopyTableData ('TABLE2', 'TABLE2');&lt;br /&gt;...&lt;br /&gt;EXECUTE IBEBLOCK FuncCopyTableData ('TABLE120', 'TABLE120');&lt;br /&gt;end;&lt;/pre&gt;Команды &lt;code&gt;EXECUTE IBEBLOCK&lt;/code&gt; в конце скрипта расположены в том порядке, в котором нужно переносить таблицы из базы в базу, чтобы не нарушались связи между данными.&lt;br /&gt;&lt;br /&gt;P.s. Обращаю внимание на ошибку в примере Copy Table на сайте IBExpert: вместо &lt;br /&gt;&lt;code&gt;ibec_QuotedStr(:SrcTableName, )&lt;/code&gt; нужно писать &lt;code&gt;ibec_QuotedStr(:SrcTableName, '''')&lt;/code&gt;, иначе скрипт не работает.&lt;h4&gt;Итоговая процедура переноса базы с Firebird 1.5 на Firebird 2.5&lt;/h4&gt;Итоговый процесс перевода базы выглядел следующим образом. Подготовка:&lt;ul&gt;&lt;li&gt;С помощью IBExpert\Tools\Extract Metadata извлечь под FB1.5 метаданные в виде скрипта.&lt;/li&gt;&lt;li&gt;Выполнить этот скрипт под FB2.5 - убедиться, что все триггеры, процедуры и т.д. создаются без проблем.&lt;/li&gt;&lt;li&gt;Определить правильный порядок переноса таблиц из базы в базу. Для этого, например, можно вручную перенести все таблицы (одну за одной) из базы в базу с помощью INSERT INTO в SQL Edtitor.&lt;/li&gt;&lt;/ul&gt;Собственно перенос базы:&lt;ul&gt;&lt;li&gt;Деактивировать триггеры в исходной базе. Для этого открыть исходную базу в IBExpert, в DBExplorer щелкнуть правой кнопкой мыши по Triggers и дать команду Deactivate triggers.&lt;/li&gt;&lt;li&gt;Извлечь метаданные (с отключенными триггерами) под FB1.5 в виде скрипта.&lt;/li&gt;&lt;li&gt;С помощью полученного скрипта создать чистую пустую базу под FB2.5.&lt;/li&gt;&lt;li&gt;Создать скрипт переноса данных на IBEBlock - по аналогии со скриптом, приведенным выше. Важно соблюсти правильный порядок следования таблиц в конце скрипта - при загрузке данных не должны нарушаться связи между данными.&lt;/li&gt;&lt;li&gt;Выполнить скрипт, сделать Commit.&lt;/li&gt;&lt;li&gt;Активировать триггеры в итоговой базе. Для этого открыть результирующую базу в IBExpert, в DBExplorer щелкнуть правой кнопкой мыши по Triggers и дать команду Activate triggers.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;Итоги&lt;/h4&gt;Первая база перенесена успешно. Теперь предстоит проверять и тестировать программы, работающие с этой базой. Позже надо будет перетащить еще несколько баз, так что методику потихоньку обкатаю.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-51103114797081890?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/51103114797081890/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/05/firebird-15-firebird-25.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/51103114797081890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/51103114797081890'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/05/firebird-15-firebird-25.html' title='Перенос данных с Firebird 1.5 на Firebird 2.5'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-7543566107242704052</id><published>2011-04-29T14:58:00.000+08:00</published><updated>2011-04-29T14:58:53.412+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='src'/><title type='text'>Android. Реализация SendEmail</title><content type='html'>Отправить email из приложения. Это типичная задача, с которой сталкиваешься при разработке под Android. И делов то всего - скопипастить &lt;a href="http://snipt.net/Martin/android-intent-usage/"&gt;готовый код&lt;/a&gt;. Но есть несколько особенностей, которые легко пропустить... и потратить кучу времени в поисках ошибки.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;Итак, типичный код, который гуляет на просторах интернета:&lt;br /&gt;&lt;pre class="brush:java"&gt;Intent it = new Intent(Intent.ACTION_SEND);   &lt;br /&gt;it.putExtra(Intent.EXTRA_EMAIL, "me@abc.com");   &lt;br /&gt;it.putExtra(Intent.EXTRA_TEXT, "The email body text");   &lt;br /&gt;it.setType("text/plain");   &lt;br /&gt;context.startActivity(Intent.createChooser(it, "Choose Email Client")); &lt;br /&gt;&lt;/pre&gt;Сразу обратим внимание на строку "text/plain". Должно быть именно &lt;b&gt;text/plain&lt;/b&gt;, а не &lt;s&gt;&lt;b&gt;plain/text&lt;/b&gt;&lt;/s&gt;. В половине примеров в интернете сделана эта ошибка. Результат - вместо диалогового окна со списком подходящих для отправки email приложений всегда вызывается GMail, без возможности выбрать приложение. &lt;br /&gt;&lt;br /&gt;Кроме того, вариант "text/plain" поддерживается очень большим количеством приложений. Здесь и Google Reader, и Facebook и Bluetooth.. список может быть очень длинным. Чтобы в списке фигурировали только email-приложения, нужно указывать "text/xml".&lt;br /&gt;&lt;br /&gt;Переходим к вызову &lt;a href="http://developer.android.com/reference/android/content/Intent.html#createChooser(android.content.Intent, java.lang.CharSequence)"&gt;Intent.createChooser&lt;/a&gt;. Функция Intent.createChooser выводит список приложений, способных обработать переданный в нее intent. При этом список приложений выводится всегда, галочки "Use by default for this action" в этом списке нет. В результате, пользователи вынуждены выбирать программу для отправки email &lt;b&gt;каждый&lt;/b&gt; раз, что их крайне нервирует. &lt;br /&gt;&lt;br /&gt;Можно легко обойтись без createChooser &lt;br /&gt;&lt;pre class="brush:java"&gt; /*context.startActivity(Intent.createChooser(it, "Choose Email Client"));*/&lt;br /&gt; context.startActivity(it);&lt;br /&gt;&lt;/pre&gt;Диалог выбора приложений все так же будет показываться, но в нем появится галочка "Use by default for this action", позволяющая запомнить выбор (чтобы отменить то, что запомнили, нужно лезть в настройки, находить в них выбранное приложение и сбрасывать там флаг "Launch by default").&lt;br /&gt;&lt;br /&gt;У createChooser есть еще один подводный камень. Если вы попытаетесь вызвать функцию из контекста виджета, то получите сообщение об ошибке - "Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?". Ясно, что надо выставить флаг FLAG_ACTIVITY_NEW_TASK. Важно только не ошибиться и выставить его у &lt;a href="http://stackoverflow.com/questions/5385724/how-can-i-call-startactivity-to-use-action-send-from-a-different-class"&gt;правильного intent&lt;/a&gt; - у того, что возвращает функция createChooser, а не того, который мы в нее передаем.&lt;br /&gt;&lt;br /&gt;Итак, собираем все вместе и получаем функцию, реализующую перечисленные варианты работы:&lt;pre class="brush:java"&gt;/**&lt;br /&gt;* Send email through one of email applications. &lt;br /&gt;* User selects app through dialog.&lt;br /&gt;* @param bUseChooser - use or don't use application chooser. &lt;br /&gt;* Chooser asks user to select email application each time &lt;br /&gt;* without possibility to remember the choice.&lt;br /&gt;* Checkbox "use by default for this action" is not available in chooser. &lt;br /&gt;* But it's available if chooser is turned off.  &lt;br /&gt;* @param bUseTextPlain - use text/plain or text/xml. &lt;br /&gt;* If you use "text/xml" then the dialog shows email applications only. &lt;br /&gt;*/&lt;br /&gt;public static void SendEmail(Context context&lt;br /&gt; , String srcEmail&lt;br /&gt; , String srcSubject&lt;br /&gt; , boolean bUseChooser&lt;br /&gt; , boolean bUseTextPlain&lt;br /&gt; , int resourceIdChooserMessage&lt;br /&gt;) {&lt;br /&gt;  Intent intent = new Intent(Intent.ACTION_SEND);&lt;br /&gt;  intent.setType(! bUseTextPlain  &lt;br /&gt;      ? "text/xml" //show email applications only&lt;br /&gt;      : "text/plain"); &lt;br /&gt;  if (srcEmail != null) {&lt;br /&gt;    intent.putExtra(Intent.EXTRA_EMAIL, new String[] { srcEmail });&lt;br /&gt;  }&lt;br /&gt;  if (srcSubject != null) {&lt;br /&gt;    intent.putExtra(Intent.EXTRA_SUBJECT, new String[] { srcSubject });&lt;br /&gt;  }&lt;br /&gt;  if (bUseChooser) {&lt;br /&gt;    //"use by default for this action" is not available here&lt;br /&gt;    //user selects required application manually each time &lt;br /&gt;    Intent chooser_intent = Intent.createChooser(intent, context.getString(resourceIdChooserMessage));&lt;br /&gt;    chooser_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);&lt;br /&gt;    context.startActivity(chooser_intent);&lt;br /&gt;  } else {&lt;br /&gt;    context.startActivity(intent);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;...&lt;br /&gt;//using without chooser&lt;br /&gt;  SendEmail(context, "my@abc.com"&lt;br /&gt;    , null&lt;br /&gt;    , false&lt;br /&gt;    , false&lt;br /&gt;    , 0);&lt;br /&gt;&lt;br /&gt;//using with chooser&lt;br /&gt;  SendEmail(context, "my@abc.com"&lt;br /&gt;    , null&lt;br /&gt;    , true&lt;br /&gt;    , false&lt;br /&gt;    , R.string.select_email_app);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;В большинстве случаев - сойдет.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-7543566107242704052?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/7543566107242704052/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/04/android-sendemail.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/7543566107242704052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/7543566107242704052'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/04/android-sendemail.html' title='Android. Реализация SendEmail'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-8703614560361341973</id><published>2011-04-14T14:16:00.001+08:00</published><updated>2011-06-17T11:35:53.138+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='datamining'/><category scheme='http://www.blogger.com/atom/ns#' term='science'/><title type='text'>Применение Data Mining в космических приложениях.</title><content type='html'>Выкладываю свою статью "Применение Data Mining в космических приложениях". Статья обзорная и содержит информацию взятую исключительно из открытых источников. Все ссылки ведут на полнотекстовые PDF. &lt;br /&gt;&lt;br /&gt;Статья была опубликована в прошлом году, в сборнике трудов конференции "Интеллект и наука". Полная ссылка: В.В. Деревянко, "Применение Data Mining в космических приложениях", Интеллект и наука: труды X Международной научно-практической конференции "Интеллект и наука" (г. Железногорск, 28-29 апреля 2010 г) - Красноярск: ИПК СФУ, 2010. - 344 с (страницы 26-33).&lt;br /&gt;&lt;br /&gt;Публикуется с любезного разрешения ИПК СФУ.&lt;a name='more'&gt;&lt;/a&gt;&lt;h4&gt;Введение&lt;/h4&gt;При использовании любого устройства в космических приложениях –  непосредственно в космических аппаратах или в наземном оборудовании, – одной из ключевых проблем является обеспечение надежности работы устройства. Надежность работы требует тщательного контроля: контроля качества производства, контроля производительности в процессе работы, своевременной диагностики и устранения возникающих неисправностей и т.д.  Подобный контроль выполняется на основе информации, поступающей с датчиков, контролирующих работу устройства. Прогресс в развитии микроэлектроники за последние 10-15 лет привел к тому, что датчики стали существенно дешевле, легче и меньше по размерам. Это привело к увеличению количества используемых датчиков и росту объемов телеметрической информации. Естественно, ручная обработка больших объемов информации слишком трудоемка – нужны средства автоматизации. &lt;br /&gt;&lt;br /&gt;Задачи автоматизации поиска знаний решаются средствами интеллектуального анализа данных – Data Mining. Фактически, Data Mining - это набор технологий поиска скрытых закономерностей в больших, необработанных объемах данных. Data Mining является частью процесса KDD (Knowledge Discovering in databases), включающем, помимо поиска закономерностей, этапы сбора, подготовки данных и последующего анализа полученных результатов. К настоящему времени разработано множество алгоритмов и технологий Data Mining. Характерно, что универсального алгоритма для извлечения знаний из данных не существует. Каждое конкретное практическое приложение, обладающее специфическими характеристиками, требует либо адаптации существующих технологий Data Mining, либо разработки новой технологии обработки данных. &lt;br /&gt;&lt;br /&gt;Целью настоящей статьи является обзор вариантов применения Data Mining в космических приложениях. На наш взгляд, все имеющиеся работы можно условно разделить на три категории. К первой относятся работы, посвященные поиску аномалий в телеметрических данных и мониторингу состояния технических систем. Ко второй – работы, посвященные повышению качества производства, тестирования компонентов системы и прогнозированию возникновения неисправностей в используемых системах. К третьей – использование Data Mining на борту летательных аппаратов. Настоящий обзор не является всеобъемлющим и основан исключительно на публикациях, доступных через Интернет.&lt;h4&gt;Поиск аномалий.&lt;/h4&gt;Одним из ключевых направлений применения технологий Data Mining является автоматизация поиска аномалий. Поиск аномалий – это поиск шаблонов данных, не соответствующих ожидаемому поведению &lt;a href="http://www.dtc.umn.edu/publications/reports/2008_16.pdf"&gt;[1]&lt;/a&gt;. Поиск аномалий широко применяется в задачах мониторинга состояния технических систем. Для решения подобных задач используются системы ISHM (Integrated Systems Health Management). В ISHM состояние системы контролируется по показаниям датчиков. Если в работе системы возникает неисправность, в данных, поступающих с датчиков, возникают аномалии, сигнализирующие об отклонение поведения системы от нормального поведения. Типичные задачи, которые решают подобные системы мониторинга, это определение факта возникновения аномалии, локализация ее местонахождения, диагностирование возникшей неисправности и прогнозирование возникновения неисправностей.&lt;br /&gt;&lt;br /&gt;Традиционно системы ISHM обычно используют одновременно несколько методов диагностики аномалий, в частности &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.20.9877&amp;rep=rep1&amp;type=pdf"&gt;[2]&lt;/a&gt;: &lt;ul&gt;&lt;li&gt;проверку выхода значения параметра за установленные пределы;&lt;/li&gt;&lt;li&gt;экспертную систему, содержащую набор правил, описывающих нормальное поведение системы (rule-based);&lt;/li&gt;&lt;li&gt;математическую модель, описывающую требуемое поведение системы (model-based).&lt;/li&gt;&lt;/ul&gt;Общий принцип у традиционных алгоритмов примерно один и тот же. Вначале эксперты задают модель поведения системы. Модель поведения представляет собой набор правил, характеризующих поведение системы. В процессе работы системы, поступающие телеметрические данные проверяются на соответствие модели. Если поведение данных начинает отклоняться от модели, то оператору, контролирующему работу системы, поступает тревожных сигнал о возможной неисправности.&lt;br /&gt;&lt;br /&gt;У всех традиционных алгоритмов есть общий недостаток – они требуют интенсивной работы экспертов. Эксперты задают набор правил, конструируют математическую модель, устанавливают допустимые пределы значений параметров. Возрастает количество данных – возрастает количество работы, которую необходимо проделать экспертам прежде, чем система мониторинга сможет работать. &lt;br /&gt;&lt;br /&gt;Методы, основанные на Data Mining - data-driven методы, - от этого недостатка свободны. Data-driven методы строят модель поведения системы автоматически, на основе имеющихся данных о нормальном поведении системы. Для обучения метода обычно достаточно несколько десятков-сотен точек нормальных данных.&lt;br /&gt;&lt;br /&gt;Data-driven методы имеют ряд преимуществ, по сравнению с традиционными:&lt;ul&gt;&lt;li&gt;не требуют априорно заданных знаний о работе системы;&lt;/li&gt;&lt;li&gt;не требуют системного анализа, чтобы определить соотношения между параметрами;&lt;/li&gt;&lt;li&gt;способны обрабатывать телеметрические данных, поступающие от работающей системы, в режиме реального времени и очень быстро реагировать на появление аномалии; модель поведения системы очень компактна и позволяет вести работу в режиме реального времени;&lt;/li&gt;&lt;li&gt;позволяют устанавливать и отслеживать взаимосвязь между большим количеством параметров;&lt;/li&gt;&lt;li&gt;способны обнаруживать коллективные и контекстные аномалии [1];&lt;/li&gt;&lt;li&gt;дают возможность автоматически обрабатывать архивы накопленных данных и извлекать из них полезную информацию;&lt;/li&gt;&lt;li&gt;позволяют легко учитывать новые данные о нормальном поведении системы и обновлять ранее построенную модель ее поведения.&lt;/li&gt;&lt;/ul&gt;Разработки data-driven методов мониторинга активно ведутся в Японии &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.20.9877&amp;rep=rep1&amp;type=pdf"&gt;[2]&lt;/a&gt; и в США [&lt;a href="http://ti.arc.nasa.gov/static/asanicms/pub-archive/173/InfoTech2009_DD_ISHM_v5.pdf"&gt;3&lt;/a&gt;, &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.106.5526&amp;rep=rep1&amp;type=pdf"&gt;4&lt;/a&gt;]. К настоящему времени, разработано множество методов: IMS, Orca, GritBot, GMM, LDS, одно-классовый SVM и т.д. Как показано в работе &lt;a href="http://ti.arc.nasa.gov/m/profile/schwabac/Martin-JANNAF.pdf"&gt;[5]&lt;/a&gt;, в общем случае разные методы находят различный набор аномалий, так что в IMHS целесообразно применять комбинации различных методов. &lt;br /&gt;&lt;br /&gt;Одним из наиболее перспективных data-driven методов является технология IMS, разработанная в NASA &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.106.5526&amp;rep=rep1&amp;type=pdf"&gt;[4]&lt;/a&gt;. IMS использует технологию кластеризации данных и вводит понятие расстояния между векторами данных. В процессе обучения IMS анализирует данные, полученные в процессе нормальной работы системы, и строит модель поведения системы – набор кластеров. Каждый кластер определяет ограничения на значения каждого параметра в каждом конкретном входящем векторе данных. Если в базе знаний нет кластера, содержащего близкие по значению данные, это означает, что система ведет себя неожиданным способом, т.е. возникла аномалия. В случае, если расстояние между текущими и нормальными данными превышает пороговое, система выдает тревожный сигнал &lt;a href="http://ti.arc.nasa.gov/static/asanicms/pub-archive/173/InfoTech2009_DD_ISHM_v5.pdf"&gt;[3]&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;В настоящее время IMS активно применяется в NASA. В частности, разработчики IMS проанализировали данные, полученные с шатла Колумбия во время его последнего полета. Как известно, шатл потерпел катастрофу из-за отрыва куска изоляционной обшивки, пробившей термоизоляцию на левом крыле. Отрыв произошел во время старта корабля. Однако о проблемах с термоизоляцией стало известно лишь через 17 дней, во время приземления шатла. База знаний IMS строилась на основе анализа данных предыдущих 5 полетов Колумбии. IMS выдала сигнал о возникновении неисправности в течении двух минут с момента ее возникновения.&lt;br /&gt;&lt;br /&gt;IMS успешно используется в центре управления полетов МКС для поиска аномалий в гироскопе с управляющим моментом. IMS успешно используется для мониторинга состояния главного двигателя шатла &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.157.7461&amp;rep=rep1&amp;type=pdf"&gt;[6]&lt;/a&gt;. Есть и другие успешные применения &lt;a href="http://www.scidaho.org/Library/NASAPDFs/Inductive_CT.pdf"&gt;[9]&lt;/a&gt;. Компания iSagacity разработала на основе IMS коммерческий программный продукт Process Data Miner, способный обнаруживать в данных тренды, являющиеся ранними предвестниками начинающихся изменений в процессах или возникновения проблем в оборудовании.&lt;br /&gt;&lt;br /&gt;Как известно, NASA собирается прекратить полеты шатлов в 2010 году. На замену шатлам приходят новые аппараты – Ares.  Система диагностики Ares 1-X использует data-driven алгоритмы &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.140.8935&amp;rep=rep1&amp;type=pdf"&gt;[7]&lt;/a&gt; совместно с математическими моделями и экспертными системами. Здесь необходимо отметить важный момент. По правилам NASA любые компьютерные системы, предоставляющие данные для критических решений при полетах с участием человека, должны были сертифицированы. Несертифицированные системы могут использоваться лишь в качестве «советчиков» - их результаты должны в обязательном порядке перепроверяются на сертифицированных системах. При использовании на Space Shuttle data-driven алгоритмы работали именно в режиме «советчиков». Система диагностики Ares изначально разрабатывалась с учетом требований сертификации. Разработчики системы диагностики Ares I-X рассчитывают на то, что им удастся ее сертифицировать.&lt;br /&gt;&lt;br /&gt;Для первого полета Ares 1-X не было данных, накопленных при предыдущих полетах. Между тем, data driven алгоритмы использовались уже при первом полете (успешно состоявшемся 28.10.2009). Для обучения метода были частично задействованы данные, полученные при полетах шатлов для ускорителя с РДТТ, отклоняемого вектора тяги, а так же данные по наземной гидравлике &lt;a href="https://c3.ndc.nasa.gov/dashlink/static/media/publication/SchwabacherInfotech2010.pdf"&gt;[8]&lt;/a&gt;.&lt;h4&gt;Контроль качества.&lt;/h4&gt;О том, насколько для космических аппаратов важно качество используемых компонентов – говорить излишне. В наше время, Data Mining активно используется в промышленности для повышения качества производства, оптимизации тестирования и прогнозирования выхода компонентов из строя &lt;a href="http://www.icaen.uiowa.edu/~ankusiak/Journal-papers/Harding.pdf"&gt;[13]&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;В работе &lt;a href="http://www.icaen.uiowa.edu/~ankusiak/Journal-papers/Kurasek.pdf"&gt;[14]&lt;/a&gt; авторы применили Data Mining для поиска причин выхода из строя печатных плат. При сборке печатных плат, под некоторыми компонентами образуются пузырьки припоя. Подобные пузырьки могут приводить к преждевременному выходу компонента (и блока в целом) из строя. 70-80% дефектов плат связаны с пузырьками, 20-30% - с компонентами.  Авторы показали, что подавляющее большинство подобных дефектов образуется под компонентами определенного вида. Полученная информация позволила разобраться в том, что может приводить к таким дефектами, и понять, каким образом следует модифицировать процесс производства.&lt;br /&gt;&lt;br /&gt;В работе &lt;a href="http://web.engr.oregonstate.edu/~tgd/publications/kdd2000-dlft.pdf"&gt;[15]&lt;/a&gt; предложен метод оптимизации процесса производства интегральных микросхем. Интегральные микросхемы фабрикуются на пластинах, содержащих сотни отдельных чипов. Далее каждый чип подвергается длительному и дорогостоящему тестированию. После чего пластины разрезаются и прошедшие проверку чипы объединяются в пакеты, которые вновь подвергаются тестированию. Авторы применили data mining, позволяющий снизить количество тестов отдельных чипов. В &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.94.151&amp;rep=rep1&amp;type=pdf"&gt;[17]&lt;/a&gt; использовали технологию распознавания образов для проверки качества изделий, отлитых из алюминия. Распознавание образов – это то же Data Mining.&lt;br /&gt;&lt;br /&gt;В работе &lt;a href="http://www.ia-tech.com/publications/rame-prdc05.pdf"&gt;[11]&lt;/a&gt; технология Data Mining была применена в FMECA-анализе. FMECA-анализ – технология анализа возможности возникновения дефектов в авиационной микроэлектронике. Традиционно такой анализ проводится на основе FMECA-таблиц, который обычно составляются экспертами вручную. Авторы использовали Data Mining для автоматизации составления FMECA-таблицы. Результат – FMECA-таблица получилась более точной, более полной, а время ее создания сократилось с нескольких месяцев до пары секунд.  &lt;br /&gt;&lt;br /&gt;К выходу электроники из строя в процессе эксплуатации приводят не только дефекты производства. К настоящему моменту, производители электроники практически полностью автоматизировали процесс производства, так что характеристики производимых изделий минимально варьируются от образца к образцу. Важнейшей причиной возникновения неисправностей являются индивидуальные характеристики эксплуатации конкретного модуля. Среднее время эксплуатации и другие традиционные характеристики надежности, крайне неточны. Для более точной оценки необходимо учитывать реальную историю эксплуатации конкретного модуля.&lt;br /&gt;&lt;br /&gt;В работе &lt;a href="http://space.iias.spb.su/webarchive/ai/publications/2002-GSP-DM.zip"&gt;[10]&lt;/a&gt; предпринята попытка разработки метода точной оценки вероятности выхода из строя авиационного и аэрокосмического оборудования на основе истории его эксплуатации. Анализ данных проводился с помощью технологий Data Mining.&lt;br /&gt;&lt;br /&gt;В работе &lt;a href="http://ti.arc.nasa.gov/static/asanicms/pub-archive/173/InfoTech2009_DD_ISHM_v5.pdf"&gt;[3]&lt;/a&gt; описан способ применения IMS для предсказания возникновения неисправностей в процессе предпусковой диагностики. В предпусковых тестах могут остаться незамеченными мелкие отличия, появившиеся в поведении модуля. Существующие технологии мониторинга фокусируются, в основном, на различиях в значении одного параметра. Метод IMS умеет анализировать совместное поведение множества параметров и способен определить, что хотя значения всех параметров находятся в допустимых пределах, поведение модуля в целом начало меняться и отличается от поведения, зафиксированного в ранее проведенных тестах. Такие изменения могут служить ранним сигналом о возникновении неисправности. Кроме того, результаты такого анализа помогут выявить условия снижения производительности тестируемого компонента уже после выхода компонента из строя. Наконец, результаты IMS-анализа могут являться входными данными для алгоритмов, прогнозирующих будущее поведение компонента &lt;a href="http://ti.arc.nasa.gov/static/asanicms/pub-archive/1382h/1382%20(Schwabacher).pdf"&gt;[12]&lt;/a&gt;.&lt;h4&gt;Data Mining на борту летательного аппарата&lt;/h4&gt;Использование бортовых систем Data Mining &lt;a href="http://www.eecs.wsu.edu/~holder/courses/CptS570/fall07/present/CastanoKDD07.pdf"&gt;[16]&lt;/a&gt;  актуально, как минимум, по трем причинам. Во-первых, такие системы дают возможность быстро реагировать на происходящие события. Например, на космическом корабле, направляющемся к Марсу, задержка между моментом возникновения события и поступления соответствующей команды из центра управления полета может составлять 20 минут. Во-вторых, бортовые системы Data Mining могут грамотно расставлять приоритеты при передачи данных в центр управления. Произошло нетривиальное событие – информация о нем будет передана в первую очередь. В-третьих, бортовые системы Data Mining могут осуществлять черновую обработку данных и передавать на землю «выжимку» значительно меньшую по объему, чем исходные данные. В условиях удаленности системы от центра управления полета, больших объемов телеметрических данных и ограниченной ширины пропускания каналов связи, все это делает бортовые системы Data Mining весьма актуальными. В работе &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.89.943&amp;rep=rep1&amp;type=pdf"&gt;[18]&lt;/a&gt; рассмотрены три технологии Data Mining, которые планируется использовать на борту летательного аппарата, летящего на Марс.&lt;h4&gt;Выводы&lt;/h4&gt;Можно выделить два основных направления использования технологий Data Mining в космических приложениях: для анализа архивных данных и для анализа телеметрических данных в реальном времени.&lt;br /&gt;&lt;br /&gt;Анализ архивных данных позволяет:&lt;ul&gt;&lt;li&gt;автоматически, без участия экспертов, построить модель нормального поведения системы;&lt;/li&gt;&lt;li&gt;найти скрытые закономерности в данных, позволяющие исследователям пост-фактум проанализировать возникновение той или иной неисправности и понять какие симптомы в данных проявлялись перед ее возникновением, каковы причины неисправности, что можно сделать, чтобы исключить ее повторение.&lt;/li&gt;&lt;/ul&gt;Анализ телеметрических данных в реальном времени служит:&lt;ul&gt;&lt;li&gt;для быстрого диагностирования появления аномалии в данных;&lt;/li&gt;&lt;li&gt;для отслеживания возникновения трендов в данных;&lt;/li&gt;&lt;li&gt;для диагностирования появления тонких различий в поведении системы, являющихся ранними предвестниками возникновения проблем.&lt;/li&gt;&lt;/ul&gt;Ключевыми преимуществами технологий Data Mining, отличающих их от ручного анализа данных, являются:&lt;ul&gt;&lt;li&gt;возможность быстрой обработки больших объемов данных;&lt;/li&gt;&lt;li&gt;возможность анализа совокупности параметров системы;&lt;/li&gt;&lt;li&gt;быстрота реакции на возникновение проблемы.&lt;/li&gt;&lt;/ul&gt;В большинстве случаев, технологии Data Mining используются в качестве «советчиков» - результаты анализа используются человеком, для принятия решений. Однако прослеживается четкая тенденция к автоматизации процесса принятия решений. &lt;br /&gt;&lt;br /&gt;Технологии Data Mining находят все более широкое применение космических приложениях. Фактически, их применение становится нормой. Это обусловлено, прежде всего, непрерывным ростом объемов архивных данных, количества систем, генерирующих телеметрические данные и объемов самих телеметрических данных. Текущие тенденции позволяют сделать вывод о том, что в дальнейшем роль технологий Data Mining в применении к космическим приложениям будет только возрастать, а круг направлений использования – активно расширяться.&lt;h4&gt;Литература&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;V. Chandola, A. Banerjee, V. Kumar, "Anomaly Detection: A Survey", ACM Computing Surveys, Vol. 41(3), Article 15, July 2009 &lt;a href="http://www.dtc.umn.edu/publications/reports/2008_16.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;T. Yairi, Y. Kato, K. Hori, "Fault Detection by Mining Association Rules from House-keeping Data", Proc. of International Symposium on Artificial Intelligence, Robotics and Automation in Space, 2001. &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.20.9877&amp;rep=rep1&amp;type=pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;D. L. Iverson, R. Martin, M. Schwabacher, et al., "General Purpose Data-Driven System Monitoring for Space Operations", AIAA Infotech@Aerospace Conference, 2009. &lt;a href="http://ti.arc.nasa.gov/static/asanicms/pub-archive/173/InfoTech2009_DD_ISHM_v5.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;D. L. Iverson, "Inductive System Health Monitoring", Proceedings of The 2004 International Conference on Artificial Intelligence (IC-AI’04), CSREA Press, Las Vegas, NV, 2004.&lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.106.5526&amp;rep=rep1&amp;type=pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;R.A. Martin, M. Schwabacher, N. Oza, A. Srivastava, "Comparison of Unsupervised Anomaly Detection Methods for Systems Health Management Using Space Shuttle Main Engine Data", Proceedings of the 54th Joint Army-Navy-NASA-Air Force Propulsion, Meeting, Denver, CO, May 2007.&lt;a href="http://ti.arc.nasa.gov/m/profile/schwabac/Martin-JANNAF.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;M. Schwabacher, N. Oza, B. Matthews, "Unsupervised Anomaly Detection for Liquid-Fueled Rocket Propulsion Health Monitoring", Proceedings of the AIAA Infotech@Aerospace Conference, AIAA, Reston, VA, 2007.&lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.157.7461&amp;rep=rep1&amp;type=pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;M. Schwabacher, R. Waterman, "Pre-Launch Diagnostics for Launch Vehicles", IEEE Aerospace Conference, 2008. &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.140.8935&amp;rep=rep1&amp;type=pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;M. Schwabacher, R.A. Martin, R. Waterman, et al., "Ares I-X Ground Diagnostic Prototype", AIAA Infotech@Aerospace Conference, 2010 &lt;a href="https://c3.ndc.nasa.gov/dashlink/static/media/publication/SchwabacherInfotech2010.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;"Inductive System Monitors Tasks", Spinoff  2008, pp. 138-139 &lt;a href="http://www.scidaho.org/Library/NASAPDFs/Inductive_CT.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Skormin, V. A., Gorodetski, V. I., and Popyack, I. J., 2002, "Data Mining Technology for Failure of Prognostic of Avionics", IEEE Trans. Aerosp. Electron. Syst., 38_2_, pp. 388–403. &lt;a href="http://space.iias.spb.su/webarchive/ai/publications/2002-GSP-DM.zip"&gt;(PDF, zip)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;K. S. Tso, A. T. Tai, S. N. Chau, L. Alkalai, "On Automating Failure Mode Analysis and Enhancing its Integrity", PRDC 2005: 287-294 &lt;a href="http://www.ia-tech.com/publications/rame-prdc05.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;M. Schwabacher and K. Goebel, "A Survey of Artificial Intelligence for Prognostics", Working Notes of 2007 AAAI Fall Symposium: AI for Prognostics, 2007. &lt;a href="http://ti.arc.nasa.gov/static/asanicms/pub-archive/1382h/1382%20(Schwabacher).pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;J.A. Harding, M. Shahbaz, S. Srinivas, and A. Kusiak, "Data Mining in Manufacturing: A Review", ASME Transactions: Journal of Manufacturing Science and Engineering, Vol. 128, No. 4, 2006, pp. 969-976 &lt;a href="http://www.icaen.uiowa.edu/~ankusiak/Journal-papers/Harding.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;A. Kusiak and C. Kurasek, "Data Mining of Printed-Circuit Board Defects", IEEE Transactions on Robotics and Automation, Vol. 17, No. 2, 2001, pp. 191-196 &lt;a href="http://www.icaen.uiowa.edu/~ankusiak/Journal-papers/Kurasek.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;T. Fountain, T. Dietterich, B. Sudyka , "Mining IC test data to optimize VLSI testing", KDD ’00: Proceedings of the sixth ACM SIGKDD international conference on Knowledge discovery and data mining, New York, NY, USA, ACM Press (2000) 18–25.&lt;a href="http://web.engr.oregonstate.edu/~tgd/publications/kdd2000-dlft.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;S.Tanner, C.Stein, S.J. Graves, "On-board Data Mining" in  "Scientific Data Mining and Knowledge Discovery" by M.M. Gaber (Editor), Springer Verlag GmbH, 2009, pp. 345-376 &lt;a href="http://www.eecs.wsu.edu/~holder/courses/CptS570/fall07/present/CastanoKDD07.pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;S. Hernández, D. Saez, D. Mery, "Neuro-Fuzzy Method for Automated Defect Detection in Aluminium Castings", ICIAR (2) 2004: 826-833 &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.94.151&amp;rep=rep1&amp;type=pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;R. Castano, et al. "On-board analysis of uncalibrated data for a spacecraft at Mars," in Proceedings of the Thirteenth International Conference on Knowledge Discovery and Data Mining, 2007, pp. 922–930. &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.89.943&amp;rep=rep1&amp;type=pdf"&gt;(PDF)&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;: буду добавлять сюда и другие интересные ссылки по теме&lt;br /&gt;&lt;br /&gt;&lt;a href="http://ti.arc.nasa.gov/profile/schwabac/publications/"&gt;Mark Schwabacher's Publications&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Güntürkün F. "A Comprehensive Review Of Data Mining Applications in Quality Improvement and a Case Study", a thesis, 111 pages, august 2007&lt;a href="http://etd.lib.metu.edu.tr/upload/12608751/index.pdf"&gt;(PDF)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;R. S. Chen, Y. C. Chen and C.C. Chen. "Using Data Mining Technology to Deign an Quality Control System for Manufacturing Industry", Proceedings of the European conference of systems, 2010, pp. 271-276, ISBN: 978-960-474-250-9 &lt;a href="http://www.wseas.us/e-library/conferences/2010/Tenerife/CSCC/CSCC-48.pdf"&gt;(PDF)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Al-Salim, Bashar and Abdoli, Mansour, "Data Mining for Decision Support of the Quality Improvement Process" (2005). AMCIS 2005 Proceedings. Paper 115.&lt;br /&gt;&lt;a href="http://www.engr.unl.edu/~mansour/resume/DataMininginQualityImprovment.pdf"&gt;(PDF)&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-8703614560361341973?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/8703614560361341973/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/04/data-mining.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8703614560361341973'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8703614560361341973'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/04/data-mining.html' title='Применение Data Mining в космических приложениях.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-4411197798687576132</id><published>2011-04-12T20:06:00.001+08:00</published><updated>2011-04-23T21:39:31.595+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='src'/><title type='text'>Делокализация Delphi приложения</title><content type='html'>Достался мне недавно в сопровождение проект на Delphi. Проект старый и довольно большой по объему. Все бы ничего, да только написан он был датскими программистами для датских клиентов.  В результате - GUI на датском, текстовые сообщения в исходниках - на датском, комментарии - на датском. Даже названия функций - и те во многих случаях написаны по датски. &lt;br /&gt;&lt;br /&gt;Я датский практически не знаю. Для нормальной работы мне нужен английский язык. Комментарии не проблема - их можно перевести прямо в исходниках. А вот что делать с самими исходниками?. С одной стороны, все строки в исходниках должны быть написаны на английском. С другой стороны, для пользователя ничего не должно измениться. Интерфейс программы был на датском языке, значит должен оставаться на датском. &lt;br /&gt;&lt;br /&gt;В результате, мне пришлось провести де-локализацию исходных кодов с последующей обратной локализацией приложения. Другими словами: в исходниках датские строки я заменил английскими, а в процессе работы приложение подменяет английские строки датским переводом. &lt;br /&gt;&lt;br /&gt;Далее речь пойдет о том, как все это было сделано.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;h4&gt;Общая идея&lt;/h4&gt;Общий алгоритм работы выглядел так.&lt;ol&gt;&lt;li&gt;Выдрать из исходных кодов все строки и сохранить их в отдельном текстовом файле.&lt;/li&gt;&lt;li&gt;Перевести этот файл и получить перевод DK-&gt;ENG.&lt;/li&gt;&lt;li&gt;Выполнить замену (swap) языков в исходных кодах на основе полученного перевода: в pas и dfm файлах все фразы на датском заменить на соответствующие фразы на английском.&lt;/li&gt;&lt;li&gt;Создать реверсный файл перевода, т.е. из файла DK-&gt;ENG получить ENG-&gt;DK&lt;/li&gt;&lt;li&gt;Локализовать приложение используя реверсный файл перевода.&lt;/li&gt;&lt;/ol&gt;&lt;h4&gt;Извлечение и перевод строк&lt;/h4&gt;В качестве основного рабочего инструмента я выбрал &lt;a href="http://ru.wikipedia.org/wiki/Gettext"&gt;gettext&lt;/a&gt;. Библиотека проверенная, надежная и &lt;a href="http://www.delphiplus.org/articles/components/dxgettext/index.html"&gt;хорошо известная&lt;/a&gt;. Реализация &lt;a href="http://dxgettext.po.dk/"&gt;gettext для Delphi&lt;/a&gt; существует, юникод в ней поддерживается. Кроме того, для gettext существует множество готовых утилит "на все случаи жизни".&lt;br /&gt;&lt;br /&gt;Gettext умеет самостоятельно выдирать строки из исходных кодов. Вернее, автоматически он выдирает ресурсные строки и строки из GUI (dfm-файлов). Для того, чтобы он мог так же извлечь текстовые строки из PAS файлов, строки нужно заключить в gnugettext._(''):&lt;br /&gt;&lt;pre class="brush:delphi"&gt;using gnugettext;&lt;br /&gt;   ...&lt;br /&gt;   const s1: String = _('строка');&lt;br /&gt;   ...&lt;br /&gt;   ShowMessage(_('Ошибка!'));&lt;br /&gt;&lt;/pre&gt;После того, как все строки помечены, выдираем строки следующей командой:&lt;br /&gt;&lt;pre&gt;dxgettext.exe -r -b PROJECT_DIRECTORY --delphi --nonascii&lt;/pre&gt;В результате gettext создает &lt;a href="http://www.gnu.org/software/hello/manual/gettext/PO-Files.html"&gt;PO-файл&lt;/a&gt;, содержащий полный список строк. Вот пример PO-файла:&lt;br /&gt;&lt;pre&gt;msgid ""&lt;br /&gt;msgstr ""&lt;br /&gt;"Project-Id-Version: PACKAGE VERSION\n"&lt;br /&gt;"POT-Creation-Date: 2011-04-03 17:41\n"&lt;br /&gt;"PO-Revision-Date: 2011-04-03 17:41\n"&lt;br /&gt;"Last-Translator: Somebody &lt;your.email@address.com&gt;\n"&lt;br /&gt;"MIME-Version: 1.0\n"&lt;br /&gt;"Content-Type: text/plain; charset=UTF-8\n"&lt;br /&gt;"Content-Transfer-Encoding: 8bit\n"&lt;br /&gt;"X-Generator: dxgettext 1.2.2\n"&lt;br /&gt;&lt;br /&gt;#. Form4..Caption&lt;br /&gt;#. FormPriceList..price..priceprice..EditFormat&lt;br /&gt;#: PriceList.dfm:209&lt;br /&gt;#: Unit1.dfm:6&lt;br /&gt;msgid "Hyster administration"&lt;br /&gt;msgstr ""&lt;br /&gt;&lt;br /&gt;#: unit7.pas:179&lt;br /&gt;#: users.pas:25&lt;br /&gt;msgid "Mindst 8 karakterer langt"&lt;br /&gt;msgstr ""&lt;br /&gt;&lt;/pre&gt;Думаю, идея понятна. В msgid содержатся оригинальные строки, в msgstr - перевод. Повторяющиеся строки в PO-файле сгруппированы, так что каждую строку придется переводить один и только один раз. Выполнить перевод PO файла можно вручную, а можно с помощью &lt;a href="http://www.poedit.net/download.php"&gt;PO Editor&lt;/a&gt;. &lt;br /&gt;&lt;h4&gt;Замена языка в исходных кодах&lt;/h4&gt;Итак, все строки выдраны и переведены. Следующая задача - поместить перевод в исходные коды. К сожалению, готовой утилиты для такой операции в арсенале gettext не нашлось. Ее пришлось написать - DxGetTextLangSwapper: &lt;a href="http://code.google.com/p/dvsrc/downloads/detail?name=20110423DxGetTextLangSwapper.7z"&gt;скачать&lt;/a&gt;, просмотреть &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/CSharp/DxGetTextLangSwapper/"&gt;исходные коды на C#&lt;/a&gt;). &lt;br /&gt;&lt;br /&gt;С помощью DxGetTextLangSwapper процесс замены языка в исходных кодах выполняется следующим образом:&lt;br /&gt;&lt;pre&gt;DxGetTextLangSwapper PROJECT_DIRECTORY  SOURCE_PO_FILE TARGET_PO_FILE&lt;/pre&gt;Утилита перебирает все строки в исходном файле, находит в pas и dfm файлах места, где используется каждая строка, и заменяет строку ее переводом. Попутно, утилита генерирует реверсный PO-файл (если исходный файл содержит перевод строк с языка А на язык Б, то реверсный - с языка Б на язык А). &lt;br /&gt;&lt;br /&gt;Утилита использует файл конфигурации &lt;code&gt;DxGetTextLangSwapper.exe.config&lt;/code&gt;, позволяющий указать кодировки входных файлов:&lt;br /&gt;&lt;pre&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;configuration&amp;gt;&lt;br /&gt;&amp;lt;!--  &amp;lt;startup&amp;gt;&lt;br /&gt;    &amp;lt;supportedRuntime version="v2.0.50727" sku="Client"/&amp;gt;&lt;br /&gt;  &amp;lt;/startup&amp;gt;--&amp;gt;&lt;br /&gt;  &amp;lt;appSettings&amp;gt;&lt;br /&gt;   &amp;lt;!-- encoding of source PO file --&amp;gt;&lt;br /&gt;    &amp;lt;add key="PO_FILE_ENCODING" value="65001"&amp;gt;&amp;lt;/add&amp;gt;&lt;br /&gt;    &amp;lt;!-- encoding of PAS files --&amp;gt;&lt;br /&gt;    &amp;lt;add key="PAS_FILES_ENCODING" value="1252"&amp;gt;&amp;lt;/add&amp;gt;&lt;br /&gt;    &amp;lt;!-- encoding of DFM files--&amp;gt;&lt;br /&gt;    &amp;lt;add key="DFM_FILES_ENCODING" value="1252"&amp;gt;&amp;lt;/add&amp;gt;&lt;br /&gt;    &amp;lt;!--&lt;br /&gt;        Entries in PO file and source files can be different during encoding problem.&lt;br /&gt;        if BRUTOREPLACER turned on, then entry at specified text line is replaced always independent if actual value is equal to value from PO or isn't equal&lt;br /&gt;        Program outputs warning about this replace with detailed info.    &lt;br /&gt;    --&amp;gt;&lt;br /&gt;    &amp;lt;add key="USE_BRUTOREPLACER" value="0" &amp;gt;&amp;lt;/add&amp;gt;&lt;br /&gt;  &amp;lt;/appSettings&amp;gt;&lt;br /&gt;&amp;lt;/configuration&amp;gt;&lt;br /&gt;&lt;/pre&gt;Помимо кодировок, в файле есть параметр BRUTOREPLACER. Нужен он вот для чего. Dxgettext создает исходный файл в UTF8. Однако на практике некоторые строки могут попасть в PO файл в испорченном виде. Например, мне так и не удалось с помощью dxgettext вытащить датские строки в PO-файл корректно (ключ --nonascii не помог). Символы со всякими там умлаутами попортились. Человеку, для перевода, таких порченных строк достаточно, они вполне узнаваемы. Однако с подстановкой перевода будут проблемы. &lt;br /&gt;&lt;br /&gt;Дело в том, что DxGetTextLangSwapper извлекает строки корректно (если правильно указать кодировку в настройках). В результате, в процессе замены, порченные строки из PO файла и корректные строки, извлеченные DxGetTextLangSwapper, не совпадут. В этом случае DxGetTextLangSwapper не выполнит замену и придется эти строки позже менять ручками. &lt;br /&gt;&lt;br /&gt;Вот здесь и потребуется режим BRUTOREPLACER. Если указать в настройках BRUTOREPLACER=1, то DxGetTextLangSwapper выполнит замену даже в том случае, если строки не совпадают. Кстати, в этом режиме замена нескольких подряд идущих строк, находящихся в одной и той же строке файле, будет выполняться неправильно. Чтобы избежать проблем в каждой строке исходного файла должно быть не более одного вызова _(''). &lt;br /&gt;&lt;br /&gt;DxGetTextLangSwapper никогда не заменяет строки, если перевод строки отсутствует. Т.е. непереведенные строки в процессе работы игнорируются. &lt;br /&gt;&lt;br /&gt;Замечу напоследок, что парсеры PO и DFM файлов написаны на скорую руку, так что некоторые особенности этих форматов вполне могут быть не учтены, что может привести к проблемам. DxGetTextLangSwapper сообщает обо всех подозрительных случаях.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Реверсный PO файл&lt;/h4&gt;Полученный реверсный PO файл, скорее всего, потребуется доработать. Дело в том, что исходный PO файл дает однозначное соответствие исходных строк и переводов: A-&gt;Б. А вот реверсный перевод Б-&gt;A запросто может быть неоднозначным. В этом случае PO-файл не скомпилируется. Неоднозначности нужно устранить (вручную).&lt;br /&gt;&lt;br /&gt;Готовый PO файл нужно преобразовать в MO-формат (это умеет делать PO Editor). Как подключать MO-файл к приложению подробно расписано вот &lt;a href="http://www.delphiplus.org/articles/components/dxgettext/index.html"&gt;здесь&lt;/a&gt;, повторяться не буду.&lt;br /&gt;&lt;h4&gt;Итоги&lt;/h4&gt;С помощью Gettext и DxGetTextLangSwapper мне успешно удалось де-локализовать один проект. Сейчас на подходе второй, так что обкатаю методику.&lt;br /&gt;&lt;br /&gt;Утилита DxGetTextLangSwapper для замены языка в исходных кодах Delphi-проекта: &lt;s&gt;&lt;a href="http://code.google.com/p/dvsrc/downloads/detail?name=20110412DxGetTextLangSwapper.7z"&gt;скачать&lt;/a&gt;&lt;/s&gt;, просмотреть &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/CSharp/DxGetTextLangSwapper/"&gt;исходные коды на C#&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 23.04.2011&lt;/b&gt;: Успешно перевел второй проект. Правда, вылезло несколько ошибок в DxGetTextLangSwapper, связанных с парсингом формата PO. Поправил. Теперь не требуется совпадения количества строк "#." и "#:" перед msgid, и поддерживаются строки "#, fuzzy" (они просто игнорируются). &lt;a href="http://code.google.com/p/dvsrc/downloads/detail?name=20110423DxGetTextLangSwapper.7z"&gt;Скачать версию 1.1&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-4411197798687576132?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/4411197798687576132/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/04/delphi.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/4411197798687576132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/4411197798687576132'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/04/delphi.html' title='Делокализация Delphi приложения'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-9140888367244039455</id><published>2011-04-06T14:08:00.003+08:00</published><updated>2011-04-25T14:04:21.240+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='business'/><title type='text'>В 2011  году поменялись КБК.</title><content type='html'>Собрался платить налог за первый квартал (УСН, 6%). Обнаружил, что &lt;a href="http://www.r24.nalog.ru/nalot/su/kk/"&gt;поменялись КБК&lt;/a&gt;: &lt;pre&gt;в 2010 был: 1821050101&lt;b&gt;0&lt;/b&gt;011000110&lt;/pre&gt;&lt;pre&gt;в 2011 стал: 1821050101&lt;b&gt;1&lt;/b&gt;011000110&lt;/pre&gt;Не ошибитесь. Все-таки с налоговиками не соскучишься - то ОКАТО поменяют, то КБК, то форму какую-нибудь изменят, то отчетность новую введут. Вот сиди и отслеживай...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;. Все оказалось еще интереснее - &lt;a href="http://www.echelnokova.ru/ip-usno-6-dlya-chajnikov/ip-usno-6-dlya-chajnikov-nalog-za-2010-god-kbk-izmeneny.html"&gt;КБК за 2010 год тоже поменяли.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-9140888367244039455?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/9140888367244039455/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/04/2011.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/9140888367244039455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/9140888367244039455'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/04/2011.html' title='В 2011  году поменялись КБК.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-730721432660667791</id><published>2011-03-20T23:40:00.006+07:00</published><updated>2012-01-18T14:22:19.524+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='a17'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Android - установка и настройка среды разработки.</title><content type='html'>Программирование на Android начинается с установки Eclipse. В данной статье я рассмотрю порядок установки и настройки Eclipse для работы с Android под Windows. Причем постараюсь рассмотреть его "от" и "до", включая генерацию цифровой подписи, автоматизацию сборки приложения, настройку ProGuard и подключение средств статистического анализа.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;h4&gt;Устанавливаем Android SDK&lt;/h4&gt;Начнем с установки Android SDK. Процесс подробно &lt;a href="http://developer.android.com/sdk/installing.html"&gt;описан&lt;/a&gt; в руководстве разработчика. Первое что требуется - установить &lt;a href="http://java.sun.com/javase/downloads/index.jsp"&gt;JDK&lt;/a&gt;. По умолчанию JDK ставится в директорию типа &lt;code&gt;c:\program files\java\jdk1.6.0_24&lt;/code&gt;. &lt;br /&gt;&lt;tt&gt;Android SDK требует 32-битную версию JDK, 64-битная его не устраивает.&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Далее - скачать и запустить программу установки последней версии &lt;a href="http://developer.android.com/sdk/index.html"&gt;Android SDK&lt;/a&gt;. В настоящий момент, это &lt;a href="http://dl.google.com/android/installer_r12-windows.exe"&gt;installer_r12-windows.exe&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;tt&gt;По умолчанию Android SDK ставится в директорию &lt;code&gt;c:\program files\Android\android-sdk&lt;/code&gt;. Для того, чтобы избежать проблем с Proguard (ошибок типа &lt;a href="http://proguard.sourceforge.net/index.html#/manual/troubleshooting.html"&gt;Expecting class path separator ';' before 'Files\Java\...&lt;/a&gt;), следует устанавливать Android SDK в директорию, не содержащую в своем пути пробелы, например: &lt;code&gt;c:\utils\Android\android-sdk&lt;/code&gt;&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;После завершения установки запускаем SDK Manager. Выбираем packages, которые нужно установить. Устанавливаем все, выбрав Accept all. SDK Manager скачивает и устанавливает все выбранные пакеты, а затем перезапускается. Установка Android SDK завершена.&lt;h4&gt;Устанавливаем Eclipse&lt;/h4&gt;Следующий шаг - установка Eclipse. Детально процесс установки изложен в &lt;a href="http://developer.android.com/sdk/eclipse-adt.html#installing"&gt;руководстве разработчика&lt;/a&gt;. Он достаточно прост. &lt;a href="http://www.eclipse.org/downloads/"&gt;Качаем Eclipse classic,&lt;/a&gt; устанавливаем. Eclipse поставляется в виде zip-архива. Распаковываем его в любую директорию, например, &lt;code&gt;c:\utils\eclipse&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;&lt;tt&gt;Использовать для установки Eclipse директорию с пробелами в пути так же &lt;a href="http://stackoverflow.com/questions/4421262/using-proguard-for-android-in-eclipse-problem"&gt;не желательно&lt;/a&gt;.&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Теперь устанавливаем плагин для работы с android SDK - ADT Plugin. Запускаем Eclipse. Он предложит создать новый workspace, соглашаемся. &lt;br /&gt;&lt;br /&gt;&lt;tt&gt;Желательно workspace и проекты сохранять в директории, не содержащие в путях пробелы. Иначе могут возникнуть проблемы при автоматизации сборки - команда &lt;code&gt;ant release&lt;/code&gt; не любит пробелы в путях.&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Выбираем &lt;code&gt;Help&amp;gt;Install new software&lt;/code&gt;. Жмем кнопку "Add..", появляется окно "Add repository". В поле &lt;code&gt;Name&lt;/code&gt; вводим "ADT Plugin", в поле &lt;code&gt;Location&lt;/code&gt; указываем &lt;code&gt;https://dl-ssl.google.com/android/eclipse/&lt;/code&gt;. Ставим галочку "Developer Tools", дважды жмем &lt;code&gt;Next&lt;/code&gt;, принимаем лицензионное соглашение. После завершения загрузки плагина перегружаем Eclipse. &lt;br /&gt;&lt;br /&gt;Осталось указать Eclipse путь к Android SDK. Открываем &lt;code&gt;Window&amp;gt&gt;Preferences&amp;gt&gt;Android&lt;/code&gt;, в графе &lt;code&gt;SDK Location&lt;/code&gt; вводим &lt;code&gt;c:\program files\Android\android-sdk&lt;/code&gt;. Жмем кнопку "Apply" - появится список установленных API. Закрываем окно - Eclipse и Android SDK установлены.&lt;h4&gt;Создаем/добавляем проект&lt;/h4&gt;При первом запуске Eclipse был создан пустой workspace. Добавим в него новый проект. &lt;br /&gt;&lt;br /&gt;Выбираем &lt;code&gt;File\New\Project\Android\AndroidProject&lt;/code&gt;, указываем название нового проекта. Выбираем Build target - версию платформы, под которую вы будете вести разработку. Задаем &lt;code&gt;Min SDK Version&lt;/code&gt; - минимальный уровень API, необходимый для запуска вашего приложения. Задаем название приложения, название пакета (типа &lt;code&gt;com.mydomain.myapp&lt;/code&gt;), название базовой Activity.&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;Пара замечаний. Уровни API в &lt;code&gt;Build target&lt;/code&gt; и &lt;code&gt;Min SDK Version&lt;/code&gt; могут различаться. &lt;code&gt;Built target&lt;/code&gt; задает уровень API, доступный вам при разработке. &lt;code&gt;Min SDK Version&lt;/code&gt; задает минимально необходимый уровень API, который должен быть на устройстве пользователя, чтобы пользователь мог скачивать ваше приложение с маркета. Так что можно вести разработку под 2.2, и, одновременно, позволять скачивать это приложение пользователям, у которых на телефоне установлен Android 1.5. Иногда это &lt;a href="http://stackoverflow.com/questions/4215606/android-market-filters-and-build-target"&gt;требуется&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Еще момент - при выборе &lt;code&gt;Built target&lt;/code&gt; доступно несколько вариантов с одним и тем же уровнем API, например: &lt;code&gt;Android 2.3.1&lt;/code&gt; и &lt;code&gt;Google APIs&lt;/code&gt;. Второй вариант является расширением первого и содержит &lt;a href="http://code.google.com/intl/ru-RU/android/add-ons/google-apis/index.html"&gt;дополнительный функционал для простого доступа к службам и данным Google&lt;/a&gt;. Этот вариант потребуется вам, например, при работе с Google Maps.&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;А что делать, если проект у вас уже есть и его нужно просто открыть? Помнится, меня этот вопрос поставил в тупик в свое время - нет в Eclipse пункта меню "Open project". На самом деле все просто - проект нужно импортировать в workspace. Делается это так: &lt;code&gt;File&amp;gt;Import&amp;gt;General&amp;gt;Existing Projects into Workspace&lt;/code&gt;.&lt;h4&gt;Генерируем подпись&lt;/h4&gt;Итак, проект создан и он компилируется. Для того, чтобы готовое приложение можно было установить на Android, его необходимо подписать цифровой подписью. О том как это делать, детально &lt;a href="http://developer.android.com/guide/publishing/app-signing.html"&gt;рассказано&lt;/a&gt; в руководстве разработчика. Eclipse создает подпись для отладочных билдов самостоятельно. Для релизных билдов подпись делается вручную, но так же через Eclipse. &lt;br /&gt;&lt;br /&gt;Релизная версия приложения создается в Eclipse операцией Export. Цифровую подпись для релизной версии можно создать прямо в процессе экспорта. &lt;br /&gt;&lt;br /&gt;Запускаем экспорт: &lt;code&gt;File&amp;gt;Export&amp;gt;Android&amp;gt;Export Android Application&lt;/code&gt;. Выбираем проект, который следует экспортировать (в нашем случае, он один, выбирать нечего). Выбираем &lt;code&gt;Create new keystore&lt;/code&gt; - создать keystore-файл, в котором будет хранится наш приватный ключ (точнее, в таком файле можно хранить сколько угодно приватных ключей, например, по одному для каждого приложения).  Вводим имя файла и дважды вводим пароль, закрывающий доступ к keystore-файлу. &lt;br /&gt;&lt;br /&gt;Теперь добавляем в keystore-файл ключ для нашего приложения. Указываем алиас - короткое имя, по которому можно будет в дальнейшем к этому ключу обращаться. Дважды вводим пароль для доступа к ключу (теперь у нас два пароля: один для доступа к keystore-файлу, другой - для доступа к ключу в keystore-файле). Вводим информацию о себе и указываем период действия ключа. &lt;br /&gt;&lt;br /&gt;&lt;tt&gt;С периодом важно не ошибиться. Если срок годности вашего ключа истечет, вы не сможете подписывать им новые версии вашего приложения. А сменить ключ у приложения нельзя. Кроме того, если вы планируете размещать ваше приложение на Android Market, то срок действия ключа должен превышать дату 22 октября 2033, иначе приложение на маркет загрузить не удастся. Руководство разработчика рекомендует устанавливать срок действия ключа 25 лет.&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Созданный keystore-файл и пароли к нему надежно сохраняем от доступа посторонних личностей..&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;Маленький нюанс. Отладочные сборки приложения Eclipse подписывает автоматически созданным keystore-файлом. Такие файлы хранятся локально, в директории типа &lt;code&gt;C:\Users\UserName\.android\debug.keystore&lt;/code&gt;. Если вы ведете разработку на нескольких разных компьютерах, то на каждом из них у вас будет сгенерирован отдельный keystore-файл для отладочных сборок. Это сразу создаст проблемы при отладке на девайсе. Поработали на одном компьютере, установили отладочную версию приложения на девайс. Перешли на другой компьютер - новая версия поверх старой не ставится - ключи разные. Приходится каждый раз сносить приложение. Чтобы избежать таких проблем имеет смысл использовать один и тот же отладочный keystore-файл на всех компьютерах. Путь к ключу прописывается в настройках: &lt;code&gt;Windows&amp;gt;Preferences&amp;gt;Android&amp;gt;Build&lt;/code&gt;. Отладочный файл debug.keystore действителен в течении 365 дней, раз в год его придется пересоздавать (&lt;a href="http://stackoverflow.com/questions/2194808/debug-certificate-expired-error-in-eclipse-android-plugins"&gt;старый файл нужно удалить, Eclipse создаст новый&lt;/a&gt;).&lt;/tt&gt;&lt;br /&gt;&lt;h4&gt;Создаем эмулятор и подключаем девайс&lt;/h4&gt;Для отладки Android приложения удобно использовать одновременно как реальный девайс, так и эмулятор. Чтобы создать эмулятор, нужно запустить &lt;code&gt;SDK Manager&lt;/code&gt; и выбрать пункт &lt;code&gt;"Virtual devices"&lt;/code&gt;. При создании эмулятора указать название, платформу, размер SD-карты, выбрать экран и hardware. &lt;br /&gt;&lt;br /&gt;Эмулятор хорош тем, что позволяет проверить работоспособность приложения на разных конфигурациях железа, не имея этого железа под рукой. В то же время, у него есть ряд недостатков. В частности, он крайне медленно запускается (можно попробовать включить недавно реализованную поддержку &lt;a href="http://tools.android.com/recent/emulatorsnapshots"&gt;снапшотов&lt;/a&gt;, чтобы обойти эту проблему), на нем нет маркета (установить &lt;a href="http://www.tech-recipes.com/rx/10004/accessing-android-market-from-android-sdk/"&gt;можно&lt;/a&gt;, но не все приложения работают).&lt;br /&gt;&lt;br /&gt;Для подключения устройства Android к компьютеру вам потребуется установить USB-драйвер для данного конкретного устройства. Как это сделать и где искать драйвера написано в &lt;a href="http://developer.android.com/sdk/oem-usb.html"&gt;руководстве разработчика&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;&lt;tt&gt;Для некоторых устройств драйвера найти крайне сложно - имеет смысл зарегистрироваться на форуме &lt;a href="http://4pda.ru/forum/index.php?s=d3a209ce06b86c9ad736ee69401e7256&amp;showforum=269"&gt;4PDA.ru&lt;/a&gt; и поискать там (без регистрации файлы с форума не скачать).&lt;/tt&gt;&lt;br /&gt;&lt;h4&gt;Автоматизируем сборку проекта&lt;/h4&gt;Создание релизной версии проекта лучше сразу автоматизировать. При частых релизах возможность собрать приложения одним кликом сэкономит кучу времени. К тому же, сама процедура автоматизации занимает всего несколько минут. Подробно она описана вот &lt;a href="http://www.androidengineer.com/2010/06/using-ant-to-automate-building-android.html"&gt;здесь&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Cкачиваем &lt;a href="http://ant.apache.org/bindownload.cgi"&gt;Ant&lt;/a&gt;. В настоящий момент, последняя версия &lt;a href="http://www.sai.msu.su/apache//ant/binaries/apache-ant-1.8.2-bin.zip"&gt;apache-ant-1.8.2-bin.zip&lt;/a&gt;. Распаковываем архив в какую-нибудь директорию, например в &lt;code&gt;c:\program files\ant\apache-ant-1.8.2&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;По моему опыту, наличие пробелов в пути к Ant к каким-либо проблемам не приводит, но на 100% я это гарантировать не берусь.&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Убеждаемся, что у нас правильно прописаны пути. Нам необходимо добавить две директории в %PATH%:&lt;ul&gt;&lt;li&gt;путь к утилитам Android SDK, т.е. &lt;code&gt;c:\utils\Android\android-sdk\tools&lt;/code&gt;&lt;/li&gt;&lt;li&gt;путь к ant\bin, т.е. &lt;code&gt;c:\program files\ant\apache-ant-1.8.2\bin&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;Кроме того, необходимо правильно инициализировать две других переменные среды:&lt;ul&gt;&lt;li&gt;JAVA_HOME должна указывать на директори JDK, т.е. на &lt;code&gt;c:\program files\java\jdk1.6.0_24&lt;/code&gt; (важно: к JDK, а не к JRE)&lt;/li&gt;&lt;li&gt;ANT_HOME должна указывать на корневую директорию Ant, т.е. &lt;code&gt;c:\program files\ant\apache-ant-1.8.2&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Заходим в корневую директорию нашего проекта. Из командной строки выполняем команду &lt;pre&gt;android update project --path .&lt;/pre&gt;(не потеряйте точку в конце). Будет создан файл &lt;code&gt;build.xml&lt;/code&gt;. Его необходимо подправить. В строке &lt;code&gt;&amp;lt;project name="MainActivity" default="help"&amp;gt;&lt;/code&gt; нужно удалить &lt;code&gt;default="help"&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Сборка отладочной версии уже работает. Для сборки достаточно зайти в корневую директорию нашего проекта и дать команду "ant". &lt;br /&gt;&lt;br /&gt;Чтобы заработала сборка релиза, необходимо прописать параметры цифровой подписи релизных версий. Для этого в корневом каталоге нашего проекта создаем файл build.properties со следующим содержанием:&lt;br /&gt;&lt;pre&gt;key.store=путь_к_файлу_keystore&lt;br /&gt;key.alias=имя_алиаса&lt;br /&gt;key.store.password=пароль_к_keystore&lt;br /&gt;key.alias.password=пароль_к_ключу&lt;br /&gt;&lt;/pre&gt;Путь к keystore-файлу, имя алиаса и пароли, естественно, нужно подставить реальные.&lt;br /&gt;&lt;br /&gt;Теперь для сборки релизной версии приложения достаточно зайти в корневой каталог проекта и дать команду &lt;code&gt;ant release&lt;/code&gt;. Эта команда не любит пробелы в пути к директории проекта - так что если ваш проект сохранен в &lt;code&gt;C:\document and settings\...&lt;/code&gt;, то команда не сработает.&lt;br /&gt;&lt;h4&gt;Подключаем ProGuard&lt;/h4&gt;ProGuard нужен для обфускации и уменьшения размера кода. До недавнего времени, ProGuard приходилось подключать вручную. Вот &lt;a href="http://www.androidengineer.com/2010/07/optimizing-obfuscating-and-shrinking.html"&gt;здесь&lt;/a&gt; детально рассмотрено, как это сделать. Начиная с 9-ой версии SDK, ProGuard &lt;a href="http://habrahabr.ru/blogs/android_development/112833/"&gt;включен&lt;/a&gt; в состав Android SDK, так что процедура установки существенно упростилась. Все что нужно сделать - добавить в файл &lt;code&gt;build.properties&lt;/code&gt; еще одну строчку: &lt;code&gt;proguard.config=proguard.cfg&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;Файл &lt;code&gt;proguard.cfg&lt;/code&gt; уже есть в корневой директории проекта. Он генерируется при выполнении команды &lt;code&gt;"android update project --path ."&lt;/code&gt; вместе с файлом &lt;code&gt;build.xml&lt;/code&gt;. Один и тот же файл &lt;code&gt;proguard.cfg&lt;/code&gt; можно использовать в разных проектах. В build.properties в этом случае потребуется указывать полный путь к файлу: &lt;code&gt;proguard.config=path\proguard.cfg&lt;/code&gt;&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Теперь команда &lt;code&gt;ant release&lt;/code&gt; использует ProGuard. Для того, чтобы ProGuard использовался так же и при ручном экспорте из Eclipse, необходимо добавить в файл &lt;code&gt;default.properties&lt;/code&gt; аналогичную строчку&lt;br /&gt;&lt;pre&gt;proguard.config=proguard.cfg&lt;/pre&gt;Если при экспорте начнут начать вылазить ошибки, проверьте переменную среды %TEMP%: &lt;a href="http://stackoverflow.com/questions/4421262/using-proguard-for-android-in-eclipse-problem"&gt;нет ли пробелов в пути к директории с временными файлами(?)&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;Любопытно, что заголовке файла default.properties написано следующее:&lt;br /&gt;&lt;br /&gt;# This file is automatically generated by Android Tools.&lt;br /&gt;# Do not modify this file -- YOUR CHANGES WILL BE ERASED!&lt;br /&gt;&lt;br /&gt;Но в руководстве разработчика &lt;a href="http://developer.android.com/guide/developing/tools/proguard.html"&gt;прямо указано&lt;/a&gt;, что редактировать нужно этот именно файл.&lt;/tt&gt;&lt;h4&gt;Добавляем юнит-тестирование&lt;/h4&gt;Средства юнит-тестирования в Android &lt;a href="http://developer.android.com/guide/developing/testing/index.html"&gt;встроены&lt;/a&gt;, их &lt;a href="http://habrahabr.ru/blogs/android_development/113584/"&gt;можно использовать&lt;/a&gt; сразу, ничего не устанавливая. Тем не менее, есть &lt;a href="http://stackoverflow.com/questions/522312/best-practices-for-unit-testing-android-apps"&gt;дополнительные инструменты&lt;/a&gt;, позволяющие упростить процесс тестирования. На них останавливаться не буду - эта тема заслуживает отдельного поста. &lt;br /&gt;&lt;h4&gt;Подключаем средства статистического анализа&lt;/h4&gt;Для Eclipse существует &lt;a href="http://programmers.stackexchange.com/questions/15776/what-are-the-top-developer-productivity-tools-plugins-for-eclipse-java"&gt;множество&lt;/a&gt; полезных плагинов. В частности, есть парочка очень неплохих плагинов для статического анализа кода: &lt;a href="http://findbugs.sourceforge.net/"&gt;FindBugs&lt;/a&gt; и &lt;a href="http://checkstyle.sourceforge.net/index.html"&gt;Checkstyle&lt;/a&gt;. По функционалу они напоминают FxCop и StyleCop (плагины для Visual Stuido, знакомые NET-программистам). &lt;br /&gt;&lt;br /&gt;Статический анализ кода - штука очень полезная, позволяющая найти многие ошибки еще до запуска приложения. Так что эти плагины имеет смысл установить. Ставятся они точно тем же путем, что и ADT Plugin: вызываем Help &amp;gt; Install new software, в поле "Work with" указываем &lt;a href="http://findbugs.cs.umd.edu/eclipse/"&gt;http://findbugs.cs.umd.edu/eclipse/&lt;/a&gt; (для FindBugs) или &lt;a href=" http://eclipse-cs.sf.net/update/"&gt;http://eclipse-cs.sf.net/update/&lt;/a&gt; (для Checkstyle).&lt;br /&gt;&lt;br /&gt;Интересно, что FindBugs (по идее) работает с скомпилированными JAR-файлами (подобно FxCop) и не требует исходных кодов. В Android формат байткода другой. Тем не менее, FindBugs успешно работает и с Android-приложениями. Похоже, он не только байткод умеет анализировать, но и исходники напрямую.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;: Дошли руки, разобрался со статическими анализаторами и написал подробный отчет - &lt;a href="http://derevyanko.blogspot.com/2012/01/android.html"&gt;"Статические анализаторы кода для Android-приложения"&lt;/a&gt;. Всего, в настоящий момент, нашлось восемь готовых к использованию анализаторов. И как минимум пять из них имеет смысл регулярно применять в работе.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Итоги&lt;/h4&gt;Что мы получили в итоге? Eclipse, настроенный для разработки Android приложения, плюс возможность собирать релизную версию Android приложения (полностью готовую для публикации на маркете) одной командой. Весь процесс установки занимает минут 20-30 - без учета времени скачивания компонентов.&lt;br /&gt;&lt;br /&gt;В процессе написания данной статьи я проделал настройку Eclipse "с нуля" на виртуальной машине и постарался отметить все места, которые могут вызвать вопросы. Возможно, кое-где процесс установки описан недостаточно полно. Появятся вопросы - пишите, я постараюсь подправить описание.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-730721432660667791?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/730721432660667791/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/03/android.html#comment-form' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/730721432660667791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/730721432660667791'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/03/android.html' title='Android - установка и настройка среды разработки.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-2098403100415426646</id><published>2011-03-10T11:45:00.004+07:00</published><updated>2011-03-13T21:56:05.694+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='delphi'/><title type='text'>Kill Word. Автоматизация удаления зависших процессов, запущенных из сервиса.</title><content type='html'>Имеется сервис, конвертирующий документы из doc в html. В качестве конвертера используется Microsoft Word. Сервис управляет им через средства автоматизации. При каждом запросе на конвертацию сервис запускает экземпляр Word, открывает в нем документ, дает команду "сохранить в в виде HTML" и закрывает Word. Все прекрасно работает, но &lt;b&gt;иногда&lt;/b&gt; процессы Word подвисают. Одна операция на сотню или даже тысячу запусков приводит к ошибке и подвисшему процессу winword.exe в Process Explorer. &lt;br /&gt;&lt;br /&gt;Если включить в настройках сервиса галочку "Разрешить взаимодействие с рабочим столом" и сделать окно Word видимым, то причина зависания проясняется: Word что-то сообщил пользователю и ждет нажатия кнопки. Процесс Word работает на невидимом рабочем столе, так что ответить ему никто никогда не сможет. &lt;br /&gt;&lt;br /&gt;Причины появления сообщений об ошибках могут быть самыми разными - при гигантском разнообразии конвертируемых документов без ошибок не обойтись. Остается только периодически удалять ручками такие зависшие процессы. Но конечно же, лучше этот процесс автоматизировать. &lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Имеются два очевидных варианта автоматизации:&lt;ol&gt;&lt;li&gt;Периодически проходить по списку процессов, определять время запуска каждого &lt;code&gt;windord.exe&lt;/code&gt; и вычислять длительность его работы. Если процесс работает слишком долго, завершать его.&lt;/li&gt;&lt;li&gt;Отслеживать время выполнения операции конвертирования. Если время выполнения операции превышает допустимое, находить и уничтожать соответствующий &lt;code&gt;winword.exe.&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;Первый метод реализовать достаточно просто - процессы &lt;code&gt;winword.exe&lt;/code&gt; будут успешно уничтожаться. Но в таком решении не все гладко:&lt;ul&gt;&lt;li&gt;Если процессы &lt;code&gt;winword.exe&lt;/code&gt; проверять слишком часто, мы напрасно снизим производительность системы. Если слишком редко, количество зависших процессов может стать недопустимо большим. Дело в том, что в редких случаях зависание &lt;code&gt;winword.exe&lt;/code&gt; приводит к лавинному эффекту, когда каждый последующий запущенный &lt;code&gt;winword.exe&lt;/code&gt; зависает .. и сервис валится&lt;/li&gt;&lt;li&gt;Нет возможности выполнить какие-либо действия непосредственно в момент подвисания (запись в лог, отправка сообщения на email, что угодно).&lt;/li&gt;&lt;/ul&gt;Второй метод вроде бы способен решить указанные проблемы. Будем запускать операцию конвертирования в отдельном потоке и ждать завершение потока в течении заданного времени. В случае таймаута, найдем &lt;code&gt;winword.exe&lt;/code&gt;, запущенный потоком, и принудительно его завершим. &lt;br /&gt;&lt;br /&gt;Но здесь есть две проблемы.&lt;ul&gt;&lt;li&gt;Не ясно, как определить, какой именно &lt;code&gt;winword.exe&lt;/code&gt; был запущен нашим потоком. На самом деле, для конкретного &lt;code&gt;winword.exe&lt;/code&gt; мы не только не можем определить родительский поток, но даже родительский процесс. Точнее, процесс то мы &lt;a href="http://theroadtodelphi.wordpress.com/2009/09/27/getting-the-parent-process-of-the-current-application-using-delphi/"&gt;найдем&lt;/a&gt;, но это будет &lt;code&gt;svchost.exe&lt;/code&gt;, а вовсе не наш процесс - все потому, что мы имеем дело с сервисом, а не с обычным приложением.&lt;/li&gt;&lt;li&gt;Поток может благополучно завершится (мы не получим сообщение о таймауте), а процесс &lt;code&gt;winword.exe&lt;/code&gt; останется висеть навсегда.&lt;/li&gt;&lt;/ul&gt;Первая проблема решается обходным путем. При запуске &lt;code&gt;Word.Application&lt;/code&gt; можно помещать в заголовок окна &lt;code&gt;Word&lt;/code&gt; некую уникальную строку. Например, GUID или путь к конвертируемому документу. В случае, если &lt;code&gt;winword.exe&lt;/code&gt; зависнет, его можно будет найти по этой строке - достаточно будет перебрать все процессы &lt;code&gt;winword.exe&lt;/code&gt;, в каждом перебрать все окна и проверить заголовок каждого окна на наличие этой уникальной строки. &lt;br /&gt;&lt;br /&gt;А вот вторая проблема, похоже, не решается никак. Так что на практике придется либо ограничится первым методом и смириться с его ограничениями, либо использовать оба метода: те зависания, которые можно отловить немедленно - отлавливать немедленно, плюс изредка проводить "сборку мусора".&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Практическая реализация&lt;/h2&gt;Для своих целей я реализовал оба метода в модуле ProcessKiller.pas. В нем реализованы две функции для поиска и уничтожения зависших процессов:&lt;ul&gt;&lt;li&gt;&lt;code&gt;FindAndKillProcessByPid&lt;/code&gt; - находит и уничтожает все процессы &lt;code&gt;winword.exe&lt;/code&gt;, которые работают слишком долго.&lt;/li&gt;&lt;li&gt;&lt;code&gt;FindAndKillProcessByWindow&lt;/code&gt; - находит и уничтожает все процессы &lt;code&gt;winword.exe&lt;/code&gt;, содержащие окно с заданной строкой в заголовке.&lt;/li&gt;&lt;/ul&gt;Эти функции объявлены следующим образом:&lt;pre class="brush:delphi"&gt;//найти и уничтожить все процессы &lt;br /&gt;//у которых имя exe файла совпадает с processExeFileName&lt;br /&gt;//и которые созданы более чем timeOutMs миллисекунд назад&lt;br /&gt;function FindAndKillProcessByPid(&lt;br /&gt;  const processExeFileName: String; &lt;br /&gt;  const timeOutMs: Integer)&lt;br /&gt;: Integer; //возвращает количество уничтоженных процессов&lt;br /&gt;&lt;br /&gt;//найти и уничтожить все процессы &lt;br /&gt;//у которых имя exe файла совпадает с processExeFileName&lt;br /&gt;//и у которых имеется окно, содержащее в заголовке &lt;br /&gt;//строку strCaption&lt;br /&gt;function FindAndKillProcessByWindow(&lt;br /&gt;  const processExeFileName: String; &lt;br /&gt;  const strCaption: String): Integer;&lt;br /&gt;&lt;/pre&gt;На практике функций, принимающих константы, скорее всего будет недостаточно. Нужна возможность варьировать критерии поиска "зависшего процесса", определения нужного exe-файла и нужного окна. Например, имена exe-файлов и уникальные строки могут задаваться списками; или может потребоваться уничтожить процесс, если он занимает слишком много памяти/CPU и т.д. На этот случай в ProcessKiller.pas, имеется другие варианты тех же функций &lt;code&gt;FindAndKillProcessByWindow&lt;/code&gt; и &lt;code&gt;FindAndKillProcessByWindow&lt;/code&gt;, которые в качестве параметров вместо констант принимают функторы.&lt;br /&gt;&lt;br /&gt;Там же объявлена пара классов, с помощью которых можно определить функторы со стандартным поведением. Для удобства, привожу текст модуля ProcessKiller целиком.&lt;br /&gt;&lt;pre class="brush:delphi"&gt;unit ProcessKiller;&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;uses Windows, SysUtils, Classes, tlhelp32, Messages;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;  tmatch_functor = function (srcName: PWideChar): Boolean of object;&lt;br /&gt;  pmatch_functor = ^tmatch_functor;&lt;br /&gt;&lt;br /&gt;  tpid_matcher_functor = function (srcPid: LongInt): Boolean of object;&lt;br /&gt;  ppid_matcher_functor = ^tpid_matcher_functor;&lt;br /&gt;&lt;br /&gt;//finds process  matched to fMatchExeName&lt;br /&gt;//enumerates windows in the process and finds one with captionString in its title bar&lt;br /&gt;//if window is found then the function kills the process&lt;br /&gt;function FindAndKillProcessByWindow(&lt;br /&gt;  fMatchExeName: tmatch_functor; //selects process by exe filename&lt;br /&gt;  fMatchWindowCaption: tmatch_functor  //selects window by window caption&lt;br /&gt;): Integer; overload;//returns count killed processes&lt;br /&gt;&lt;br /&gt;//finds process matched to fMatchExeName&lt;br /&gt;//checks its start time. If process is outdated then kills it.&lt;br /&gt;function FindAndKillProcessByPid(fMatchExeName: tmatch_functor; //selects process by exe filename&lt;br /&gt;  fMatchPid: tpid_matcher_functor //check PID and decide if process should be killed&lt;br /&gt;): Integer; overload;//returns count killed processes&lt;br /&gt;&lt;br /&gt;//**** simplified (and not flexible) versions of same functions&lt;br /&gt;function FindAndKillProcessByPid(&lt;br /&gt;  const processExeFileName: String;&lt;br /&gt;  const timeOutMs: Integer)&lt;br /&gt;: Integer; overload;&lt;br /&gt;&lt;br /&gt;function FindAndKillProcessByWindow(&lt;br /&gt;  const processExeFileName: String;&lt;br /&gt;  const strCaption: String)&lt;br /&gt;: Integer; overload;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;//**** a couple of simple matchers to implement&lt;br /&gt;//simplified versions of FindAndKillProcessByPid and  FindAndKillProcessByWindow&lt;br /&gt;  TMatcher = class&lt;br /&gt;  public&lt;br /&gt;    constructor Create(const sample: String);&lt;br /&gt;    function EqualToI(srcName: PWideChar): Boolean;&lt;br /&gt;    function EndWithI(srcName: PWideChar): Boolean;&lt;br /&gt;  private&lt;br /&gt;    m_Sample: String;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;  TPidOutdatedMatcher = class&lt;br /&gt;  public&lt;br /&gt;    constructor Create(intervalMS: Integer);&lt;br /&gt;    function IsOutdated(srcPid: LongInt): Boolean;&lt;br /&gt;  private&lt;br /&gt;    m_IntervalMS: Integer;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;//**** auxiliary functions:&lt;br /&gt;//find all processes with exe file names that match to search criteria&lt;br /&gt;procedure GetListProcesses(fMatchExeName: tmatch_functor; dest: TStringList);&lt;br /&gt;&lt;br /&gt;//find all windows in specified process which captions match to search criteria&lt;br /&gt;function FindWindowInProcess(const processId: Cardinal; fMatchWindowCaption: tmatch_functor): Boolean;&lt;br /&gt;&lt;br /&gt;//kill specified process&lt;br /&gt;procedure KillProcess(const processId: Cardinal);&lt;br /&gt;&lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;procedure GetListProcesses(fMatchExeName: tmatch_functor; dest: TStringList);&lt;br /&gt;var handle: THandle;&lt;br /&gt;  ProcStruct: PROCESSENTRY32; // from "tlhelp32" in uses clause&lt;br /&gt;begin&lt;br /&gt;  handle := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);&lt;br /&gt;  if handle &amp;lt;&amp;gt; 0 then try&lt;br /&gt;    ProcStruct.dwSize := sizeof(PROCESSENTRY32);&lt;br /&gt;    if Process32First(handle, ProcStruct) then repeat&lt;br /&gt;      if fMatchExeName(PWideChar(@ProcStruct.szExeFile)) then begin&lt;br /&gt;        dest.AddObject('', Pointer(ProcStruct.th32ProcessID));&lt;br /&gt;      end;&lt;br /&gt;    until not Process32Next(handle, ProcStruct);&lt;br /&gt;  finally&lt;br /&gt;    CloseHandle(handle);&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function FindAndKillProcessByPid(fMatchExeName: tmatch_functor; fMatchPid: tpid_matcher_functor): Integer;&lt;br /&gt;var list: TStringList;&lt;br /&gt;    i: Integer;&lt;br /&gt;    pid: Cardinal;&lt;br /&gt;begin&lt;br /&gt;  Result := 0;&lt;br /&gt;  list := TStringList.Create;&lt;br /&gt;  try&lt;br /&gt;    GetListProcesses(fMatchExeName, list);&lt;br /&gt;    for i := 0 to list.Count - 1 do begin&lt;br /&gt;      pid := Cardinal(list.Objects[i]);&lt;br /&gt;      if fMatchPid(pid) then begin&lt;br /&gt;        KillProcess(pid);&lt;br /&gt;        inc(Result);&lt;br /&gt;      end;&lt;br /&gt;    end;&lt;br /&gt;  finally&lt;br /&gt;    list.Free;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function FindAndKillProcessByWindow(fMatchExeName: tmatch_functor; fMatchWindowCaption: tmatch_functor): Integer;&lt;br /&gt;var list: TStringList;&lt;br /&gt;    i: Integer;&lt;br /&gt;    pid: Cardinal;&lt;br /&gt;begin&lt;br /&gt;  Result := 0;&lt;br /&gt;  list := TStringList.Create;&lt;br /&gt;  try&lt;br /&gt;    GetListProcesses(fMatchExeName, list);&lt;br /&gt;    for i := 0 to list.Count - 1 do begin&lt;br /&gt;      pid := Cardinal(list.Objects[i]);&lt;br /&gt;      if FindWindowInProcess(pid, fMatchWindowCaption) then begin&lt;br /&gt;        KillProcess(pid);&lt;br /&gt;        inc(Result);&lt;br /&gt;      end;&lt;br /&gt;    end;&lt;br /&gt;  finally&lt;br /&gt;    list.Free;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;  wrapper = record  //helper class to pass functor to enum_window_proc&lt;br /&gt;    m_F: tmatch_functor;&lt;br /&gt;  end;&lt;br /&gt;  pwrapper = ^wrapper;&lt;br /&gt;&lt;br /&gt;function enum_window_proc(hWnd: HWND; lparam: LPARAM): BOOL; stdcall;&lt;br /&gt;var buffer: String;&lt;br /&gt;begin&lt;br /&gt;  SetLength(buffer, MAX_PATH);&lt;br /&gt;  Result := true;&lt;br /&gt;  if SendMessage(hWnd, WM_GETTEXT, MAX_PATH, Integer(@buffer[1])) &amp;lt;&amp;gt; 0 then begin&lt;br /&gt;    if pwrapper(lparam)^.m_F(@buffer[1]) then Result := false;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function FindWindowInProcess(const processId: Cardinal; fMatchWindowCaption: tmatch_functor): Boolean;&lt;br /&gt;var snap_proc_handle: THandle;&lt;br /&gt;  next_proc: Boolean;&lt;br /&gt;  thread_entry: TThreadEntry32;&lt;br /&gt;  w: wrapper;&lt;br /&gt;begin&lt;br /&gt;  w.m_F := fMatchWindowCaption;&lt;br /&gt;  Result := false;&lt;br /&gt;  snap_proc_handle := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); //enumerate all threads&lt;br /&gt;  if (snap_proc_handle &amp;lt;&amp;gt; INVALID_HANDLE_VALUE) then try&lt;br /&gt;    thread_entry.dwSize := SizeOf(thread_entry);&lt;br /&gt;    next_proc := Thread32First(snap_proc_handle, thread_entry);&lt;br /&gt;    while next_proc do begin&lt;br /&gt;      if thread_entry.th32OwnerProcessID = processId then begin //check the owner Pid against the PID requested&lt;br /&gt;        if not EnumThreadWindows(thread_entry.th32ThreadID, @enum_window_proc, LPARAM(@w)) then begin&lt;br /&gt;          Result := true;&lt;br /&gt;          break;&lt;br /&gt;        end;&lt;br /&gt;      end;&lt;br /&gt;      next_proc := Thread32Next(snap_proc_handle, thread_entry);&lt;br /&gt;    end;&lt;br /&gt;  finally&lt;br /&gt;    CloseHandle(snap_proc_handle);&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure KillProcess(const processId: Cardinal);&lt;br /&gt;var nerror: Integer;&lt;br /&gt;    process: THandle;&lt;br /&gt;    fdwExit: DWORD;&lt;br /&gt;begin //see http://stackoverflow.com/questions/4690472/error-invalid-handle-on-terminateprocess-vs-c&lt;br /&gt;  process := OpenProcess(PROCESS_TERMINATE, TRUE, processId);&lt;br /&gt;  fdwExit := 0;&lt;br /&gt;  GetExitCodeProcess(process, fdwExit);&lt;br /&gt;  if not TerminateProcess(process, fdwExit) then begin&lt;br /&gt;    nerror := GetLastError;&lt;br /&gt;  end;&lt;br /&gt;  CloseHandle(process);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;{ TMatcher }&lt;br /&gt;constructor TMatcher.Create(const sample: String);&lt;br /&gt;begin&lt;br /&gt;  m_Sample := sample;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TMatcher.EndWithI(srcName: PWideChar): Boolean;&lt;br /&gt;var&lt;br /&gt;  len: Integer;&lt;br /&gt;  len_sample: Integer;&lt;br /&gt;  pw: PWideChar;&lt;br /&gt;begin&lt;br /&gt;  Result := false;&lt;br /&gt;  len_sample := Length(m_Sample);&lt;br /&gt;  len := lstrlen(srcName);&lt;br /&gt;  if (len &amp;gt;= len_sample) then begin&lt;br /&gt;      pw := PWideChar(@srcName[len - len_sample]);&lt;br /&gt;      if lstrcmpi(pw, PWideChar(m_Sample)) = 0 then begin&lt;br /&gt;        Result := true;&lt;br /&gt;      end;&lt;br /&gt;    end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TMatcher.EqualToI(srcName: PWideChar): Boolean;&lt;br /&gt;begin&lt;br /&gt;  Result := lstrcmpi(srcName, PWideChar(m_Sample)) = 0;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;{ TPidOutdatedMatcher }&lt;br /&gt;constructor TPidOutdatedMatcher.Create(intervalMS: Integer);&lt;br /&gt;begin&lt;br /&gt;  m_IntervalMS := intervalMS;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TPidOutdatedMatcher.IsOutdated(srcPid: Integer): Boolean;&lt;br /&gt;var tc, te, tk, tu, current: FILETIME;&lt;br /&gt;    u0, u1: ULARGE_INTEGER;&lt;br /&gt;    process: THandle;&lt;br /&gt;    st: SYSTEMTIME;&lt;br /&gt;begin&lt;br /&gt;  Result := false;&lt;br /&gt;  process := OpenProcess(PROCESS_ALL_ACCESS, TRUE, srcPid);&lt;br /&gt;  try&lt;br /&gt;    if GetProcessTimes(process, tc, te, tk, tu) then begin&lt;br /&gt;      FileTimeToSystemTime(tc, st);&lt;br /&gt;      GetSystemTimeAsFileTime(current);&lt;br /&gt;&lt;br /&gt;      FileTimeToSystemTime(current, st);&lt;br /&gt;      u1.LowPart := current.dwLowDateTime;&lt;br /&gt;      u1.HighPart := current.dwHighDateTime;&lt;br /&gt;&lt;br /&gt;      u0.LowPart := tc.dwLowDateTime;&lt;br /&gt;      u0.HighPart := tc.dwHighDateTime;&lt;br /&gt;&lt;br /&gt;      if (u1.QuadPart - u0.QuadPart) / 10000 &amp;gt; m_IntervalMS then Result := true;&lt;br /&gt;    end;&lt;br /&gt;  finally&lt;br /&gt;    CloseHandle(process);&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function FindAndKillProcessByPid(const processExeFileName: String; const timeOutMs: Integer): Integer;&lt;br /&gt;var&lt;br /&gt;  me: TMatcher;&lt;br /&gt;  md: ProcessKiller.TPidOutdatedMatcher;&lt;br /&gt;begin&lt;br /&gt;  md := nil;&lt;br /&gt;  me := TMatcher.Create(processExeFileName);&lt;br /&gt;  try&lt;br /&gt;    md := TPidOutdatedMatcher.Create(timeOutMs);&lt;br /&gt;    ProcessKiller.FindAndKillProcessByPid(me.EqualToI, md.IsOutdated);&lt;br /&gt;  finally&lt;br /&gt;    md.Free;&lt;br /&gt;    me.Free;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function FindAndKillProcessByWindow(const processExeFileName: String; const strCaption: String): Integer;&lt;br /&gt;var&lt;br /&gt;  me: TMatcher;&lt;br /&gt;  ms: TMatcher;&lt;br /&gt;begin&lt;br /&gt;  ms := nil;&lt;br /&gt;  me := TMatcher.Create(processExeFileName);&lt;br /&gt;  try&lt;br /&gt;    ms := TMatcher.Create(strCaption);&lt;br /&gt;    ProcessKiller.FindAndKillProcessByWindow(me.EqualToI, ms.EndWithI);&lt;br /&gt;  finally&lt;br /&gt;    me.Free;&lt;br /&gt;    ms.Free;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Демонстрационное приложение WordKiller&lt;/h2&gt;Проверить работоспособность модуля ProcessKiller.pas можно с помощью тестового приложения WordKiller. &lt;br /&gt;&lt;br /&gt;Это сервис, способный выполнять две задачи. &lt;ul&gt;&lt;li&gt;С заданной периодичностью проверять список процессов и уничтожать процессы, которые работают слишком долго (первый метод). Имя exe-файла процесса, интервал запуска и максимально допустимый период работы процессов задаются в конфигурационном файле &lt;code&gt;WordKiller.ini&lt;/code&gt;&lt;/li&gt;&lt;li&gt;С заданной периодичностью запускать Word, открывать в нем тестовый документ, а затем находить и уничтожать открытый процесс &lt;code&gt;winword.exe&lt;/code&gt; с помощью &lt;code&gt;FindAndKillProcessByWindow&lt;/code&gt; (второй метод).&lt;/li&gt;&lt;/ul&gt;Первую функцию можно использовать в готовом виде - как сервис, способный периодически проверять и уничтожать зависшие потоки. Вторая функция добавлена исключительно в целях тестирования. Включается и отключается она в &lt;code&gt;WordKiller.ini&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Чтобы установить сервис нужно выполнить команду &lt;code&gt;WordKiller.exe /install&lt;/code&gt;, чтобы деинсталлировать - &lt;code&gt;WordKiller.exe /uninstall&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Если вы найдете ошибку в приложении - буду признателен, если сообщите о ней в комментариях.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Исходные коды&lt;/h2&gt;&lt;a href="http://code.google.com/p/dvsrc/downloads/detail?name=20110310WordKiller.7z&amp;can=2&amp;q="&gt;Скачать исходные коды проекта WordKiller.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/dvsrc/source/browse/#svn%2Ftrunk%2FDelphi%2FWordKiller"&gt;Просмотреть online&lt;/a&gt; исходные коды WordKiller.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-2098403100415426646?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/2098403100415426646/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/03/kill-word.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/2098403100415426646'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/2098403100415426646'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/03/kill-word.html' title='Kill Word. Автоматизация удаления зависших процессов, запущенных из сервиса.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-6692430175217747186</id><published>2011-02-27T23:53:00.007+07:00</published><updated>2011-03-01T15:45:18.510+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='business'/><title type='text'>Отчетность ИП в ПФРФ за 2010.</title><content type='html'>Каждый индивидуальный предприниматель платит взносы в пенсионный фон на страховую и накопительную часть. До 2010 года никакой отчетности по этим взносам в ПФРФ предоставлять не требовалось (по крайней мере для ИП, которые не нанимают работников). Сходил, сделал сверку - и все. С нынешнего года нужно предоставлять "декларацию" - отчетность по форме РСВ-2. Ее надо представить в пенсионный фонд &lt;a href="http://www.nalogservice.ru/faq/document26263.htm"&gt;до 1 марта 2011 года&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;На сайте ПФРФ есть &lt;a href="http://www.pfrf.ru/userdata/presscenter/pr/polygraphy/2010/booklet/samozaniatoe.pdf"&gt;брошюрка&lt;/a&gt;, где все подробно расписано. Бланк формы РСВ-2 в брошюрке приведен, но в виде PDF его использовать не удобно. Не проблема - вот бланк &lt;a href="http://www.klerk.ru/blank/170300/"&gt;РСВ-2 в формате Excel&lt;/a&gt;. Но и он скорее всего будет бесполезен. И вот почему.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;В указанной брошюрке сказано: "Для заполнения Расчета используйте средства вычислительной техники или шариковую (перьевую) ручку". На практике ручки не прокатывают. Отчетность нужна в электронном виде. Приходите в пенсионный фонд и получаете три программы - &lt;a href="http://www.opfr-komi.clarionlife.net/dokumenty-pu-5/"&gt;Документы ПУ 5&lt;/a&gt;,  &lt;a href="http://www.pfrf.ru/ot_tyumen/soft/5454.html"&gt;CheckXML, CheckXML-UFA&lt;/a&gt;. Первая служит для заполнения разнообразной отчетности для ПФРФ, последние две - для проверки заполненной отчетности. Подозреваю, что в разных отделениях ПФРФ набор выдаваемого программного обеспечения может отличаться. Но мне досталась программа "Документы ПУ 5" и пришлось работать в ней.&lt;br /&gt;&lt;br /&gt;Общий порядок работы с программой таков. Устанавливаете, запускаете. Вносите в окно "Справочники-&gt;Страхователи/Работодатели" информацию о себе - фио, коды, инн, адрес и т.д.  Далее в окне "Документы-&gt;Расчет страховых взносов: РСВ-1, РСВ-2" заполняете форму РСВ-2 (выбираете страхователя, проверяете раздел "Титульный лист", заполняете "Раздел 1" - период предпринимательской деятельности, "Раздел 2.1" - 4 числа в строке "уплачено с начала расчетного периода"), сохраняете готовую форму в XML. Далее с помощью двух других программ проверяете корректность получившегося XML-файла. XML и логи проверки складываете на флешку, распечатываете готовую форму РСВ-2 в двух экземплярах, подписываете и несете в ПФРФ.&lt;br /&gt;&lt;br /&gt;Честно говоря, на мой взгляд программа "Документы ПУ 5" - ужасна. Ошибки, подвисания, топорный интерфейс, непонятные сообщения о проблемах в заполнении формы, полное отсутствие справки по РСВ-2. Конечно, жаловаться смысла нет, поэтому просто перечислю проблемы, с которыми я столкнулся. Возможно кому-нибудь пригодятся найденные решения. &lt;br /&gt;&lt;br /&gt;Первая проблема: где взять регистрационный номер ТФОМС. Его можно узнать пряом на сайте ТФОМС. Вполне возможно, что такую услугу предоставляют не всех сайтах ТФОМС, но на красноярском - она &lt;a href="http://nus.krasmed.ru:7777/rn/faces/pages/TPred.jspx"&gt;есть&lt;/a &gt; точно.&lt;br /&gt;&lt;br /&gt;Вторая проблема - ошибка при проверке: &lt;i&gt;"Причина. Нарушена структура блока. Отсутствует обязательный элемент (либо нарушен порядок следования элементов). &lt;br /&gt;Ожидался элемент: ДанныеДляРасчетаСуммНачисленныхСтраховыхВзносов. Источник. &amp;lt;ЛицоПодтверждающееСведения&amp;gt;1&amp;lt;/ЛицоПодтверждающееСведения&amp;gt;"&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Причина ошибки - не заполнен "раздел 2.1" в РСВ-2. Вам нужно обязательно указать в этом разделе период осуществления предпринимательской деятельности (с 1 января по 31 декабря, если вы зарегистрировали ИП не в прошедшем году, а ранее). По умолчанию он не указан. А сообщение об этой ошибке, скажем так, замысловатое.&lt;br /&gt;&lt;br /&gt;Третья проблема: адрес страхователя. Его ввод нетривиален. Если вы просто указали название города и улицы, то проверочная программа выдаст что-то такое:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;***30: Ошибка в адресе &amp;lt;АдресРегистрации&amp;gt;: так как указано &amp;lt;ГеографическоеНазвание&amp;gt; для элемента адреса &amp;lt;Город&amp;gt;, то должно быть указано &amp;lt;Сокращение&amp;gt; для этого элемента:&lt;br /&gt;&lt;br /&gt;***30: Ошибка в адресе &amp;lt;АдресРегистрации&amp;gt;: так как указано &amp;lt;ГеографическоеНазвание&amp;gt; для элемента адреса &amp;lt;Улица&amp;gt;, то должно быть указано &amp;lt;Сокращение&amp;gt; для этого элемента:&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Секрет в том, что надо писать не просто "Красноярск", а "Красноярск Г", не просто улица "МИРА", а "МИРА УЛ". Охотно верю, что специалистам ввод адреса в правильном формате &lt;a href="http://www.gnivc.ru/Document.aspx?id=1571"&gt;КЛАДР&lt;/a&gt; прост и привычен.. неспециалисту же приходится изрядно поломать голову чтобы понять, что он делает не так. Лично для меня загадка, почему разработчики программы "Документы ПУ 5" сделали настолько неудобную форму ввода адреса. Нужно там подставлять Г и УЛ - подставьте автоматом! или хотя бы подсказку дайте, в каком формате вносить информацию (кстати, подстановка сокращений для дома и квартиры в программе предусмотрены - есть соответствующая галочка). &lt;br /&gt;&lt;br /&gt;И последнее: рекомендую ставить весь подобный софт на виртуалку. И систему не замусорите и проблем с инсталляцией избежите (например, одна из проверочных программ требует установки Firebird. Лично у меня уже установлен Firebird, причем тот что нужен мне, а не тот, что нужен ей.. Естественно, настраивать запуск второго FB в параллель программа не умеет).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update.&lt;/b&gt; Сходил в пенсионный фонд. Оказывается, сдавать надо два документа: РСВ-2 и индивидуальные (информационные?) сведения. Индивидуальные сведения делаются в той же программе "Документы ПУ 5" и включат три страницы: титульный лист, форму С3В-6-1 и форму АДВ-6-2. Мне этот документ сделали за 20 секунд и сказали - "в этом году делаем за вас, в следующем отчетность без него не примем".&lt;br /&gt;&lt;br /&gt;И еще. В нашем отделении пенсионного фонда РСВ-2 и информационные сведения сдаются в &lt;b&gt;разные&lt;/b&gt; кабинеты, так что надо стоять &lt;b&gt;две очереди&lt;/b&gt;. Дурдом.&lt;br /&gt;&lt;br /&gt;Мне интересно, зачем ввели столько отчетности? Чем не устраивала простая сверка, как раньше? Несколько лет назад я просто звонил в пенсионный фонд и спрашивал - все платежи от меня получили? они говорили - все. И идти никуда было не нужно. Теперь я должен подготовить 2 документа на 5 листов, прийти лично и отстоять 2 очереди. Потратить в итоге на все часов 5-6, если не больше. Зачем?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 2&lt;/b&gt;: а парево то с этими С3В нехилое, вон как народ бурно обсуждает на &lt;a href="http://blogs.klerk.ru/showthread.php?t=381850"&gt;klerk.ru&lt;/a&gt;. Кстати там ссылочка на &lt;a href="http://www.pfrf.ru/ot_orenb_soft/6075.html"&gt;другой софт&lt;/a&gt; проскакивает, возможно он лучше.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update 3&lt;/b&gt;: мой товарищ, у которого ИП зарегистрировано в другом районе города, наученный моим опытом &lt;a href="http://kandakov.blogspot.com/2011/02/6.html"&gt;тщательно подготовил&lt;/a&gt; всю необходимую отчетность и пошел в свое отделение пенсионного фонда. И сходу им пожаловался, как сложно разбираться с их кривым софтом. Ему сказали "а зачем вы разбирались? нам от вас ничего не нужно". Отчетность, которую он с таким трудом готовил, не взяли, а сверку провели так как раньше - по паспорту :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-6692430175217747186?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/6692430175217747186/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/02/2010.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6692430175217747186'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6692430175217747186'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/02/2010.html' title='Отчетность ИП в ПФРФ за 2010.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-4490538001891417417</id><published>2011-02-20T13:51:00.001+07:00</published><updated>2011-02-20T14:46:00.897+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Как разделить Android приложение на Pro и Free версии.</title><content type='html'>Если вы разрабатываете бесплатное приложение под Android, то это совсем не значит, что оно не принесет вам доход. Путей монетаризации достаточно: показ рекламы, пожертвования пользователей, продажа специальной "Pro" версии приложения, в которой нет рекламы и/или есть дополнительный функционал.&lt;br /&gt;&lt;br /&gt;Типичный сценарий таков. Вы разрабатываете Free версию приложения, в которой реклама есть. И предлагаете приобрести Pro версию, в которой рекламы нет. Бесплатность приложения привлекает большое количество пользователей. Больше пользователей - больше кликов по рекламе, больше покупок Pro версии.&lt;br /&gt;&lt;br /&gt;Возникает чисто технический вопрос: как сделать две версии приложения - Pro и Free, - на базе одних и тех же исходных кодов? Вот об этом и поговорим.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;h2&gt;Традиционные способы разделения Pro и Free версий&lt;/h2&gt;Вопрос разделения Pro и Free версий Android-приложений обсуждается на многих форумах (вот, например, ветка на &lt;a href="http://stackoverflow.com/questions/2529062/maintaining-both-free-and-pro-versions-of-an-application"&gt;stackoverflow&lt;/a&gt;). Как правило, предлагаются следующие варианты:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Вводить runtime или compile time переключатели Pro/Free функциональности.&lt;/li&gt;&lt;li&gt;Использовать git. Вести в основной ветке разработку Pro-версии, и в отдельной ветке держать измененную Free-версию. При сборке очередного билда делать для Free-версии команду rebase, накатывая Free-изменения на основную Pro-версию.&lt;/li&gt;&lt;li&gt;Создавать два отдельных проекта, использующих общую &lt;a href="http://developer.android.com/guide/developing/eclipse-adt.html#libraryProject"&gt;Android library.&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Вместо Pro версии разрабатывать и продавать "unlocker" приложение. Если "unlocker" приложение на устройстве установлено, то ваше приложение работает как "Pro". Если не установлено - то как "Free". (Правда, такой подход подразумевает скорее разработку двух разных приложений, чем две версии одного и того же приложения, т.е. это несколько другое.)&lt;/li&gt;&lt;/ul&gt;Ни один из перечисленных подходов мне не подошел: все либо не удобны, либо не надежны, либо возможностей не хватает. Пришлось искать другой вариант.&lt;h2&gt;Основные отличия Pro и Free версий&lt;/h2&gt;В чем отличия Pro и Free версий  на уровне исходных кодов?&lt;ul&gt;&lt;li&gt;Приложения называются по разному. Например, &lt;code&gt;MyApplication&lt;/code&gt; и &lt;code&gt;MyApplication Pro&lt;/code&gt;&lt;/li&gt;&lt;li&gt;У приложений разные иконки: &lt;code&gt;icon.png&lt;/code&gt;, &lt;code&gt;icon_pro.png&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Во Free приложении может присутствовать кнопка "Donate" ("Bue Pro"). Два основных места, где она присутствует: главный экран приложения и option menu. На уровне исходников это означает, что в основной Activity приложения должен присутствовать код для отображения и обработки нажатия такой кнопки, а меню должны задаваться в ресурсах разными xml-файлами.&lt;/li&gt;&lt;li&gt;Во Free приложении может присутстовать код для отображения рекламного баннера.&lt;/li&gt;&lt;li&gt;Pro версия может содержать функции, которых во Free версии нет. Т.е. в Pro-проект должны входить дополнительные классы и файлы ресурсов.&lt;/li&gt;&lt;li&gt;Pro и Free приложения должны иметь разные названия пакетов, например: &lt;code&gt;com.dummy.myapp&lt;/code&gt; и &lt;code&gt;com.dummy.myapp.pro&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;В целом, отличия сводятся к следующим:&lt;ul&gt;&lt;li&gt;Отличная структура директорий (из-за отличий в названии пакета).&lt;/li&gt;&lt;li&gt;Отличия в значениях некоторых атрибутов в манифесте и других xml файлах (названия иконок, название программы и т.д.)&lt;/li&gt;&lt;li&gt;Разный набор файлов (некоторые файлы должны быть только во Free версии, другие только в Pro).&lt;/li&gt;&lt;li&gt;Небольшие отличия в коде (например, в Pro версии должен использоваться один идентификатор меню, во Free - другой; в Pro версии надо вызвать одну функцию, во Free - другую).&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Утилита apfsplitter&lt;/h2&gt;Подход, к которому я в итоге пришел, достаточно прост: разрабатывать единую базовую версию, и генерировать на ее основе Pro и Free версии с помощью специальной (самописной) утилиты AndroidProFreeSplitter (сокращенно, apfsplitter). &lt;br /&gt;&lt;br /&gt;Имеется единственный базовый проект. На нем можно разрабатывать и отлаживать как Pro, так и Free версию, используя булевую переменную IS_PRO как переключатель. Когда же нужно собрать релиз, AndroidProFreeSplitter генерирует на основе базового два новых проекта: Pro и Free. Генерация сводится к копированию дерева проекта, исключению ненужных файлов и изменению некоторых файлов проекта. Порядок копирования, исключения и изменения файлов определяется набором правил. Правила задаются в конфигурационном файле и представляют, по большому счету, список регулярных выражений + файловых масок.&lt;br /&gt;&lt;br /&gt;Этот подход напоминает git-вариант. Только изменения "накатываются" на базовую версию не с помощью git, а с помощью специальной утилиты. &lt;br /&gt;&lt;h2&gt;Тестовый проект DemoProFreeSplitter&lt;/h2&gt;Утилита apfsplitter является достаточно общей и не накладывает никаких ограничений на структуру проекта. Но на практике такие ограничения накладывать приходится - иначе как описать набор правил для преобразования файлов?  &lt;br /&gt;&lt;br /&gt;В реальном проекте, я ввел ряд соглашений об именовании Pro-Free зависимых файлов и функций и прописал соответствующий набор правил в конфигурационном файле. Вот эти соглашения.&lt;br /&gt;&lt;h3&gt;Версии&lt;/h3&gt;У нас три версии - базовая, pro и free. Базовая версия может работать как pro и как free в зависимости от значения переменной Version.IS_PRO. При генерации Pro/Free проектов apfsplitter автоматические выставляет корректное значение IS_PRO.&lt;br /&gt;&lt;h3&gt;Именование файлов&lt;/h3&gt;Файлы, которые должны быть включены только во free-версию, заканчиваются на "_free". Файлы, которые должны быть включены только в pro-версию, заканчиваются на "_pro". Пример: файл "icon_pro.png" будет скопирован только в pro-версию.&lt;br /&gt;&lt;h3&gt;Макросы, аналоги #ifdef/#endif&lt;/h3&gt;Да, макросы это плохо. И использовать их нужно крайне осторожно. Тем не менее, макросы могут пригодиться. Например, чтобы удалять Pro-код из Free-версии и Free-код из Pro-версии. &lt;br /&gt;&lt;br /&gt;Макросы введены в следующем виде:&lt;pre class="brush:java"&gt;//#startBASE &lt;br /&gt;  //здесь код который будет выполняться только в базовой версии&lt;br /&gt;  //в Pro и Free он будет закомментирован&lt;br /&gt;//#endBASE&lt;br /&gt;&lt;br /&gt;//#startPRO&lt;br /&gt;  //здесь код который будет выполняться только в pro-версии&lt;br /&gt;  //в Free он будет закомментирован&lt;br /&gt;//#endPRO&lt;br /&gt;&lt;br /&gt;//#startPRO&lt;br /&gt;  //здесь код который будет выполняться только во free-версии&lt;br /&gt;  //в Pro он будет закомментирован&lt;br /&gt;//#endPRO&lt;br /&gt;&lt;/pre&gt;Обращаю внимание, что ненужный код не удаляется, а комментируется. Хотя в принципе, в конфигурационном файле apfsplitter можно изменить соответствующие регулярные выражения и код будет удаляться.&lt;br /&gt;&lt;h3&gt;Именование функций&lt;/h3&gt;Если у нас есть Pro/Free-зависимый код, то он оформляется в виде трех функций:&lt;br /&gt;&lt;pre class="brush:java"&gt;//#startBASE &lt;br /&gt;public void prepareDonateButton_ProFree() {&lt;br /&gt;//эта функция работает только в базовой версии&lt;br /&gt;//в Free/Pro проектах она закомментирована.&lt;br /&gt; if (Version.IS_PRO) {&lt;br /&gt;  prepareDonateButton_Pro();&lt;br /&gt; } else {&lt;br /&gt;  prepareDonateButton_Free();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;//#endBASE&lt;br /&gt;&lt;br /&gt;//#startFREE &lt;br /&gt;public void prepareDonateButton_Free() {&lt;br /&gt;   //показываем кнопку Donate во Free версии&lt;br /&gt;}&lt;br /&gt;//#endFREE&lt;br /&gt;&lt;br /&gt;//#startPRO &lt;br /&gt;public void prepareDonateButton_Pro() {&lt;br /&gt;  //не показываем кнопку Donate в Pro версии&lt;br /&gt;}&lt;br /&gt;//#endPRO &lt;br /&gt;&lt;/pre&gt;В базовой версии будут присутствовать все три функции. В Pro версии все вызовы prepareDonateButton_ProFree будут заменены на вызовы prepareDonateButton_Pro, а функции prepareDonateButton_ProFree и prepareDonateButton_Free закомментированы (с помощью макросов). Аналогично, во Free версии все вызовы prepareDonateButton_ProFree будут заменены на вызовы prepareDonateButton_Free, а функции prepareDonateButton_ProFree и prepareDonateButton_Pro закомментированы.&lt;br /&gt;&lt;h3&gt;Класс Version&lt;/h3&gt;В проекте имеется вот такой класс Version:&lt;br /&gt;&lt;pre class="brush:java"&gt;public class Version {&lt;br /&gt; public static final boolean IS_PRO = false;&lt;br /&gt; public static int GetFreeProId(int idFree, int idPro) {&lt;br /&gt;   return IS_PRO ? idPro : idFree; &lt;br /&gt; }&lt;br /&gt; public static int GetFreeId(int idFree) {&lt;br /&gt;   return IS_PRO ? 0 : idFree; &lt;br /&gt; }&lt;br /&gt; public static int GetProId(int idPro) {&lt;br /&gt;   return IS_PRO ? idPro : 0; &lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;Когда мы работаем в базовой версии, то переменная IS_PRO определяет, какая из версий Pro/Free компилируется. При создании Pro/Free версий apfsplitter принудительно устанавливает значение константы IS_PRO в true или false.&lt;br /&gt;&lt;br /&gt;Функции GetFreeProId, GetFreeId и GetProId нужны, чтобы корректно обращаться к ресурсам. Предположим, у нас есть два варианта menu: R.menu.menu_free и R.menu.menu_pro. Чтобы вызвать меню мы пишем в базовой версии такой код:&lt;br /&gt;&lt;pre class="brush:java"&gt;inflater.inflate(       &lt;br /&gt;    Version.GetProFreeId(R.menu.menu_free, R.menu.menu_pro) &lt;br /&gt;  , menu);&lt;br /&gt;&lt;/pre&gt;В Pro версии вызов GetProFreeId будет заменен на&lt;br /&gt;&lt;code&gt;inflater.inflate(R.menu.menu_pro, menu)&lt;/code&gt;. В Free версии на &lt;code&gt;inflater.inflate(R.menu.menu_free, menu)&lt;/code&gt;. Такой финт нужен потому, что в Pro версии идентификатора R.menu.menu_free может не существовать, а во Free версии может не существовать R.menu.menu_pro (напомню, что файлы оканчивающиеся на _free не попадают в Pro версию, так что menu_free.xml не попадет в Pro версию и идентификатора R.menu.menu_free не будет; аналогичная ситуация с menu_pro).&lt;br /&gt;&lt;br /&gt;Функции GetFreeId и GetProId работают аналогично. В той версии, где нужного идентификатора нет, вместо него будет стоять ноль.&lt;br /&gt;&lt;h2&gt;Тестовый проект&lt;/h2&gt;Тестовый проект &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoProFreeSplitter"&gt;DemoProFreeSplitter&lt;/a&gt; демонстрирует использование указанных соглашений именования и утилиты apfsplitter. &lt;br /&gt;&lt;br /&gt;Командный &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoProFreeSplitter/generate.bat"&gt;файл&lt;/a&gt; для генерации Pro и Free версий тестового проекта выглядит так:&lt;br /&gt;&lt;pre&gt;apfsplitter.exe "sources" "versions\pro" "apfs_config.xml" pro&lt;br /&gt;apfsplitter.exe "sources" "versions\free" "apfs_config.xml " !pro&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Директория "sources" содержит базовую версию проекта. Результаты записываются в "versions\pro" и "versions\free". Конфигурационный файл с правилами разбиения называется apfs_config.xml. Последним параметром передается тег версии - "pro" или, для free версии,"!pro".&lt;h2&gt;Выводы&lt;/h2&gt;Итак, с помощью утилиты apfsplitter можно без особых хлопот генерировать Pro и Free версии Android приложения на основе единого набора исходных кодов - "базовой" версии. Сама базовая версия может работать как Pro и Free, так что ее можно использовать при разработке, отладке и тестировании как Pro так и Free версии. &lt;br /&gt;&lt;br /&gt;Тестовый проект &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/DemoProFreeSplitter"&gt;DemoProFreeSplitter&lt;/a&gt; демонстрирует возможности apfsplitter. В проект входит конфигурационный файл apfsplitter. Чтобы можно было использовать этот файл в реальном проекте, необходимо придерживаться в этом проекте ряда простых соглашений в именовании Pro/Free зависимых файлов и функций. Кроме того, необходимо изменить последнее правило:&lt;br /&gt;&lt;pre class="brush:java"&gt;&amp;lt;replace if="target:pro"&amp;gt;   &lt;br /&gt; &amp;lt;files&amp;gt;.project&amp;lt;/files&amp;gt;&lt;br /&gt; &amp;lt;search&amp;gt;projectDescription/name&amp;lt;/search&amp;gt;&lt;br /&gt; &amp;lt;replace&amp;gt;My Application Pro Title&amp;lt;/replace&amp;gt;&lt;br /&gt;&amp;lt;kind&amp;gt;xml&amp;lt;/kind&amp;gt;&lt;br /&gt;&amp;lt;/replace&amp;gt;&lt;br /&gt;&lt;/pre&gt;Данное правило заменяет в Pro-версии название проекта. Вам необходимо либо заменить "My Application Pro Title" на что-нибудь другое, либо вообще удалить это правило из конфигурационного файла.&lt;br /&gt;&lt;br /&gt;Утилита apfsplitter достаточно универсальна и не привязана к каким-либо соглашениям. Соглашения, описанные выше, оказались удобными для меня, но это лишь один из возможных вариантов. Естественно, их можно доработать, изменить или полностью заменить на другие. Для этого потребуется отредактировать конфигурационный файл apfsplitter. Описание его структуры - тема отдельного разговора, к которому я вернусь в дальнейшем.&lt;h2&gt;Ссылки и исходники&lt;/h2&gt;&lt;a href="http://code.google.com/p/dvsrc/downloads/detail?name=20110220apfsplitter.7z"&gt;Скачать утилиту apfsplitter и тестовый проект DemoProFreeSplitter в виде 7z-архива&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/dvsrc/source/browse/#svn%2Ftrunk%2FAndroid%2FDemoProFreeSplitter"&gt;Просмотреть исходные коды тестового проекта DemoProFreeSplitter&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/dvsrc/source/browse/#svn%2Ftrunk%2FCSharp%2FAndroidProFreeSplitter"&gt;Просмотреть исходные коды apfsplitter&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-4490538001891417417?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/4490538001891417417/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/02/android-pro-free.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/4490538001891417417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/4490538001891417417'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/02/android-pro-free.html' title='Как разделить Android приложение на Pro и Free версии.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-6656194497656717091</id><published>2011-01-22T20:31:00.001+07:00</published><updated>2011-01-22T23:42:54.817+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Named Folders'/><category scheme='http://www.blogger.com/atom/ns#' term='release'/><title type='text'>Named Folders 3.0. Релиз, итоги.</title><content type='html'>Выложил релиз Named Folders 3.0 - плагина для Far Manager, позволяющего быстро перемещаться между директориями в Far (&lt;a href="http://code.google.com/p/namedfolders/downloads/detail?name=NamedFolders%20x86_b254_release.7z"&gt;версия x86&lt;/a&gt;, &lt;a href="http://code.google.com/p/namedfolders/downloads/detail?name=NamedFolders%20x64_b254_release.7z"&gt;версия x64&lt;/a&gt;). С момента &lt;a href="http://derevyanko.blogspot.com/2010/09/named-folders-30-alpha-googlecode.html"&gt;начала&lt;/a&gt; работы над третьей версией плагина прошло уже четыре месяца.. и 14 билдов. &lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Детально об изменениях написано в &lt;a href="http://code.google.com/p/namedfolders/wiki/NfWhatsnewRus"&gt;whatsnew&lt;/a&gt;. Основные изменения следующие:&lt;ul&gt;&lt;li&gt;Плагин стал юникодным, работает теперь только в Far 2 и &lt;a href="http://derevyanko.blogspot.com/2010/10/far-2x.html"&gt;использует FAR API 2.0&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;У плагина появилась 64-битная версия.&lt;/li&gt;&lt;li&gt;Код плагина приведен более менее в нормальное состояние, пригодное для дальнейшей работы (ох, не весь еще приведен..)&lt;/li&gt;&lt;li&gt;&lt;a href="http://derevyanko.blogspot.com/2010/11/vc.html"&gt;Оптимизированы&lt;/a&gt; размеры итоговых dll.&lt;/li&gt;&lt;li&gt;Исправлено множество багов и добавлен ряд полезных мелочей в функциональность плагина.&lt;/li&gt;&lt;li&gt;Полностью переделана технология разбора относительных путей и изменен синтаксис команд для ограничения глубины поиска.&lt;/li&gt;&lt;/ul&gt;О последнем пункте хочу сказать чуть подробнее. Ведь именно из-за него релиз плагина был отложен более чем на полтора месяца.&lt;br /&gt;&lt;br /&gt;Named Folders 2.5 позволял не только переходить в директорию, на которую непосредственно указывал псевдоним, но и указывать дальнейший путь. Например:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;команда &lt;code&gt;cd:pf&lt;/code&gt; переходит в &lt;code&gt;c:\program files&lt;/code&gt;;&lt;/li&gt;&lt;li&gt;команда &lt;code&gt;cD:pf\*&lt;/code&gt; находит все поддиректории &lt;code&gt;c:\program files&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;cD:pf\\*&lt;/code&gt; находит как директории, непосредственно вложенные в &lt;code&gt;c:\program files&lt;/code&gt;, так и все их поддиректории&lt;/li&gt;&lt;li&gt;&lt;code&gt;cD:pf\\\a&lt;/code&gt; позволяет искать вглубь на три уровня&lt;/li&gt;&lt;/ul&gt;Другими словами, слеши позволяли ограничивать глубину поиска. Поставил 5 слешей - ищешь не более чем на 5 уровней вглубь. 10 слешей - на 10 уровней. И т.д.&lt;br /&gt;&lt;br /&gt;Для чего это нужно? Для скорости поиска. Чтобы перебрать все файлы в &lt;code&gt;c:\program files&lt;/code&gt;, может потребоваться несколько минут. Если же вы знаете, что файл лежит не глубоко, в двух-трех уровнях от поверхности, то почему бы не использовать эту информацию для ускорения поиска? Это как раз в духе плагина - использовать для поиска любую информацию, которая есть у пользователя.&lt;br /&gt;&lt;br /&gt;К сожалению, в NF2.5 синтаксис команд поиска с ограничением глубины был не слишком интуитивен. И, что печально, реализация хромала на обе ноги. Работали лишь самые простейшие команды, а чуть более сложные уже работали не так как ожидается или вообще не работали.&lt;br /&gt;&lt;br /&gt;После &lt;a href="http://code.google.com/p/namedfolders/issues/detail?id=7"&gt;длительных дискуссий&lt;/a&gt;, было решено изменить синтаксис метасимволов, используемых для ограничения глубины поиска. Вслед за изменением синтаксиса последовала полная смена реализации, после чего поиск стал работать предсказуемо и появилась возможность писать достаточно сложные выражения поиска.&lt;br /&gt;&lt;br /&gt;Детали приведены в &lt;a href="http://code.google.com/p/namedfolders/wiki/WIkiRelatedPathsRu"&gt;вики&lt;/a&gt;. Здесь же я приведу просто несколько примеров, как теперь можно использовать поиск с ограничением уровня вложенности. &lt;br /&gt;&lt;ul&gt;&lt;li&gt;cd:sh\** - неограниченный поиск всех директорий вглубь.&lt;/li&gt;&lt;li&gt;cd:sh\**:2 - поиск вглубь на два уровня вниз.&lt;/li&gt;&lt;li&gt;cd:sh\*\* - поиск вглубь на два уровня вниз. Эта команда похожа на cd:sh\**:2, но есть важное отличие. Команда cd:sh\*\* включит в результаты только директории второго уровня, а команда cd:sh\:2 - как второго, так и первого уровня.&lt;/li&gt;&lt;li&gt;cd:sh\*\*\a - найти директории, расположенные на третьем уровне вложенности, удовлетворяющие маске "a".&lt;/li&gt;&lt;li&gt;cd:sh\**:3a - найти все директории, расположенные на первом, втором и третьем уровнях вложенности, и удовлетворяющие маске "a".&lt;/li&gt;&lt;li&gt;cd:sh\**:3\a - найти все директории, расположенные на первом, втором и третьем уровнях вложенности, а затем в каждой из них провести поиск директории, удовлетворяющей маске "a".&lt;/li&gt;&lt;li&gt;cd:sh\.. - перейти в родительскую директорию.&lt;/li&gt;&lt;li&gt;cd:sh\..\* - найти все директории, вложенные в родительскую директорию.&lt;/li&gt;&lt;li&gt;cd:sh\..*:2 - вернуть две директорию: родительскую и ту, в которую родительская вложена.&lt;/li&gt;&lt;li&gt;cd:sh\..* - вернуть все родительские директории. Если наша директория находится на N-ном уровне в файловой системе, то команда вернет N - 1 директорию.&lt;/li&gt;&lt;li&gt;cd:sh\..*\**:2a" - найти все родительские директории и в каждой из них провести поиск директорий, удовлетворяющих маске "a", причем поиск ограничить двумя уровнями.&lt;/li&gt;&lt;/ul&gt;В NF3 функциональность для ограничения глубины поиска можно использовать не только в командах перехода по псевдониму &lt;code&gt;cd:sh&lt;/code&gt;, но и еще в паре мест:&lt;ul&gt;&lt;li&gt;В командах прямого перехода в директорию: &lt;code&gt;cd:path&lt;/code&gt;&lt;/li&gt;&lt;li&gt;В путях, на которые ссылаются псевдонимы. Т.е. для псевдонима pf можно указать не просто директорию &lt;code&gt;c:\program files&lt;/code&gt;, но, например, &lt;code&gt;c:\program files\**:3&lt;/code&gt;. В результате, команда &lt;code&gt;cd:pf&lt;/code&gt; будет всегда показывать полный список всех директорий, вложенных в &lt;code&gt;c:\program files&lt;/code&gt; на 1-3 уровня&lt;/li&gt;&lt;/ul&gt;Из других изменений в функциональности в NF 3.0 отмечу:&lt;ul&gt;&lt;li&gt;Реализована возможность запуска  64-битных приложений из 32-битного FAR&lt;/li&gt;&lt;li&gt;Команда прямого перехода в директорию &lt;code&gt;cd:path&lt;/code&gt; позволяет указывать path в кавычках; кроме того path может быть сетевой директорией.&lt;/li&gt;&lt;li&gt;Реализованы команды неявного создания шоткатов, ссылающихся на две панели сразу.&lt;/li&gt;&lt;li&gt;Макрос FastPrefix стал гораздо более удобным: нажатие ":" не только приводит к вводу "cd:" в командной строке, но и меняет раскладку клавиатуры на английскую. Нажал ":" и печатай команду NF.&lt;/li&gt;&lt;/ul&gt;В следующей версии плагина (3.1) планируется следующее:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Поддержка win7-libraries (&lt;a href="http://code.google.com/p/namedfolders/issues/detail?id=10"&gt;#10&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Поддержка атрибутов для каталогов и шоткатов (&lt;a href="http://code.google.com/p/namedfolders/issues/detail?id=8"&gt;#8&lt;/a&gt;). Например, для каталогов нужен атрибут "показывать в меню дисков" и "синхронизировать с библиотекой win7"&lt;/li&gt;&lt;li&gt;"НЕ" в фильтре и в масках пути (&lt;a href="http://code.google.com/p/namedfolders/issues/detail?id=46"&gt;#46&lt;/a&gt;, &lt;a href="http://code.google.com/p/namedfolders/issues/detail?id=47"&gt;#47&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;Интеграция с doskey (&lt;a href="http://code.google.com/p/namedfolders/issues/detail?id=36"&gt;#36&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;Визуализация длительных операций (&lt;a href="http://code.google.com/p/namedfolders/issues/detail?id=44"&gt;#44&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;Новую версию постараюсь выложить на PlugRing. Обо всех замеченных багах можно писать сюда, на email или в &lt;a href="http://code.google.com/p/namedfolders/issues/list"&gt;issue tracker&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-6656194497656717091?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/6656194497656717091/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/01/named-folders-30.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6656194497656717091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6656194497656717091'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/01/named-folders-30.html' title='Named Folders 3.0. Релиз, итоги.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-7236586150503416648</id><published>2011-01-21T19:23:00.002+07:00</published><updated>2011-01-21T19:27:36.531+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><title type='text'>Конвертер PDF в HTML. Вариантов море, а нужного нет.</title><content type='html'>Появилась задача: конвертация PDF-файлов в HTML в автоматическом режиме на сервере. Условия просты: текст должен конвертироваться в текст (не в картинку), форматирование страницы должно быть максимально приближено к оригиналу, плюс все это должно делаться локально - документ должен оставаться на сервере и никуда не передаваться. Оказалось, что найти подходящий конвертер очень и очень не просто.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Сразу отпали:&lt;ul&gt;&lt;li&gt;Утилиты типа &lt;a href="http://pdfbox.apache.org/commandlineutilities/ExtractText.html"&gt;Apache PDFBox&lt;/a&gt; и &lt;a href="http://www.amyuni.com/en/developer/ocrmodule/features"&gt;Amyuni OCR Module&lt;/a&gt;. Все они позволяют извлечь текст, но не сохраняют форматирование&lt;/li&gt;&lt;li&gt;Утилиты типа &lt;a href="http://answers.google.com/answers/threadview?id=134543"&gt;ImageMagick&lt;/a&gt;, способные конвертировать PDF в набор JPEG-файлов. Нам нужен текст.&lt;/li&gt;&lt;li&gt;Сервисы для конвертации PDF-документов, типа &lt;a href="http://access.adobe.com/access/#form"&gt;сервиса Adobe&lt;/a&gt;, а так же сервисы включения PDF-документов в веб-страниц типа, &lt;a href="http://www.scribd.com"&gt;scribd&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Adobe Acrobat. Конвертировать PDF в HTML он умеет, а вот программно использовать его не получается.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.gnostice.com/XtremePDFConverter_VCL.asp"&gt;XtremePDFConverter на VCL&lt;/a&gt;, способный конвертировать PDF в RTF. Мы его пробовали три года назад. Форматирование при конвертировании сохраняет не плохо, но имелись серьезные проблемы с кодировкой. Например, датские символы не конвертировались. Скачал с сайта текущую демоверсию (3.0.1), проверил - проблема с датскими символами осталась.&lt;/li&gt;&lt;/ul&gt;Поискал в google &lt;a href="http://www.google.com/search?client=opera&amp;rls=en-GB&amp;q=%22pdf+to+html%22&amp;sourceid=opera&amp;ie=utf-8&amp;oe=utf-8"&gt;"PDF to HTML"&lt;/a&gt;. Вывалился гигантский список недорогих утилит типа &lt;a href="http://www.pdfpdf.com/pdfconverter.html"&gt;PDF Ripper&lt;/a&gt; и &lt;a href="http://www.intrapdf.com/convert_pdf_to_html.htm"&gt;IntraPDF&lt;/a&gt;. Попробовал несколько штук - у всех качество очень посредственное. Форматирование портится просто чудовищно, некоторые PDF документы вообще не воспринимаются...&lt;br /&gt;&lt;br /&gt;Более глубокий поиск дал следующие варианты:&lt;ul&gt;&lt;li&gt;&lt;a href="http://pdftohtml.sourceforge.net/"&gt;pdftohtml&lt;/a&gt; - бесплатный конвертер, основанный на старой версии xpdf 2.02. Последняя версия pdftohtml 0.40 датирована 2006 годом. Во многих случаях, эта утилита работает отлично. Но, к сожалению, некоторые PDF-документы ей не по зубам - валится с ошибкой. Движок &lt;a href="http://www.foolabs.com/xpdf/index.html"&gt;xpdf&lt;/a&gt; уже давно обновился, текущая версия - 3.02pl5 (октябрь 2010 года). Вот только обновлений pdftohtml под нее нет.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.pdfonline.com/easyconverter/sdk/pdf-to-html/conversion_samples.htm#usermanual"&gt;BCL easy converter&lt;/a&gt;. Качество преобразования отличное, документы, на которых pdftohtml вылетает, конвертит на ура. Но цена крайне высока: ~$4000 за серверную лицензию и более $1000 за лицензию разработчика.&lt;/li&gt;&lt;li&gt;Компоненты &lt;a href="http://www.lowagie.com/iText/"&gt;iText&lt;/a&gt; и &lt;a href="http://itextsharp.sourceforge.net/"&gt;iTextSharp&lt;/a&gt;. Они позволяют распарсить PDF документ, и построить в памяти объектную модель документа. Далее, эту модель &lt;a href="http://stackoverflow.com/questions/2794269/transform-pdf-to-html-keep-layout"&gt;можно попробовать использовать&lt;/a&gt; для создания HTML документа. Правда, готового решения мне не попалось.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.crawfordtech.com/products/transforms"&gt;CrawfordTech's Transforms&lt;/a&gt;. Не ясно, что за зверь. Судя по описанию - какой-то универсальный конвертер, позволяющий конвертировать друг в друга форматы Xerox, Postscript, PDF, AFP, PCL и т.д. Надо изучать. Запросил у них демоверсию.&lt;/li&gt;&lt;li&gt;&lt;a href="http://pdftransformer.abbyy.com/"&gt;ABBYY PDF Transformer 3.0&lt;/a&gt;. Конечно, с этим продуктом та же проблема, что с Adobe Acrobat - он не предназначен для использования программным образом. Но у ABBI есть &lt;a href="http://www.abbyy.com/ocr_sdk/"&gt;SDK&lt;/a&gt;, которое можно попробовать использовать для наших целей. Написал в ABBYY письмо, посмотрим что ответят.&lt;/li&gt;&lt;/ul&gt;Как видите, 100%-подходящего варианта нет. Остается искать дальше :(&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-7236586150503416648?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/7236586150503416648/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/01/pdf-html.html#comment-form' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/7236586150503416648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/7236586150503416648'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/01/pdf-html.html' title='Конвертер PDF в HTML. Вариантов море, а нужного нет.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-4434268229201896051</id><published>2011-01-14T16:46:00.002+07:00</published><updated>2011-02-20T21:29:10.584+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Даблклик по виджету на Android</title><content type='html'>Как известно, даблклика (даблтапа - double tap) на андроиде нет. Но порой требуется, чтобы одинарный и двойной щелчок по виджету приводили к разным действиям. Приходится реализовывать даблклик обходным путем. Например, так, как описано ниже.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Идея обходного пути проста. Заводим статический синглетон. В синглетоне регистрируем клики по виджиту. При первом клике запускается таймер. Если таймер срабатывает до того, как произойдет второй клик, мы запускаем функцию обработки одинарного клика. Если же раньше произойдет второй клик, запускаем функцию обработки double click, а последующее событие таймера игнорируем.&lt;br /&gt;&lt;br /&gt;Layout для виджета выглядит так:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"&lt;br /&gt; android:gravity="center" &lt;br /&gt; android:orientation="vertical" &lt;br /&gt; android:id="@+id/widget_layout"&lt;br /&gt; android:layout_width="72dip"&lt;br /&gt;   android:layout_height="72dip"&lt;br /&gt; android:clickable="true"&amp;gt;&lt;br /&gt; &amp;lt;ImageView android:id="@+id/widget_image_view"&lt;br /&gt;     android:scaleType="fitXY"&lt;br /&gt;     android:layout_width="fill_parent" &lt;br /&gt;     android:layout_height="fill_parent" &lt;br /&gt;     android:layout_marginLeft="0.0dip"&lt;br /&gt;     android:src="@drawable/link_pressed"&lt;br /&gt;  /&amp;gt;&lt;br /&gt;&amp;lt;/RelativeLayout&amp;gt;&lt;br /&gt;&lt;/pre&gt;Т.е. виджет содержит компонент &lt;code&gt;ImageView&lt;/code&gt;, который умеет обрабатывать клики. Вот как выглядит класс виджета:&lt;pre class="brush:java"&gt;public class MyProvider extends AppWidgetProvider {&lt;br /&gt;&lt;br /&gt;private static final String ACTION_WIDGET_CONTROL = "my.domain.WIDGET_CONTROL";&lt;br /&gt;public static final String ACTION_UPDATE_WIDGET_CONTEXT = "my.domain.UPDATE_WIDGET_CONTEXT";&lt;br /&gt;public static final String URI_SCHEME = "my.domain";      &lt;br /&gt;&lt;br /&gt;public static final String PARAM_WIDGET_ID = "my.domain.appWidgetId";&lt;br /&gt;public static final String PARAM_WIDGET_POSITION = "my.domain.widgetPos";&lt;br /&gt;&lt;br /&gt;@Override&lt;br /&gt;public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {&lt;br /&gt;    for (int widget_id : appWidgetIds) {&lt;br /&gt;      update_widget_view(context, widget_id, appWidgetManager);                  &lt;br /&gt;    }                  &lt;br /&gt;    super.onUpdate(context, appWidgetManager, appWidgetIds);        &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void set_click_listener_to_widget(Context context, int widgetId, RemoteViews remoteViews) {&lt;br /&gt;//обработчик одинарного клика по виджету&lt;br /&gt; Intent active = new Intent(context, AWidgetProvider.class);&lt;br /&gt; //при нажатии на ImageButton на виджите&lt;br /&gt; //будет посылаться ACTION_WIDGET_CONTROL&lt;br /&gt; //мы перехватим его в OnReceive&lt;br /&gt; active.setAction(ACTION_WIDGET_CONTROL); &lt;br /&gt; Uri data = Uri.parse(URI_SCHEME + "://widget#");&lt;br /&gt; data = data.buildUpon()&lt;br /&gt;  .appendQueryParameter("widget_id", String.valueOf(widgetId))&lt;br /&gt;  .build();&lt;br /&gt; active.setData(data);&lt;br /&gt; PendingIntent pi = PendingIntent.getBroadcast(context, 0, active, 0);&lt;br /&gt; remoteViews.setOnClickPendingIntent(R.id.widget_image_view, pi); &lt;br /&gt;}                 &lt;br /&gt;    &lt;br /&gt;@Override&lt;br /&gt;public void onReceive(Context context, Intent intent) {&lt;br /&gt; final String action = intent.getAction();&lt;br /&gt; ....&lt;br /&gt; if (action.equals(ACTION_WIDGET_CONTROL)) {&lt;br /&gt;//обрабатываем клик по виджету&lt;br /&gt;    Uri uri = intent.getData();&lt;br /&gt;    //определяем позицию виджета на экране&lt;br /&gt;    Rect widget_pos = intent.getSourceBounds(); &lt;br /&gt;    int widget_id = Integer.parseInt(uri.getQueryParameter("widget_id"));&lt;br /&gt;    if (widget_id != AppWidgetManager.INVALID_APPWIDGET_ID) {&lt;br /&gt;      on_widget_click(context, widget_id, widget_pos);&lt;br /&gt;    }&lt;br /&gt; } &lt;br /&gt; super.onReceive(context, intent);&lt;br /&gt;}       &lt;br /&gt;   &lt;br /&gt;private boolean on_widget_click(Context context, int widgetId, Rect widgetPos) {&lt;br /&gt;//Регистрируем все клики по виджету&lt;br /&gt;  return ((Kernel)context.getApplicationContext())&lt;br /&gt;     .RegisterClick(widgetId, widgetPos);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void update_widget_view(Context context, int widgetId, AppWidgetManager appWidgetManager) {&lt;br /&gt; RemoteViews remote_views = new RemoteViews(context.getPackageName()&lt;br /&gt;     , R.layout.widget);&lt;br /&gt; set_click_listener_to_widget(context, widgetId, remote_views); &lt;br /&gt; appWidgetManager.updateAppWidget(widgetId, remote_views);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void makeDoubleClickAction(Context context&lt;br /&gt;  , int appWidgetId, Rect widgetPos) {&lt;br /&gt;//!TODO: обработка одинарного клика&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void makeSingleClickAction(Context context&lt;br /&gt;  , int appWidgetId, Rect widgetPos) {&lt;br /&gt;//!TODO: Обработка двойного клика&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Переходим к синглетону, в котором мы будет регистрировать клики. Синглетон создаем на базе класса &lt;code&gt;Application&lt;/code&gt; - во-первых, чтобы избежать &lt;a href="http://derevyanko.blogspot.com/2010/12/android_26.html"&gt;проблем с использованием синглетона в разных Activity&lt;/a&gt;, во-вторых, чтобы получить доступ к контексту, который нам нужен (скорее всего) в функциях &lt;code&gt;makeDoubleClickAction&lt;/code&gt; и &lt;code&gt;makeSingleClickAction&lt;/code&gt;. Код синглетона:&lt;br /&gt;&lt;pre class="brush:java"&gt;public final class Kernel extends Application { &lt;br /&gt;//http://stackoverflow.com/questions/708012/android-how-to-declare-global-variables&lt;br /&gt;public static final String TAG_LOG = "my.domain";&lt;br /&gt;&lt;br /&gt;public static int DOUBLE_CLICK_DELAY_MS = 200; &lt;br /&gt;&lt;br /&gt;//номер сессии - порядковый номер кликов&lt;br /&gt;//номер сессии нужен, чтобы избежать ложного срабатывания функции "один клик" по таймеру уже&lt;br /&gt;//после того, как сработала функция "дабл клик".&lt;br /&gt;private static int m_DoubleClickSession = 1;&lt;br /&gt;private static int m_DoubleClickCountClicks = 0;&lt;br /&gt;private static DoubleClickHelper m_DoubleClickHelper = null;&lt;br /&gt;&lt;br /&gt;/* Регистрирует клик. Возвращает True, если это был первый клик в последовательности double-click.&lt;br /&gt; * */&lt;br /&gt;public boolean RegisterClick(int appWidgetId, Rect widgetRect) {&lt;br /&gt; synchronized (this) {&lt;br /&gt;  if (m_DoubleClickCountClicks == 1) {&lt;br /&gt;   m_DoubleClickSession++;&lt;br /&gt;   m_DoubleClickCountClicks = 0;&lt;br /&gt;   m_DoubleClickHelper = null; &lt;br /&gt;   MyProvider.makeDoubleClickAction(this.getApplicationContext()&lt;br /&gt;      , appWidgetId, widgetRect);&lt;br /&gt;   return false;&lt;br /&gt;  } else {&lt;br /&gt;   m_DoubleClickHelper = new DoubleClickHelper(m_DoubleClickSession&lt;br /&gt;     , appWidgetId, widgetRect);&lt;br /&gt;   Timer timer = new Timer();&lt;br /&gt;   timer.schedule(m_DoubleClickHelper, Kernel.DOUBLE_CLICK_DELAY_MS);&lt;br /&gt;   m_DoubleClickCountClicks = 1;&lt;br /&gt;   return true;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void on_first_click_timer(int sessionId, int appWidgetId, Rect widgetRect) {&lt;br /&gt; synchronized (this) {&lt;br /&gt;  //если номер сессии изменился, значит пока мы ждали таймер, был второй клик&lt;br /&gt;  //и была вызвана функция дабл-клика. В этом случае, ничего не вызываем.&lt;br /&gt;  if (Kernel.m_DoubleClickSession == sessionId) { &lt;br /&gt;   m_DoubleClickSession++;&lt;br /&gt;   m_DoubleClickCountClicks = 0;&lt;br /&gt;   m_DoubleClickHelper = null;&lt;br /&gt;   MyProvider.makeSingleClickAction(this.getApplicationContext(), appWidgetId, widgetRect);&lt;br /&gt;  }&lt;br /&gt; }  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class DoubleClickHelper  extends TimerTask {&lt;br /&gt;    final int m_AppWidgetId;&lt;br /&gt;    final Rect m_WidgetRect;&lt;br /&gt;    final int m_DoubleClickSession;&lt;br /&gt;    &lt;br /&gt; public DoubleClickHelper(int doubleClickSession, int appWidgetId, Rect widgetRect) {&lt;br /&gt;  m_AppWidgetId = appWidgetId;&lt;br /&gt;  m_WidgetRect = widgetRect;&lt;br /&gt;  m_DoubleClickSession = doubleClickSession;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; @Override &lt;br /&gt; public void run() {&lt;br /&gt;  on_first_click_timer(m_DoubleClickSession, m_AppWidgetId, m_WidgetRect);&lt;br /&gt; }  &lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Значение задержки таймера &lt;code&gt;DOUBLE_CLICK_DELAY_MS&lt;/code&gt; лучше вынести в настройки, ведь скорее всего, на разных устройствах потребуется задавать разные значения задержки. &lt;br /&gt;&lt;br /&gt;Функции &lt;code&gt;makeSingleClickAction&lt;/code&gt; и &lt;code&gt;makeDoubleClickAction&lt;/code&gt; могут быть объявлены как угодно. В моем проекте требовалось передать в них в качестве параметров контекст и позицию виджета на экране. Отсюда многочисленные Rect в коде - если вам позиция виджета не важна, можете смело выкидывать все Rect-ы.&lt;br /&gt;&lt;br /&gt;Приведенный выше код вполне успешно работает в приложении &lt;a href="http://www.android-widget.com/"&gt;Animated Widget Contact Launch&lt;/a&gt;. Тем не менее, ошибки не исключены. Вполне возможно, что со временем они выплывут - тогда я о них обязательно сообщу. Если же вы найдете ошибку, буду признателен за сообщение о ней в комментариях.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;: как оказалось, приведенный выше код работает вовсе не так хорошо, как хотелось бы. Дело оказалось в 200 миллисекундной задержке, которая возникает при клике по виджету (пока ждем второй клик). Она слишком заметна и субъективно кажется что виджет тормозит. Пользователи стали жаловаться. Пришлось выкручиваться: по первому клику сразу же запускать однокликовое действие (запуск активити с анимацией) и, одновременно, продолжать отслеживать второй клик. Особенность: отслеживать второй клик приходится как на стороне виджета, так и на стороне запущенной активити, т.к. на практике второй клик может прийти и туда, и туда.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-4434268229201896051?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/4434268229201896051/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/01/android.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/4434268229201896051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/4434268229201896051'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/01/android.html' title='Даблклик по виджету на Android'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-459888531696164358</id><published>2011-01-11T16:23:00.000+07:00</published><updated>2011-01-11T16:23:22.554+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='links'/><title type='text'>Реестр intents</title><content type='html'>Наткнулся на крайне полезный ресурс&lt;br /&gt;&lt;a href="http://www.openintents.org/en/intentsbigtable"&gt;Registry of intents protocols&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;С этими интентами под Андроид ведь просто беда.. каждый раз приходится выискивать - какие параметры у какого intent есть. А тут - все по полочкам разложено. Удобно.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-459888531696164358?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/459888531696164358/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2011/01/intents.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/459888531696164358'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/459888531696164358'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2011/01/intents.html' title='Реестр intents'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-346565496461740897</id><published>2010-12-26T22:15:00.001+07:00</published><updated>2010-12-27T15:21:15.819+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Синглетоны на Android. Как не стукнуться клювом об дерево...</title><content type='html'>&lt;a href="http://ru.wikipedia.org/wiki/Одиночка_(шаблон_проектирования)"&gt;Синглетон&lt;/a&gt; - объект существующий в единственном экземпляре. Удобнейшая штука для хранения объектов, существующих в течении всего времени жизни приложения: всякого рода настроек, кешей и т.д. Сильно злоупотреблять синглетонами не стоит, т.к. у них есть &lt;a href="http://www.antonioshome.net/blog/2006/20060906-1.php"&gt;четко определенные недостатки&lt;/a&gt;. Однако в некоторых случаях без синглетонов (как и без глобальных переменных) просто не обойтись.&lt;br /&gt;&lt;br /&gt;Синглетон можно реализовать разными способами. Обычно его реализуют так, чтобы он был доступен в любой точке приложения. Например, в виде класса, в котором все функции статические. Или в виде обычного класса со статичной функцией &lt;code&gt;GetInstance()&lt;/code&gt; и приватным конструктором. Главное, учесть при реализации синглетона особенности языка программирования и платформы. Иначе ваше приложение рискует погрязнуть в ворохе непонятных и трудноуловимых багов. Я уже как-то писал об одной такой нетривиальной &lt;a href="http://derevyanko.blogspot.com/2009/01/clic.html"&gt;особенности реализации синглетона на CLI/C++&lt;/a&gt;. Оказалось, что и в реализации синглетона под Android есть подобная нетривиальная особенность. &lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Простейшая и вроде бы очевидная реализация синглетона под Android неверна. Пример:&lt;br /&gt;&lt;pre class="brush:java"&gt;public final class MySingleton {&lt;br /&gt;   private static MyClass m_A = new MyClass();&lt;br /&gt;   private static MyClass getA() {&lt;br /&gt;      return m_A;&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;Такой синглетон заработает. И может работать очень долго. Но в один прекрасный момент, вы рискуете получить &lt;code&gt;null&lt;/code&gt; в &lt;code&gt;getA()&lt;/code&gt;. Со всеми вытекающими последствиями...&lt;br /&gt;&lt;br /&gt;Причина проста. Вы инициализируете статический класс синглетона в контексте Activity. Если в вашем приложении несколько activity, то синглетон доступен в каждой из них. Но если первоначальная activity, в которой создавался синглетон, будет уничтожена, то и синглетон тоже будет уничтожен. После этого, в других activity вы будете получать null вместо A. А ведь под андроидом подобное уничтожение activity дело обычное..&lt;br /&gt;&lt;br /&gt;Проблема эта &lt;a href="http://stackoverflow.com/questions/3826905/singletons-vs-application-context-in-android"&gt;известна&lt;/a&gt;. Имеются решения:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;создавать &lt;a href="http://stackoverflow.com/questions/3826905/singletons-vs-application-context-in-android"&gt;синглетон в контексте сервиса&lt;/a&gt;, а не в контексте activity;&lt;/li&gt;&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/708012/android-how-to-declare-global-variables"&gt;унаследовать класс синглетона от класса &lt;code&gt;Application&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;На мой взгляд, второй способ проще. Если нет каких-либо особых противопоказаний, можно использовать именно его. Синглетон, унаследованный от &lt;code&gt;Application&lt;/code&gt;, будет выглядеть так:&lt;br /&gt;&lt;pre class="brush:java"&gt;public final class MySingleton extends Application {&lt;br /&gt;   private MyClass m_A = new MyClass();&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public void onCreate() {&lt;br /&gt;     super.onCreate();&lt;br /&gt;     //инициализация объектов синглетона&lt;br /&gt;     Resources r = this.getResources();      &lt;br /&gt;     ...&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private MyClass getA() {&lt;br /&gt;    return m_A;&lt;br /&gt;  } &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Нужно не забыть немножко подправить манифест, указав класс синглетона в качестве класса приложения:&lt;br /&gt;&lt;pre class="brush:xml"&gt;...&lt;br /&gt;   &lt;application android:name=".MySingleton" android:icon="@drawable/icon".../&gt;&lt;br /&gt;   ...&lt;br /&gt;&lt;/pre&gt;&lt;code&gt;MySingleton&lt;/code&gt; стал нестатическим классом. Тем не менее, обращаться к нему можно отовсюду, где имеется context - из виджета, из activity и т.п.&lt;br /&gt;&lt;pre class="brush:java"&gt;//виджет:&lt;br /&gt;public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {&lt;br /&gt;  MySingleton s = (MySingleton)context.getApplication(); &lt;br /&gt;}&lt;br /&gt;//Activity:&lt;br /&gt;public void onCreate(Bundle savedInstanceState) {&lt;br /&gt;  super.onCreate(savedInstanceState);&lt;br /&gt;  MySingleton s = (MySingleton)this.getApplication(); &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Не так удобно, как раньше, но вполне терпимо.&lt;h2&gt;Выводы&lt;/h2&gt;Если ваш приложение под Android стало вдруг временами зависать или виджет стал отваливаться через некоторое время после начала работы, проверьте: а правильно ли у вас реализованы синглетоны?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-346565496461740897?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/346565496461740897/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2010/12/android_26.html#comment-form' title='Комментарии: 6'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/346565496461740897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/346565496461740897'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2010/12/android_26.html' title='Синглетоны на Android. Как не стукнуться клювом об дерево...'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-8535958361177511948</id><published>2010-12-21T11:47:00.108+07:00</published><updated>2011-03-06T18:56:52.575+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='business'/><title type='text'>Расчетный счет ИП. Налоги, расходы и вычеты. Минимизация затрат.</title><content type='html'>В предыдущих статьях речь шла о тонкостях &lt;a href="http://derevyanko.blogspot.com/2010/09/1.html"&gt;открытия валютного расчетного счета ИП&lt;/a&gt;, &lt;a href="http://derevyanko.blogspot.com/2010/09/blog-post.html"&gt;оформлении паспорта сделки и получения денег от заказчика&lt;/a&gt;. Теперь поговорим о насущном: во сколько обходится содержание валютного расчетного счета, какие явные и неявные траты существуют и можно ли как-нибудь сэкономить. &lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Разумеется, в каждом банке свои тарифы и свои правила. Так что суммы, приведенные ниже, всего лишь иллюстрируют порядок величин и могут варьироваться на практике.&lt;br /&gt;&lt;h2&gt;Явные затраты по расчетному счету&lt;/h2&gt;Напомню, речь идет о двух счетах: рублевом и валютном. Для получения денег от иностранного заказчика нужны оба счета, соответственно, и оплачивать приходится оба.&lt;br /&gt;&lt;table border="1"&gt;&lt;tr&gt;&lt;th align="left" valign="top" width="50%"&gt;Затраты&lt;/th&gt;&lt;th align="left" valign="top" width="20%"&gt;Сумма&lt;/th&gt;&lt;th align="left" valign="top" width="30%"&gt;Оплата&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top"&gt;Открытие рублевого счета, всевозможные комиссии за свидетельствование подписей, копирование документов и т.д.&lt;/td&gt;&lt;td align="left" valign="top" &gt;1200 руб&lt;/td&gt;&lt;td align="left" valign="top"&gt;Один раз при открытии счета&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top"&gt;Открытие валютного счета&lt;/td&gt;&lt;td align="left" valign="top"&gt;$30&lt;/td&gt;&lt;td align="left" valign="top"&gt;Один раз при открытии счета&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top"&gt;Комиссия за ведение рублевого счета&lt;/td&gt;&lt;td align="left" valign="top"&gt;600 руб&lt;/td&gt;&lt;td align="left" valign="top"&gt;Каждый месяц, но только если были операции по счету, см. ниже.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top"&gt;Комиссия за ведение валютного счета&lt;/td&gt;&lt;td align="left" valign="top"&gt;$20&lt;/td&gt;&lt;td&gt;Каждый месяц, но только если были операции по счету, см. ниже.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top"&gt;Комиссия за выполнение функции валютного контроля + НДС&lt;/td&gt;&lt;td align="left" valign="top"&gt;300 руб&lt;/td&gt;&lt;td align="left" valign="top"&gt;За каждый полученный платеж в валюте.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top"&gt;Комиссия за выполнение платежных поручений&lt;/td&gt;&lt;td align="left" valign="top"&gt;20 руб&lt;/td&gt;&lt;td align="left" valign="top"&gt;За каждое платежное поручение.&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;Несколько замечаний.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Комиссия за ведение рублевого счета взимается только в том случае, если вы счетом пользовались, т.е. &lt;b&gt;переводили&lt;/b&gt; с него деньги. Если же вы не создавали платежных поручений, а только получали деньги на счет, то комиссия не взимается.&lt;/li&gt;&lt;li&gt;Похожая ситуация с валютным счетом. Если операции по счету не проводятся, то и комиссия не взимается. При этом обязательный перевод валюты с транзитного счета на текущий и &lt;strike&gt;обязательная&lt;/strike&gt; продажа валюты с текущего валютного счета на рублевый "операциями" не считаются. Так что лично с меня комиссию за ведение валютного счета пока не брали ни разу.&lt;/li&gt;&lt;li&gt;Комиссия за выполнение платежных поручений не берется при оплате налогов и сборов.&lt;/li&gt;&lt;li&gt;За перевод денег с вашего расчетного счета на вашу же пластиковую карточку, открытую в другом банке, дополнительных процентов не берут. Такая операция стоит 20 руб - как и любое другое платежное поручение.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;С какой суммы платить налог&lt;/h2&gt;Если вы используете упрощенную систему налогообложения с объектом налогообложения "доходы", то вы обязаны уплатить налог 6% с полученных доходов. Доходы приходят в валюте, а налоги платятся в рублях. С какой суммы платится налог?&lt;br /&gt;&lt;br /&gt;Напомню, что при поступлении платежа необходимо выполнить &lt;a href="http://derevyanko.blogspot.com/2010/09/blog-post.html"&gt;две операции&lt;/a&gt;: оформить валютную справку и перевести деньги транзитного счета на текущий, а затем продать валюту с текущего валютного счета на рублевый. Банковские операции - дело не быстрое, так что за один день не управиться. В итоге возникает курсовая разница между рублевым эквивалентом полученной суммы на момент зачисления и итоговой суммой, поступившей вам на рублевый расчетный счет. Причем разница может быть как положительной, так и отрицательной.&lt;br /&gt;&lt;br /&gt;Пример. Вам поступил платеж $1000. Обстоятельства сложились так, что курс доллара в это время стабильно рос. Получаем следующее:&lt;br /&gt;&lt;table border="1"&gt;&lt;tr&gt;&lt;th align="left" valign="top" width="15%"&gt;День&lt;/th&gt;&lt;th align="left" valign="top" width="45%"&gt;Операция&lt;/th&gt;&lt;th align="left" valign="top" width="15%"&gt;Курс ЦБ&lt;/th&gt;&lt;th align="left" valign="top" width="35%"&gt;Сумма на вашем счету&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top"&gt;1&lt;/td&gt;&lt;td align="left" valign="top"&gt;Зачисление денег на транзитный валютный счет&lt;/td&gt;&lt;td align="left" valign="top"&gt;30&lt;/td&gt;&lt;td align="left" valign="top"&gt;$1000 = 30000 руб&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top"&gt;2&lt;/td align="left" valign="top"&gt;&lt;td&gt;Перевод денег на текущий валютный счет&lt;/td&gt;&lt;td align="left" valign="top"&gt;31&lt;/td&gt;&lt;td align="left" valign="top"&gt;$1000 = 31000 руб&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="left" valign="top"&gt;3&lt;/td&gt;&lt;td align="left" valign="top"&gt;Продажа валюты на рублевый расчетный счет&lt;/td&gt;&lt;td align="left" valign="top"&gt;32&lt;/td&gt;&lt;td align="left" valign="top"&gt;1000 * Курс банка = N руб (см. ниже)&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;Вопрос: с какой суммы платить налог?&lt;br /&gt;&lt;br /&gt;Ответ я нашел &lt;a href="http://forum.klerk.ru/showthread.php?t=347579"&gt;здесь&lt;/a&gt;: "у предпринимателей (не путать с организациями), курсовая разница возникает (и с нее платится налог) только при продаже валюты дороже курса ЦБ на дату продажи (у меня такое иногда случается, как ни странно). При изменении курса лежащей на счете валюты у ИП - нет курсовой разницы."&lt;br /&gt;&lt;br /&gt;Таким образом, налог платится с 30000 рублей. Если в момент продажи валюты (день 3) курс банка был выше, чем курс ЦБ на дату продажи (т.е. в день 3), то вам потребуется доплатить налог с курсовой разницы. У меня такого пока не было ни разу - банк стабильно держит курс валюты ниже, чем у ЦБ. &lt;br /&gt;&lt;h2&gt;Курс ЦБ и курс банка&lt;/h2&gt;В приведенном выше примере у ЦБ курс в день 3 был 32 руб. У банка он был ниже, скажем, 31.5. В результате мы платим налог с 30 т.р., а на наш счет упала сумма 31500 руб. Мы в выигрыше. Но если доллар за эти дни не рос в цене, а падал, то мы будем в проигрыше: налог платим с 30 т.р., а на счет упадет, скажем, всего 28500 руб. Тут уж как повезет с курсом.&lt;br /&gt;&lt;br /&gt;Здесь есть еще одна тонкость - курс банка постоянно меняется. Причем это совсем не тот курс, который используется для обмены валюты физ. лицами и который устанавливается раз в сутки. Интересующий наш курс изменяется непрерывно и зависит от торгов на ММВБ. Когда вы подаете заявление на продажу валюты - там один курс. Через час, когда оператор это заявление пускает в работу, курс может быть совершенно другим. Сплошная лотерея.&lt;br /&gt;&lt;br /&gt;Из банка удалось вытянуть следующую информацию по этому вопросу. "На сегодняшний день действуют следующие спрэды по паре ЕВРО/рубль: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;с 10-01 до 18-00 (время московское)  - 30 копеек,&lt;/li&gt;&lt;li&gt;с 18-01 до 10-00 (время московское) - 70 копеек"&lt;/li&gt;&lt;/ul&gt;Спред здесь - это расхождение с курсом ЦБ. Разница во времени у нас (в Красноярске) с Москвой 4 часа. В результате, если я оформляю заявку на продажу утром, до 14:00 (=10:00 по Москве), то натыкаюсь на курс примерно на 40 копеек ниже, чем после 14:00 (кр). Так что теперь до 14:00 по красноярскому времени заявки я не оформляю..&lt;br /&gt;&lt;br /&gt;P.s. подозреваю, далеко не все банки продают валюту на бирже. В крупных банках подобной "лотереи", скорее всего, можно избежать. &lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Налоговые вычеты за платежи в пенсионный фонд РФ&lt;/h2&gt;Все индивидуальные предприниматели ежегодно платят отчисления в ПФР, ФФОМС и ТФОМС. В 2010 г. сумма этих отчислений составляет 12 т.р. Сумма приличная... Но есть один положительный момент - ее можно использовать для сокращения величины налогов.&lt;br /&gt;&lt;br /&gt;Если вкратце, то согласно &lt;a href="http://www.zakonrf.info/nk/346.21/"&gt;статье 346.21. НК РФ&lt;/a&gt;, вы имеете право уменьшить налог на сумму отчислений в ПФР, ФФОМС и ТФОМС но не более чем на 50% от суммы налога. Для того, чтобы вычесть из налога все 12 т.р., ваш суммарный налог за год должен составить более 24 т.р. Вот &lt;a href="http://forum.klerk.ru/showthread.php?t=281186"&gt;здесь&lt;/a&gt; детально разбирается алгоритм расчета налога с учетом вычетов. &lt;br /&gt;&lt;br /&gt;Налоги платятся ежеквартально, а отчисления в ПФР можно платить ежемесячно, ежеквартально, или всю сумму разом в конце года. Для вычетов это не имеет значение - даже если вы платите в ПФР в конце года, налоговый вычет на всю сумму налогов сделать все-равно &lt;a href="http://forum.klerk.ru/showthread.php?p=53082338#post53082338"&gt;можно&lt;/a&gt;.&lt;br /&gt;&lt;h2&gt;Итоги: минимизация расходов&lt;/h2&gt;Итак, вот несколько возможных путей минимизации затрат связанных с расчетным счетом.&lt;ul&gt;&lt;li&gt;Постарайтесь получать платежи в то время, когда курс валюты растет, а не падает (да, я знаю что фиг подгадаешь :).&lt;/li&gt;&lt;li&gt;Правильно выбирайте время дня для создания заявки на продажу валюты - время, когда банковский курс валюты максимально высок и близок к курсу ЦБ.&lt;/li&gt;&lt;li&gt;Лучше забрать деньги два раза в одном месяце, чем по одному разу в каждом из двух месяцев. В первом случае вы заплатите комиссию за обслуживание счета один раз, во втором - дважды.&lt;/li&gt;&lt;li&gt;Используйте налоговые вычеты связанные с платежами в ПФ РФ, ФФОМС и ТФОМС для сокращение величины налогов.&lt;/li&gt;&lt;li&gt;Один крупный платеж выгоднее двух мелких - комиссия за проведение валютной операции для них одинакова.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; И еще: нужно &lt;a href="http://www.nalogservice.ru/faq/document26263.htm"&gt;вовремя&lt;/a&gt; сдавать отчетность в пенсионный фонд и налоговую, чтобы штрафов не накидали.. В пенсионном фонде с этого года тоже &lt;a href="http://derevyanko.blogspot.com/2011/02/2010.html"&gt;приходится отчитываться&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-8535958361177511948?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/8535958361177511948/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2010/12/blog-post.html#comment-form' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8535958361177511948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8535958361177511948'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2010/12/blog-post.html' title='Расчетный счет ИП. Налоги, расходы и вычеты. Минимизация затрат.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-6004028506114065528</id><published>2010-12-21T11:21:00.003+07:00</published><updated>2010-12-31T23:17:03.886+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='release'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Первая бета Animated Widget Contact Launch на маркете.</title><content type='html'>Первая бета версия моего приложения Animated Widget Contact Launch (AWidget) &lt;a href="http://www.androidpit.com/en/android/market/apps/app/com.mobilityflow.awidget/Animated-Widget-Contact-Launch"&gt;появилась&lt;/a&gt; на Android Market. &lt;br /&gt;&lt;br /&gt;Это - виджит, которому можно присвоить контакт из телефоной книги. Виджет отображает фотографию из свойств контакта или, если контакт без фотографии, имя. &lt;br /&gt;&lt;br /&gt;Назначение виджета - ускорить выполнение операций с контактом. При щелчке по виджиту из него &lt;i&gt;анимированно&lt;/i&gt; выезжает куча шариков, каждый из которых связан с той или иной функцией - позвонить, послать email, смс, отредактировать контакт и т.п. &lt;br /&gt;&lt;br /&gt;Таким образом, любая операция с контактом выполняется в два щелчка. Плюс анимация радует глаз.&lt;br /&gt;&lt;br /&gt;AWidget любопытен тем, что в нем использована &lt;a href="http://derevyanko.blogspot.com/2010/12/android.html"&gt;технология анимации виджета по клику&lt;/a&gt;, о которой я недавно рассказывал. &lt;br /&gt;&lt;br /&gt;Мелких багов в AWidget пока еще достаточно (это бета версия, которая требует обкатки), но в целом все работает. Плюс - AWidget полностью бесплатен.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://fs01.androidpit.info/aqr/x61/1249961-1292883356647.png"/&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; Вот &lt;a href="http://4pda.ru/forum/index.php?showtopic=208410&amp;st=0&amp;gopid=6055773&amp;#entry6055773"&gt;Пост на 4PDA&lt;/a&gt;, посвященный Animated Widget Contact Launch. Со скриншотами.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update2:&lt;/b&gt; &lt;a href="http://www.android-widget.com/"&gt;сайт AWidget&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-6004028506114065528?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/6004028506114065528/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2010/12/animated-widget-contact-launch.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6004028506114065528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/6004028506114065528'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2010/12/animated-widget-contact-launch.html' title='Первая бета Animated Widget Contact Launch на маркете.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-2253764919157873226</id><published>2010-12-02T18:39:00.001+07:00</published><updated>2010-12-21T11:22:53.730+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Анимированный виджет на Android. Анимация по клику.</title><content type='html'>Итак, анимированный виджет на Android сделать толком &lt;a href="http://derevyanko.blogspot.com/2010/11/android-setimageviewuri.html"&gt;не получается&lt;/a&gt;. Идем в обход? &lt;br /&gt;&lt;br /&gt;Идея проста. Делаем виджет. При нажатии на виджет появляется активити. Активити полностью прозрачна и целиком перекрывает рабочий стол. В том месте, где на рабочем столе расположен исходный виджет, на активити рисуем такой же "виджет". И уже этот "виджет" мы можем сделать анимированным. &lt;br /&gt;&lt;br /&gt;Практика показала, что такой подход действительно работает. Но в нем много нюансов. О них и пойдет речь.&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Тестовый проект AniClick&lt;/b&gt;&lt;br /&gt;Рассмотрим простейший пример такого анимированного виджета. Для простоты вновь используем ту же анимацию, что в &lt;a href="http://derevyanko.blogspot.com/2010/11/android-setimageviewuri.html"&gt;прошлый раз&lt;/a&gt; - крутящееся колесо. Анимация колеса состоит из 10 кадров. Статический виджет показывает колесо - один (по умолчанию, самый первый) кадр анимации. При нажатии на виджет колесо на экране начинает крутиться. При повторном нажатии колесо останавливается. Теперь виджет показывает другой кадр анимации - тот, на котором произошел клик. Если снова кликнуть виджет, колесо вновь начнет крутиться. И т.д.&lt;br /&gt;&lt;br /&gt;Проект называется &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/AniClick"&gt;AniClick&lt;/a&gt;. В проекте 5 основных исходных файлов:&lt;ul&gt;&lt;li&gt;&lt;code&gt;AniWidgetProvider.java&lt;/code&gt; - реализация провайдера виджета. Содержит код для вызова активити и обновления виджета после завершения работы активити.&lt;/li&gt;&lt;li&gt;&lt;code&gt;DataStorage.java&lt;/code&gt; - отвечает за сохранение и загрузку данных (номер кадра) каждого экземпляра виджета в &lt;code&gt;SharedPreferences&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;MainActivity.java&lt;/code&gt; - прозрачная активити, которая перекрывает экран при нажатии на виджет. Внутренний класс &lt;code&gt;ScreenView&lt;/code&gt; обеспечивает немедленную перерисовку экрана и позволяет достичь приемлемых значений FPS.&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;ScreenManager.java&lt;/code&gt; - эмулирует анимацию (перебирает кадр за кадром) и отрисовывет текущий кадр на переданный &lt;code&gt;canvas&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;code&gt;Utils.java&lt;/code&gt; - вспомогательный класс, позволяющий конвертировать &lt;code&gt;Rect&lt;/code&gt; в строку и обратно.&lt;/li&gt;&lt;/ul&gt;Проект небольшой, классы легковесные и довольно простые. Исходные коды можно &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/AniClick"&gt;просмотреть&lt;/a&gt; онлайн, можно &lt;a href="http://code.google.com/p/dvsrc/downloads/detail?name=20101102_AniClick_src.7z&amp;can=2&amp;q="&gt;скачать&lt;/a&gt; в виде 7z-архива.&lt;br /&gt;&lt;br /&gt;Теперь переходим к особенностям реализации.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Вызов активити при нажатии на виджет&lt;/b&gt;&lt;br /&gt;Первая проблема - получение координат исходного виджета. Нам они необходимы, чтобы мы могли нарисовать на активити картинку точно поверх исходного виджета.&lt;br /&gt;&lt;pre class="brush:java"&gt;//class AniWidgetProvider&lt;br /&gt;...&lt;br /&gt;public void onReceive(Context context, Intent intent) {&lt;br /&gt;  final String action = intent.getAction();&lt;br /&gt;  if (action.equals(ACTION_WIDGET_CONTROL)) {&lt;br /&gt;    Uri uri = intent.getData();&lt;br /&gt;    Rect widget_pos = intent.getSourceBounds();&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;Все просто. Но код работает только под API 2.1. В более ранних версиях API функции getSourceBounds нет и координаты кликнутного виджета узнать невозможно (?).&lt;br /&gt;&lt;br /&gt;Вызов activity из виджета выполняется следующим образом:&lt;pre class="brush:java"&gt;private void make_widget_action(Context context, int appWidgetId, Rect widgetPos) {&lt;br /&gt; Intent start = new Intent(context, MainActivity.class);&lt;br /&gt; start.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);&lt;br /&gt; start.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);&lt;br /&gt; start.putExtra(PARAM_WIDGET_ID, appWidgetId); &lt;br /&gt; start.putExtra(PARAM_WIDGET_POSITION, Utils.RectToStr(widgetPos)); &lt;br /&gt; context.startActivity(start);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Прозрачная activity&lt;/b&gt;&lt;br /&gt;Наша активити должна быть прозрачной. Как делать прозрачные activity &lt;a href="http://zaman91.wordpress.com/2010/03/22/android-how-to-create-transparent-or-opeque-background/"&gt;известно&lt;/a&gt; - нужно использовать тему &lt;code&gt;@android:style/Theme.Translucent&lt;/code&gt;. Беда в том, что в этой теме свойство стиля &lt;code&gt;android:windowNoTitle&lt;/code&gt; выключено. Поэтому, при нажатии на виджет, сверху будет появляться заголовок activity. Отключить заголовок можно, создав собственную тему:&lt;pre class="brush:xml"&gt;&amp;lt;!-- res/values/TranslucentNoTitleBarTheme.xml --&amp;gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt;&amp;lt;resources&amp;gt;&lt;br /&gt;  &amp;lt;style name="TranslucentNoTitleBarTheme" parent="@android:style/Theme.Translucent"&amp;gt;&lt;br /&gt; &amp;lt;item name="android:windowNoTitle"&amp;gt;false&amp;lt;/item&amp;gt;&lt;br /&gt;  &amp;lt;/style&amp;gt;&lt;br /&gt;&amp;lt;/resources&amp;gt;&lt;br /&gt;&lt;/pre&gt;и применив ее в манефесте к нашей activity:&lt;pre class="brush:xml"&gt;&amp;lt;activity android:name=".MainActivity"&lt;br /&gt;  android:theme="@style/TranslucentNoTitleBarTheme"&lt;br /&gt;  android:label="@string/app_name"&amp;gt;&lt;br /&gt;&amp;lt;/activity&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Обновление виджета после завершения активити&lt;/b&gt;&lt;br /&gt;Ткнули на крутящееся колесо, колесо остановилось, activity закрывается. Запомнили кадр, который показывался в момент клика. Теперь нужно сообщить виджету о том, что его картинку следует обновить. Это делается следующим образом. В activity даем команду виджиту:&lt;pre class="brush:java"&gt;public class MainActivity extends Activity {&lt;br /&gt;....&lt;br /&gt;@Override&lt;br /&gt;public boolean onTouchEvent(MotionEvent event) {&lt;br /&gt;//any click on the animation stops it  &lt;br /&gt; if (event.getAction() == MotionEvent.ACTION_DOWN) { &lt;br /&gt;    ...&lt;br /&gt;    Intent active = new Intent(this, AniWidgetProvider.class);&lt;br /&gt;    active.setAction(AniWidgetProvider.ACTION_UPDATE_WIDGET_CONTEXT);&lt;br /&gt;    Uri uri = Uri.parse(AniWidgetProvider.URI_SCHEME + "://widget#");&lt;br /&gt;    uri = uri.buildUpon()&lt;br /&gt;      .appendQueryParameter("widget_id", String.valueOf(m_WidgetId))&lt;br /&gt;      .build();&lt;br /&gt;    active.setData(uri);      &lt;br /&gt;    this.sendBroadcast(active); //сообщаем виджету об обновлении&lt;br /&gt;   ...&lt;br /&gt; }&lt;br /&gt; return super.onTouchEvent(event);&lt;br /&gt;}&lt;br /&gt;...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;В провайдере виджета принимаем и выполняем эту команду:&lt;pre class="brush:java"&gt;//class AniWidgetProvider&lt;br /&gt;public static final String ACTION_UPDATE_WIDGET_CONTEXT &lt;br /&gt;  = "com.rammus.aniclick.UPDATE_WIDGET_CONTEXT";&lt;br /&gt;...&lt;br /&gt;@Override&lt;br /&gt;public void onReceive(Context context, Intent intent) {&lt;br /&gt;...&lt;br /&gt; if (action.equals(ACTION_UPDATE_WIDGET_CONTEXT)) {&lt;br /&gt;   Uri uri = intent.getData();&lt;br /&gt;   int widget_id = Integer.parseInt(uri.getQueryParameter("widget_id"));&lt;br /&gt;   if (widget_id != AppWidgetManager.INVALID_APPWIDGET_ID) {&lt;br /&gt;  //перерисовывем картинку&lt;br /&gt;    WidgetInstanceManager wim = get_instance(widget_id);&lt;br /&gt;    wim.ReinitializeWidgetBitmap(context);&lt;br /&gt;  //даем команду виджету обновить картинку&lt;br /&gt;    RemoteViews remote_views &lt;br /&gt;       = new RemoteViews(context.getPackageName()&lt;br /&gt;            , R.layout.widget);&lt;br /&gt;    update_widget_view(AppWidgetManager.getInstance(context)&lt;br /&gt;       , widget_id&lt;br /&gt;       , remote_views);&lt;br /&gt;   } else super.onReceive(context, intent);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;private void update_widget_view(AppWidgetManager appWidgetManager&lt;br /&gt;  , int appWidgetId, RemoteViews remoteViews) {&lt;br /&gt;  WidgetInstanceManager wim = get_instance(appWidgetId);  &lt;br /&gt;  remoteViews.setImageViewBitmap(R.id.widget_image_view, wim.GetBitmap() );&lt;br /&gt;  appWidgetManager.updateAppWidget(appWidgetId, remoteViews);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;View для рисования на activity.&lt;/b&gt;&lt;br /&gt;Чтобы можно было рисовать на активити, нам нужен View. Изначально я пошел следующим путем:&lt;pre class="brush:java"&gt;public class ActivityMain extends Activity {&lt;br /&gt; private ScreenView m_MainView;&lt;br /&gt; ...&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public void onCreate(Bundle savedInstanceState) {&lt;br /&gt;   super.onCreate(savedInstanceState);     &lt;br /&gt;   m_MainView = new ScreenView(this);&lt;br /&gt;   setContentView(m_MainView);&lt;br /&gt;   ...&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private class ScreenView extends View { &lt;br /&gt;  public ScreenView(Context context){&lt;br /&gt;   super(context);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  //рисуем текущий фрейм анимации на активити&lt;br /&gt;  @Override protected void onDraw(Canvas canvas) {&lt;br /&gt;    super.onDraw(canvas);   &lt;br /&gt;    RedrawScreen();&lt;br /&gt;  }&lt;br /&gt; } //ScreenView&lt;br /&gt;&lt;br /&gt; private class AnimationThread extends TimerTask {  &lt;br /&gt;  @Override &lt;br /&gt;  public void run() {&lt;br /&gt;  //запускается 25 раз в секунду&lt;br /&gt;  //перейти к следующему кадру анимации&lt;br /&gt;    MakeAnimationStep();&lt;br /&gt;  //перерисовать активити&lt;br /&gt;    ActivityMain.this.m_MainView.postInvalidate();&lt;br /&gt;  }   &lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;У нас два вспомагательных класса. &lt;code&gt;ScreenView&lt;/code&gt; - это View, на котором рисуется очередной фрейм анимации. &lt;code&gt;AnimationThread&lt;/code&gt; - поток, который 25 раз в секунду увеличивает номер текущего кадра анимации и дает команду перерисовать активити. Все работает, но анимация рисуется рывками - часть кадров пропускается.&lt;br /&gt;&lt;br /&gt;Причина пропусков очевидна. Команда invalidate &lt;a href="http://stackoverflow.com/questions/2801877/android-why-wont-invalidate-update-my-buttons-immediately"&gt;не обновляет экран мгновенно&lt;/a&gt; - она лишь помещает команду redraw в очередь. У нас команды redraw генерируются с заданным FPS - 25 раз в секунду. А слишком частые redraw приводят к тому, что OS объединяет несколько redraw в одну команду (для оптимизации). Для покадрового вывода анимации такое поведение не подходит.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;SurfaceView вместо View&lt;/b&gt;&lt;br /&gt;При разработке игр вместо View используют &lt;a href="http://stackoverflow.com/questions/1243433/android-difference-between-surfaceview-and-view"&gt;SurfaceView&lt;/a&gt;. Основное отличие - в SurfaceView можно рисовать из фоновых потоков, что идеально подходит в нашем случае. Вот как выглядит код с SurfaceView:&lt;pre class="brush:java"&gt;class ScreenView extends SurfaceView implements SurfaceHolder.Callback { &lt;br /&gt; private final String TAG_LOG = "com.rammus.flw.ScreenView";&lt;br /&gt; SurfaceHolder m_SurfaceHolder;&lt;br /&gt; &lt;br /&gt; public ScreenView(Context context){&lt;br /&gt;  super(context);&lt;br /&gt;&lt;br /&gt;  m_SurfaceHolder = this.getHolder();&lt;br /&gt;  //ставим формат TRANSLUCENT чтобы получить &lt;br /&gt;  //прозрачную activity&lt;br /&gt;  m_SurfaceHolder.setFormat(PixelFormat.TRANSLUCENT );&lt;br /&gt;  m_SurfaceHolder.addCallback(this);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;  public void surfaceCreated(SurfaceHolder holder) {&lt;br /&gt;  //необходимо реализовать интерфейс SurfaceHolder.Callback&lt;br /&gt;  //и вызвать Repaint() при создании поверхности.&lt;br /&gt;  //Иначе мы получим моргание экрана в момент запуска activity.&lt;br /&gt;    Repaint(); &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void surfaceDestroyed(SurfaceHolder holder) {&lt;br /&gt;  }  &lt;br /&gt; &lt;br /&gt;  public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {&lt;br /&gt;  }  &lt;br /&gt;&lt;br /&gt;  @Override protected void onDraw(Canvas canvas) {&lt;br /&gt;    super.onDraw(canvas);   &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private void paint(Canvas canvas) {&lt;br /&gt;    RedrawScreen();&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  public void Repaint() {&lt;br /&gt;  //доступ к lockCanvas должен быть синхронизирован&lt;br /&gt;  //чтобы функцию Repaint можно было вызывать из&lt;br /&gt;  //разных потоков&lt;br /&gt;    synchronized (m_SurfaceHolder) {&lt;br /&gt; Canvas c = m_SurfaceHolder.lockCanvas();&lt;br /&gt; if (c != null) {&lt;br /&gt;     try {&lt;br /&gt;     super.draw(c);&lt;br /&gt;     paint(c);&lt;br /&gt;   } finally {&lt;br /&gt;     m_SurfaceHolder.unlockCanvasAndPost(c);&lt;br /&gt;   }&lt;br /&gt;       } else {&lt;br /&gt;  Log.e(TAG_LOG, "c is null!");&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt; } //Repaint&lt;br /&gt;} //ScreenView&lt;br /&gt;&lt;br /&gt;public class ActivityMain extends Activity {&lt;br /&gt; private ScreenView m_MainView;&lt;br /&gt; ...&lt;br /&gt; @Override&lt;br /&gt; public void onCreate(Bundle savedInstanceState) {&lt;br /&gt;   super.onCreate(savedInstanceState);     &lt;br /&gt;   m_MainView = new ScreenView(this);&lt;br /&gt;   setContentView(m_MainView);&lt;br /&gt;   ...&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private class AnimationThread extends TimerTask {  &lt;br /&gt;   @Override &lt;br /&gt;   public void run() {&lt;br /&gt;     m_ScreenManager.MakeAnimationStep();&lt;br /&gt;     //перерисовка экрана вызывается из ФОНОВОГО потока.&lt;br /&gt;     m_MainView.Repaint();&lt;br /&gt;   }   &lt;br /&gt; } &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Вместо &lt;code&gt;invalidate&lt;/code&gt; мы вызываем функцию &lt;code&gt;Repaint&lt;/code&gt;, которая рисует картинку на &lt;code&gt;SurfaceView&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Здесь хочу обратить внимание на несколько важных моментов.&lt;ol&gt;&lt;li&gt;Для того, чтобы наша поверхность была прозрачной, необходимо вызвать&lt;br /&gt;&lt;code&gt;setFormat(PixelFormat.TRANSLUCENT)&lt;/code&gt; По умолчанию установлен формат &lt;code&gt;PixelFormat.OPAQUE&lt;/code&gt;, это не то.&lt;/li&gt;&lt;li&gt;Перерисовка экрана вызывается из фонового потока &lt;code&gt;AnimationThread&lt;/code&gt;. Чтобы перерисовка работа корректно, вызовы функций &lt;code&gt;lockCanvas&lt;/code&gt; и &lt;code&gt;unlockCanvasAndPost&lt;/code&gt; должны быть синхронизированы. Отсюда synchronize в коде.&lt;/li&gt;&lt;li&gt;ScnreenView должен реализовывать интерфейс &lt;code&gt;SurfaceHolder.Callback&lt;/code&gt; и обязательно вызывать функцию отрисовки при создании поверхности в функции &lt;code&gt;surfaceCreated&lt;/code&gt;. Если этого не сделать, то при запуске activity экран на короткое мгновение будет становиться черным.&lt;/li&gt;&lt;li&gt;Функция &lt;code&gt;lockCanvas&lt;/code&gt; корректно отрабатывает только после полного создания activity. Если вызвать функцию Repaint в &lt;code&gt;ActivityMain.OnCreate&lt;/code&gt;, то мы &lt;code&gt;lockCanvas&lt;/code&gt; вернет null и мы получим ошибку "c is null!" в логе. Таким образом МГНОВЕННО запустить анимацию не получится - задержка равная времени создания activity неизбежна.&lt;/li&gt;&lt;/ol&gt;И последнее - наша activity прозрачна. Сквозь нее виден рабочий стол и, естественно, наш исходный виджет. В тестовом примере анимированное колесо крутится на активити, а под ним видно колесо, нарисованное на виджите. Думаю, что эта проблема вполне решаема, но в тестовом проекте я ее не касался.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Выводы&lt;/b&gt;&lt;br /&gt;Подход с псевдоанимированным виджетом оказался вполне рабочим. Конечно, есть определенные проблемы - под активити видно исходный виджет, запуск анимации происходит не мгновенно, а лишь после создания активити, анимация в виджете работает только по клику на виджет и т.д. Однако, существует ряд приложений, в которых такой анимации более чем достаточно. Для них главное, чтобы значение FPS получалось пристойным - а этого можно добиться.&lt;br /&gt;&lt;br /&gt;Одно из таких приложений мы сейчас разрабатываем. Надеюсь, уже &lt;a href="http://derevyanko.blogspot.com/2010/12/animated-widget-contact-launch.html"&gt;скоро оно появится на маркете&lt;/a&gt; и тогда данный подход пройдет проверку боем. Там посмотрим.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Исходные коды проекта&lt;/b&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/AniClick"&gt;Просмотреть&lt;/a&gt; исходные коды, &lt;a href="http://code.google.com/p/dvsrc/downloads/detail?name=20101102_AniClick_src.7z&amp;can=2&amp;q="&gt;скачать&lt;/a&gt; исходные коды, &lt;a href="http://code.google.com/p/dvsrc/downloads/detail?name=20101102_AniClick_apk.7z&amp;can=2&amp;q="&gt;скачать apk&lt;/a&gt;. Short description in English is &lt;a href="http://code.google.com/p/dvsrc/wiki/AndroidAniClick"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-2253764919157873226?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/2253764919157873226/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2010/12/android.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/2253764919157873226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/2253764919157873226'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2010/12/android.html' title='Анимированный виджет на Android. Анимация по клику.'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-3881823924742256995</id><published>2010-11-18T10:56:00.000+07:00</published><updated>2010-11-18T10:56:14.571+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Анимированный виджет на Android и setImageViewUri</title><content type='html'>В &lt;a href="http://derevyanko.blogspot.com/2010/10/android-how-to.html"&gt;прошлый раз&lt;/a&gt;, обсуждая возможность создания анимированного виджета на Android, я остановился на прискорбном выводе: виджет с динамически генерируемой анимацией создать не получается. Если картинки для анимации сидят в ресурсах - тогда другое дело, функция setImageViewResource отрабатывает бодро. А вот пересылка в remoteview битмапок через  setImageViewBitmap по несколько раз в секунду приводит к катастрофическому проседанию FPS и ошибкам FAILED BINDER TRANSACTION. Остался открытым вопрос - что если для обновления виджета применить функцию setImageViewUri? Попробуем.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;За основу я взял тот же тестовый проект, что и в прошлый раз - &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/TestAnimatedWidget"&gt;TestAnimatedWidget&lt;/a&gt;. Добавил в него третий режим эмуляции анимации:&lt;pre class="brush:java"&gt;private enum tmodes {&lt;br /&gt;  MODE_PASS_BITMAP_ID //setImageViewResource&lt;br /&gt;  , MODE_PASS_BITMAP_ITSELF //use setImageViewBitmap&lt;br /&gt;  , MODE_PASS_BITMAP_URI //use setImageViewUri&lt;br /&gt;}; &lt;br /&gt; private static String PATH_TO_INTERNAL_FILES; //path to stored bitmaps&lt;br /&gt;&lt;/pre&gt;Ввел функцию &lt;code&gt;save_all_bitmaps_from_resources_to_application_directory&lt;/code&gt; для сохранения битмапок из ресурсов в файлы:&lt;br /&gt;&lt;pre class="brush:java"&gt;private void save_all_bitmaps_from_resources_to_application_directory(Context context) {&lt;br /&gt;  for (int i = 0; i &amp;lt; WidgetInstanceContent.WHEEL_ANIMATION.length; ++i) {&lt;br /&gt;    Bitmap bmp = WidgetInstanceContent.GetCachedBitmapById(i + 1);&lt;br /&gt;    try {    &lt;br /&gt;      String filename = String.valueOf(i + 1) + ".png";&lt;br /&gt;      FileOutputStream out = context.openFileOutput(filename, Context.MODE_WORLD_READABLE);    &lt;br /&gt;      bmp.compress(Bitmap.CompressFormat.PNG, 100, out);&lt;br /&gt;    } catch (Exception e) {&lt;br /&gt;      e.printStackTrace();&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;Эта функция вызывается в OnUpdate. &lt;br /&gt;&lt;pre class="brush:java"&gt;Uri uri = Uri.fromFile(context.getFileStreamPath("FILENAME"));&lt;br /&gt;PATH_TO_INTERNAL_FILES = uri.toString();&lt;br /&gt;if (USE_GENERATED_BITMAPS == tmodes.MODE_PASS_BITMAP_URI) {&lt;br /&gt;  save_all_bitmaps_from_resources_to_application_directory(context);&lt;br /&gt;}&lt;/pre&gt;В функцию &lt;code&gt;update_widget_view&lt;/code&gt; добавил обработку третьего режима эмуляции анимации:&lt;br /&gt;&lt;pre class="brush:java"&gt;...&lt;br /&gt;} else if (USE_GENERATED_BITMAPS == tmodes.MODE_PASS_BITMAP_URI) { &lt;br /&gt;//pass URI of bitmap to remote view&lt;br /&gt;  int step = wic.FrameId;&lt;br /&gt;  String s = PATH_TO_INTERNAL_FILES;&lt;br /&gt;  s = s.replace("FILENAME", String.valueOf(step + 1) + ".png");&lt;br /&gt;  try {   &lt;br /&gt;    s = s.replace("file://", "");&lt;br /&gt;    Uri uri = Uri.parse(new File(s).toString());&lt;br /&gt;    remoteViews.setImageViewUri(R.id.bkview, uri);&lt;br /&gt;    appWidgetManager.updateAppWidget(appWidgetId, remoteViews);&lt;br /&gt;  } catch (Exception ex) {&lt;br /&gt;    Log.e(TAG_LOG, ex.toString());&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;Несколько замечаний по коду. Переменная PATH_TO_INTERNAL_FILES используется для хранения пути к сохраняемым файлам (получить этот путь в update_widget_view не удается, т.к. там нет context). В качестве имени файла в ней указано "FILENAME" - в процессе работы вместо "FILENAME" подставляется реальное имя файла. Префикс "file://" приходится удалять, т.к. с ним setImageViewUri не работает, похоже из-за &lt;a href="http://code.google.com/p/android/issues/detail?id=2733&amp;q=setImageUri&amp;colspec=ID%20Type%20Status%20Owner%20Summary%20Stars"&gt;вот этого бага&lt;/a&gt; вылазит ошибка "resolveUri failed on bad bitmap uri...".&lt;br /&gt;&lt;h2&gt;Результат&lt;/h2&gt;Результат отрицательный. Ошибка FAILED BINDER TRANSACTION вылазить перестала, но анимация выглядит отвратительно: слишком многие кадры пропускаются. Функцию setImageViewUri можно использовать для загрузки больших картинок в виджет, а вот увеличить вызывать ее 10-25 раз в секунду, чтобы сэмулировать анимацию, не получится.&lt;br /&gt;&lt;h2&gt;Исходные коды&lt;/h2&gt;Обновленную версию примера можно взять в репозитории &lt;a href="http://code.google.com/p/dvsrc/source/browse/trunk/Android/TestAnimatedWidget"&gt;TestAnimatedWidget&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-3881823924742256995?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/3881823924742256995/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2010/11/android-setimageviewuri.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/3881823924742256995'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/3881823924742256995'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2010/11/android-setimageviewuri.html' title='Анимированный виджет на Android и setImageViewUri'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-8251033555591347073</id><published>2010-11-17T13:44:00.000+07:00</published><updated>2010-11-17T13:44:30.732+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><title type='text'>Исходные коды для блога на google code</title><content type='html'>Наконец то решил для себя вопрос, где хранить исходные коды мелких проектов, которые я создаю для блога. Решил тривиально - завел на google code новый проект &lt;a href="http://code.google.com/p/dvsrc/"&gt;dvsrc&lt;/a&gt;. Теперь буду заливать все исходники туда и для каждого создавать короткое wiki-сообщение на английском языке. Исходники будут подразделяться на группы C++, C#, Delphi, Android и т.д. Надеюсь, будет удобно и мне, и читателям.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3333619357391010212-8251033555591347073?l=derevyanko.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derevyanko.blogspot.com/feeds/8251033555591347073/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://derevyanko.blogspot.com/2010/11/google-code.html#comment-form' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8251033555591347073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3333619357391010212/posts/default/8251033555591347073'/><link rel='alternate' type='text/html' href='http://derevyanko.blogspot.com/2010/11/google-code.html' title='Исходные коды для блога на google code'/><author><name>dv</name><uri>http://www.blogger.com/profile/05900550318230375812</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-du3UfslCcAI/Th2emZ0ZTBI/AAAAAAAAACM/TzL59WYm46Y/s220/avatar2011.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3333619357391010212.post-8066620954462340453</id><published>2010-11-13T11:42:00.007+07:00</published><updated>2010-11-18T11:07:57.295+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='src'/><title type='text'>Анимированный виджет на Android. Можно или нельзя?</title><content type='html'>Можно ли на Android сделать анимированный виджет? Можно, но есть серьезные ограничения. Давайте разберемся.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;Android SDK &lt;a href="http://stackoverflow.com/questions/2964767/is-it-possible-to-animate-a-android-widget-i-create-in-the-same-way-the-universal"&gt;не предоставляет&lt;/a&gt; средств для создания анимированных виджетов. Возможность периодически обновлять содержимое виджита конечно же &lt;a href="http://stackoverflow.com/questions/3872267/how-to-update-widget-every-minute"&gt;есть&lt;/a&gt;. Достаточно &lt;a href="http://stackoverflow.com/questions/3872267/how-to-update-widget-every-minute"&gt;время от времени&lt;/a&gt; вызывать что-то вроде:&lt;br /&gt;&lt;pre class="brush:java"&gt;RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.AAA);&lt;br /&gt;//... обновляем remoteViews ...&lt;br /&gt;appWidgetManager.updateAppWidget(appWidgetId, remoteViews);&lt;br /&gt;&lt;/pre&gt;и содержимое виджита будет изменяться. Но ни о какой анимации здесь речи не идет. &lt;code&gt;RemoteViews&lt;/code&gt; &lt;a href="http://stackoverflow.com/questions/4118233/android-widgets-animations-on-remoteviews"&gt;не позволяет&lt;/a&gt; сопоставить контролу анимацию - только картинку. А самостоятельно вызывать обновление виджета хотя бы несколько раз в секунду (для создания минимального эффекта анимации) &lt;a href="http://stackoverflow.com/questions/2898632/frequent-android-widget-updates"&gt;не получится&lt;/a&gt;: слишком высоки будут накладные расходы и слишком быстро будет садится батарея. &lt;br /&gt;&lt;br /&gt;Однако, кое что сделать можно. В частности, поставить альтернативный десктоп, поддерживающий анимированные виджеты. Например, бесплатный &lt;a href="http://code.google.com/p/android-launcher-plus/"&gt;Launcher Plus&lt;/a&gt;. На &lt;a href="http://hpp.intuitit.mobi/for-developers"&gt;сайте разработчика&lt;/a&gt; представлен готовый &lt;a href="http://code.google.com/p/animated-widget-for-hpp/"&gt;пример анимированного виджета&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Но можно обойтись и без альтернативных десктопов. Не так давно в сети появилось приложение &lt;a href="http://androidcommunity.com/forums/f7/animated-widgets-on-with-google-devices-26383/"&gt;FlipClock&lt;/a&gt; - анимированные часы, работающие на стандартном десктопе (разработка mobi.intuitit - именно они делают Launcher Plus). &lt;br /&gt;&lt;br /&gt;Как работает FlipClock? Насколько я могу судить, в нем используется примерно та же идея, что изложена&lt;br /&gt;&lt;a href="http://andytsui.wordpress.com/2010/06/06/animating-android-home-screen-widget/"&gt;здесь&lt;/a&gt;. Суть проста - вы эмулируете анимацию путем регулярного обновления виджита (например, 25 раз в секунду). Анимация в виджете работает лишь короткое время, при наступлении определенного события - при изменении времени, при клике по виджиту и т.д. В результате, можно избежать гигантских накладных расходов. &lt;br /&gt;&lt;br /&gt;К сожалению, в таком подходе создания анимированного виджета есть подводный камень. Причем очень большой. Рассмотрим пример.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Пример анимированного виджета&lt;/b&gt;&lt;br /&gt;Создадим простой виджет TestAnimatedWidget размером 2x2. На виджете будет изображено колесо. При нажатии на виджет колесо крутится - совершает один оборот. Анимация колеса состоит из 11 кадров. &lt;br /&gt;&lt;br /&gt;Код для такого виджета будет выглядеть примерно так:&lt;pre class="brush:java"&gt;package com.rammus.taw;&lt;br /&gt;&lt;br /&gt;import java.util.HashMap;&lt;br /&gt;import java.util.Timer;&lt;br /&gt;import java.util.TimerTask;&lt;br /&gt;&lt;br /&gt;import android.app.PendingIntent;&lt;br /&gt;import android.appwidget.AppWidgetManager;&lt;br /&gt;import android.appwidget.AppWidgetProvider;&lt;br /&gt;import android.content.Context;&lt;br /&gt;import android.content.Intent;&lt;br /&gt;import android.graphics.Bitmap;&lt;br /&gt;import android.graphics.Canvas;&lt;br /&gt;import android.net.Uri;&lt;br /&gt;import android.util.Log;&lt;br /&gt;import android.widget.RemoteViews;&lt;br /&gt;&lt;br /&gt;public class TestAnimatedWidgetProvider extends AppWidgetProvider{  &lt;br /&gt;  //Флаг указывающий каким образом анимировать виджит. У нас два варианта:&lt;br /&gt;  //1) false - передавать в remote view идентификатор очередного фрейма анимации&lt;br /&gt;  //2) true - передавать в remote view сгенерированную битмапку. Битмапка передается та же - мы загружаем ее из ресурсов (см. WidgetInstanceContent)&lt;br /&gt;  //вариант 1) позволяет устанавливать большие FPS. &lt;br /&gt;  //вариант 2) безбожно тормозит и сыпет ошибками FAILED BINDER TRANSACTION&lt;br /&gt;  private static final boolean USE_GENERATED_BITMAPS = true; &lt;br /&gt;  private static final int FPS = 25; &lt;br /&gt;    &lt;br /&gt;    private static final String LOG_TAG = "com.rammus.TAW";&lt;br /&gt;    private static final String ACTION_WIDGET_CONTROL = "com.rammus.taw.WIDGET_CONTROL";&lt;br /&gt;    private static final String EXTRA_APPWIDGET_ID = "com.rammus.taw.APP_WIDGET_ID";&lt;br /&gt;    private static final String URI_SCHEME = "com.rammus.taw";&lt;br /&gt;    private static HashMap&amp;lt;Integer, WidgetInstanceContent&amp;gt; m_Instances = new HashMap&amp;lt;Integer, WidgetInstanceContent&amp;gt;();&lt;br /&gt;  &lt;br /&gt;    @Override&lt;br /&gt;    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {&lt;br /&gt;       for (int widget_id : appWidgetIds) {          &lt;br /&gt;         RemoteViews remote_view = new RemoteViews(context.get  Name(), R.layout.taw);&lt;br /&gt;                 &lt;br /&gt;          WidgetInstanceContent wic = new WidgetInstanceContent(widget_id&lt;br /&gt;             , appWidgetManager.getAppWidgetInfo(widget_id).minWidth&lt;br /&gt;             , appWidgetManager.getAppWidgetInfo(widget_id).minHeight&lt;br /&gt;             , context);&lt;br /&gt;          &lt;br /&gt;        //make widget clickable&lt;br /&gt;        Intent active = new Intent(context, TestAnimatedWidgetProvider.class);&lt;br /&gt;        active.setAction(ACTION_WIDGET_CONTROL);&lt;br /&gt;        Uri data = Uri.parse(URI_SCHEME + "://widget#");&lt;br /&gt;        data = data.buildUpon()&lt;br /&gt;          .appendQueryParameter("widget_id", String.valueOf(widget_id))&lt;br /&gt;          .build();&lt;br /&gt;        active.setData(data);&lt;br /&gt;        PendingIntent pi = PendingIntent.getBroadcast(context, 0, active, 0);&lt;br /&gt;        remote_view.setOnClickPendingIntent(R.id.bkview, pi);&lt;br /&gt;          &lt;br /&gt;         m_Instances.put(widget_id, wic);         &lt;br /&gt;         update_widget_view(appWidgetManager, widget_id, remote_view);          &lt;br /&gt;     }                  &lt;br /&gt;     super.onUpdate(context, appWidgetManager, appWidgetIds);    &lt;br /&gt;  }   &lt;br /&gt;        &lt;br /&gt;  private WidgetInstanceContent get_instance(int appWidgetId) {&lt;br /&gt;    return m_Instances.get(appWidgetId);&lt;br /&gt;  }    &lt;br /&gt;   &lt;br /&gt;  @Override&lt;br /&gt;    public void onDeleted(Context context, int[] appWidgetIds) {     &lt;br /&gt;        Log.d(LOG_TAG, "onDelete()");&lt;br /&gt;        super.onDeleted(context, appWidgetIds);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    @Override&lt;br /&gt;    public void onReceive(Context context, Intent intent) {&lt;br /&gt;        final String action = intent.getAction();&lt;br /&gt;        if (action.equals(AppWidgetManager.ACTION_APPWIDGET_DELETED)) {&lt;br /&gt;            final int appWidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);&lt;br /&gt;            if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {&lt;br /&gt;        
