Monday, April 2, 2012

Секретные USSD коды Android

Внимание! Нижеприведенные ссылки работают только при запуске с телефона !


*#*#4636#*#* - Просмотр технической информации о телефоне, батарее, wifi, а также статистике использования.

*#*#225#*#* - Информация из календаря.

*#06# - IMEI телефона.

Thursday, February 9, 2012

Просмотр исходников Android в Eclipse

Всем известно что Android - это платформа с открытым кодом. Как скачать исходники из репозитория я уже писал в отдельной статье. Сегодня я хочу описать простой способ с помощью которого можно просматривать исходный код платформы в Eclipse IDE.

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

Итак, если исходники ещё не подключены к среде разработки, то при попытке открыть описание любого класса из Android SDK (Open Declaration - F3)


вы увидите сообщение "Source not found" приблизительно вот такого содержания:

Самый простой способ подключить исходники - это установить плагин "Android Source" для Eclipse. Актуальную ссылку на апдейт сайт можно найти на Google Code страничке проекта. Обратите внимание, что вам нужен именно Android Sources плагин, поскольку там их несколько.

Теперь берём эту ссылку (сейчас она вот такая http://adt-addons.googlecode.com/svn/trunk/source/com.android.ide.eclipse.source.update/) и добавляем плагин. В Eclipse Indigo это можно сделать через Help > Install New Software.


После установки плагина мы с радостью видим исходный код Android SDK. На текущий момент поддерживаются все версии с 1.5 по 4.0.3. Пользуйтесь - это весьма удобно !

Friday, February 3, 2012

Установка Android 4 на Amazon Kindle Fire

Вчера приятель мне привёз Kindle Fire - бюджетный планшетник от Amazon. В штатах он стоит всего две сотни долларов. По умолчанию на устройстве установлена модифицированная сборка Android 2.3 и нет привычных мне Android Market, Gmail, Google Maps, Google Talk и других приложений от Google. Но, зато есть Amazon Store. На нём можно найти некоторые полезные приложения, но далеко не все. Например, там нет Skype. Также изменения коснулись стандартного интерфейса Android, который был переделан и теперь напоминает книжный шкаф.


Полюбовавшись прелестями нового планшета, я решил на него установить недавно появившуюся прошивку Android 4.0.3. Поискав на просторах интренета, я нашёл несколько подробных инструкций как это сделать. Вот основная статья, которая мне пригодилась. Всё достаточно подробно расписано, поэтому не буду повторяться. Буквально два слова. Установка выполняется с помощью утилиты Kindle Fire Utility v0.9.2, которая запускается на компе и скидывает на мобильное устройство приложение для перепрошивки. У текущей версии утилиты есть один недостаток - она не работает для обновления Kindle Fire 6.2.2.

Потратив некоторе время на изучение мат. части я выяснил причину. Она заключалась в том что не получалось зарутить (root) устройство. Для того чтобы это сделать утилита Kindle Fire использует приложение BurritoRoot. Видимо разработчики Kindle Fire Utility ещё не успели добавить последнюю версию этого приложения где была реализована поддержка Kindle Fire 6.2.2.
Поэтому, всё что мне потребовалось сделать - это установить вручную BurritoRoot3. Для этого нужно скачать последнюю версию .apk и выполнить несколько команд:

adb shell chmod 777 /data/local/tmp
adb install BurritoRoot3.apk
adb shell /data/local/tmp/BurritoRoot3.bin --root

После чего Kindle Fire Utility определит что устройство действительно зарутовано:

Остальные шаги установки я выполнил строго следуя инструкции и в течении 15 минут смог полюбоваться обновлённым интерфейсом Android 4.0.3 (CyanogenMod 9)


Люди пишут что эта прошивка ещё сыровата, однако особых проблем я пока не обнаружил. Может быть единственное - это не работает камера, но и то лишь потому у Kindle её вообще нет )

Friday, May 20, 2011

Импорт базы данных с сервера на Andoird

Эх, что-то давно я у себя в блоге ничего не писал...
Пора бы уже и поделиться чем-нибудь свеженьким.

В общем задача ( для меня свежая, для кого-то может и не очень ) :
а) есть сервер с базой данных
б) мобильное приложение, которое должно кэшировать часть серверной базы данных
в) доступ к данным возможен через веб сервис в виде csv / xml / json
Требуется реализовать импорт базы данных наиболее эфективным способом, поскольку данных достаточно много.

Итак, поехали ...

Первое с чего я решил начать, это выборка данных через веб сервис. В каком виде брать данные из веб сервиса ? Долго не думая над этим вопросо я решил пройти по протоптоной дорожке и вытаскивать данные в JSON. Почему ? Всё очень просто. Во-первых, Android SDK имеет встроенную поддержку JSON с помощью org.json библиотеки. Во-вторых, работать с JSON, как по мне, несколько удобнее чем с XML или CSV. И в третьих он компактнее чем XML, хотя и не настолько компактный как CSV.

В общем первая реализация не заставила себя долго ждать и получилось что-то вроде:
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(url.toString());
HttpResponse response = client.execute(request);

// Pull content stream from response
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();

ByteArrayOutputStream content = new ByteArrayOutputStream();

// Read response into a buffered stream
int readBytes = 0;
byte[] buffer = new byte[BUFFER_SIZE];
while ((readBytes = inputStream.read(buffer)) != -1) {
  content.write(buffer, 0, readBytes);
}  

String plain = new String(content.toByteArray());
JSONArray data = new JSONArray(plain);
Отточеная до автоматизма тривиальность не предвещала никаких сложностей. Но увы, как это часто бывает всё не так просто как кажется на первый взгляд.

Пробный запуск и приложение вылетает с OutofMemoryError. Оказалось, что данных сервер выдаёт порядка 4,3Мб и при очередном копировании данных в памяти виртуальная машина выкидывает исключение. На каком именно копировании я не разбирался. Всего их получается три inputStream -> content, content -> plaint, plain -> data и в памяти в один момент могут держаться все четыре копии данных, что порядка 16Мб. Для меня всё же остаётся не совсем ясным почему возникает это исключение, поскольку на моём устройстве 576 Мб оперативки из которых 256 Мб обычно свободны. Ну да ладно. Зато это послужило хорошим поводом чтобы разобраться.

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

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

Попробовал - заработало. Ура !

Самое время заняться локальной базой данных и её наполнением. Вот примерный код:
for (int i = 0; i < response.length(); i++) {
  JSONObject item = response.getJSONObject(i).getJSONObject("item");
  Point point = Point.fromJson(item);
  ContentValues values = new ContentValues();
  values.put(KEY_POINT_ID, object.getId());
  values.put(KEY_POINT_NAME, object.getName());
  values.put(KEY_POINT_LAT, object.getLat());
  values.put(KEY_POINT_LON, object.getLon());
  values.put(KEY_POINT_TYPE, object.getType());
  values.put(KEY_POINT_UPDATE_TIME, object.getUpdateTime());
  db.insert(TABLE_POINT, null, values);
}
Запускаем - замечательно, всё работает как в сказке с первой попытки. Только вот как-то долго... Прошло минуты две, а обработаны меньше четверти данных... Вряд-ли у пользователей хватит терпения ждать 10 минут, пока загрузится приложение. Да и как-то не гуманно это что-ли, так что начинаем анализировать на что тратится так много времени. Для этого под Android есть специальная тулза traceview и Debug.startMethodTracing / Debug.stopMethodTracing в помощь. Важный момент - размер .trace - файла по умолчанию 8 Мб, что исчерпывается быстро. Поэтому я установил его побольше и ограничить импорт выборкой одной страницы в 582 записи. Оборачиваем интересующий нас код вот так:
Debug.startMethodTracing("myapp", 20 * 1024 * 1024);
// ... сдесь идёт код импорта данных ...
Debug.stopMethodTracing();
Снова запускаем и по завершению выполнения находим myapp.trace в корне /sdcard на телефоне. Теперь для анализа "сырых" данных из этого файла запускаем:
traceview <путь-к-папке>/myapp
Из таблички со статистикой видно что 77,5% времени "сжирает" операция вставки данных в таблицу. Затем 17,3% тратится на выборку данных с сервера из которых 14,4% от общего времени - это разборка JSON строк в объекты. Я решил начать оптимизировать с малого, а именно с разборки JSON. Первая мысль, которая мне пришла в голову - это использовать JSON Streaming. JSON Streaming - это аналог XML Pull парсера для JSON. Идея этого подхода заключается в том, что структура документа не хранится целиком в памяти, а вместо этого парсер сообщает о синтаксическом разборе каждого элемента в отдельности. Поскольку этот подход не поддерживается стандартной библиотекой org.json, я воспользовался альтернативой - Jackson JSON. Вот приблизительно как выглядел код после того как я его переписал:
do {
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(url.toString());
HttpResponse response = client.execute(request);

// Pull content stream from response
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();

JsonFactory f = new JsonFactory();
JsonParser jp = f.createJsonParser(inputStream);
if (jp.nextToken() == JsonToken.VALUE_NULL) {
  break;
}

while (jp.nextToken() != JsonToken.END_ARRAY) {
  Point point = null;

  while (jp.nextToken() != JsonToken.END_OBJECT) {
    String fieldname = jp.getCurrentName();
    jp.nextToken(); // move to value, or
    // START_OBJECT/START_ARRAY
    if ("item".equals(fieldname)) { // contains an object
      point = pointFromJson(jp);
    } else {
      throw new IllegalStateException("Unrecognized field '" + fieldname + "'!");
    }
  }

  if (point != null) {
    db.getPointDao().create(point);
  }
}
jp.close();
А, вот как изменилась статистика:
После трёх замеров, получилось что разборка JSON ускорилась всего на 10-15%. Немного, но кроме этого данные не копируются в памяти несколько раз, поскольку пыборка происходит непосредственно из потока с http-ответом. Оптимизируем дальше. Почему JSON ? На самом деле в этой задаче нет особого смысла использовать JSON, поскольку данные "плоские" и не имеют никакой вложенности. Я решил ещё раз переписать вытягивание данных и на этот раз использовать CSV, поскольку он самый экономичный по размеру. А значит и по скорости приложение должно ощутимо выиграть, поскольку меньше данных будет скачиваться по сети. Итак, вместо 4,3Мб в CSV формате получилось 2,7 Мб что меншье на 37%. Код приводить не буду, он достаточно тривиальный. Скажу только что для работы с csv лучше всего использовать готовую библиотеку opencsv. Что же получилось теперь ? Прирост получился невелик - всего 5%-10%, но это потому что у меня слишком быстрый интернет. В реальных условиях работы через 3G импорт данных будет работать значительно быстрее после таких изменений. Ведь всё-таки почти на 40% меньше данных скачивается. Теперь, когда передача данных рабоатает оптимально, можно заняться оптимизацией вставки объектов в базу данных. Когда выполняется много однотипных операций вставки, то имеет смысл обернуть их в одну транзакцию. Таким образом будет во множество раз меньше обращений к файловой системе для физического сохранения данных, что сэкономит огромное количество времени. Это раз. И вторая, менее значительная оптимизация - использование откомпилированых запросов на вставку. Это два. Улучшенный код вставки данных:
// Prepare insert statement.
SQLiteStatement insert = db.compileStatement(INSERT_STATEMENT); 
db.beginTransaction();

try {
  while (scanner.hasNextLine()) {
    String line = scanner.nextLine();

    String[] columns = line.split(";");

    try {
      if (columns.length == 7) {
        insert.clearBindings();

        // Important ! Order of the fields in the statement
        // should be the same like in CSV input.
        for (int i = 0; i < 7; i++) {
          insert.bindString(i + 1, columns[i]);
        }

        insert.execute();
        result++;
      }
    } catch (Exception e) {
      Log.e("AppTrack", Log.getStackTraceString(e));
    }
  }

  db.setTransactionSuccessful();
} finally {
  db.endTransaction();
}

Итак, после всех-всех оптимизаций импорт данных занимает менее 1,5 мин вместо изначальных 10 мин. Кроме этого данные не копируются несколько раз в памяти, что существенно снижает нагрузку на сборщик мусора и виртуальную машину.

Вот такой небольой case study. Надеюсь кому-нибудь пригодится. Если что - пишите в коменты.

Monday, March 21, 2011

Нагрузочное тестирование веб сервиса с помощью soapUI

Есть такая полезная штука soapUI. Пользуюсь ей давно, но, в основном, только для того чтобы быстро проверить отдельные методы веб сервисов. Сегодня понадобилось провести нагрузочное тестирование веб сервиса авторизации, который я недавно написал.
Задача как бы тривиальная, но, вот самое сложное в такого рода задачах - это выбрать инструмент. Итак приступаем. После получаса общения с поисковой системой у меня насобирался списочек различного рода утилит. И первую, которую я решил попробовать – это младший брат soapUI, под названием loadUI. В общем, я эту штуку скачал, установил, запустил, немного подождал, увидел пользовательский интерфейс и понял что это не то что мне нужно. Интерфейс, не ужасный, очень даже красивый, и, наверное, даже слишком для такой утилиты, просто я его не понял. Половина экрана занимает раздел с устаревшими новостями и всё как-то уж очень медленно работает. Закрыв его подальше, я решил посмотреть нет ли в soapUI того что мне нужно, т.к. где-то краем глаза видел что эти две утилиты интегрируются. И мне повезло - оказывается есть ! И уже давно есть, просто раньше я не обращал внимания на то что когда импортируешь WSDL чудесной галочкой можно скачать чтобы soapUI сгенерировал набор тестов:


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


Вот как выглядит то что получилось в дереве проекта:


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


В моём случае всё достаточно просто: клиент через определённые интервалы времени обращается к методу ApproveSession для подтверждения сессии и в конце работы принудительно закрывает свою сессию с помощью TerminateSession. Всё просто, но есть два нюанса:
1. Необходима поддержка HTTP сессии с помощью Cookies
2. Каждый клиент должен присылать свой уникальный ключ на сервер, который в идеале ещё и должен бы быть закриптован

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


Разобравшись с первым вопросом я приступил ко второму. После получаса рысканья по просторам интернета, нашёл очень полезную статью, которая реально сэкономила мне немного времени, и теперь я с радостью потрачу его на блог :)
Итак, применительно к моему случаю, SOAP запрос на подтверждение сессии выглядит приблизительно следующим образом:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:ApproveSession>
         <tem:token>?</tem:token>
      </tem:ApproveSession>
   </soapenv:Body>
</soapenv:Envelope>

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

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <tem:ApproveSession>
         <tem:token>token${=context.getProperty("ThreadIndex")+1}</tem:token>
      </tem:ApproveSession>
   </soapenv:Body>
</soapenv:Envelope>

Теперь, когда запросы готовы, можно добавить несколько повторов (к сожалению, не знаю как можно организовать цикл в SoapUI, если кто подскажет – буду признателен), задержки между ними и запустить нагрузочный тест:


Итак, в результате запуска теста, получен полезный отчёт. Каждый шаг был выполнен 501 раз (колонка cnt) на протяжении интервала тестирования в одну минуту. Этот интервал я установил перед запуском теста. Кроме того, среднее время отклика сервера в пределах 250 мс (колонка avg), при этом максимальное время отклика порядка 1 сек, а минимальное - 100 мс. Очень неплохо при параллельной работе 500 нагрузочных клиентов. Суммарный объём трафика для всех клиентов менее 1 мб, при этом не было ни одного отказа. В целом результат меня удовлетворил. К сожалению, не получилось сэмулировать большее количество клиентов. При попытке запустить 1000 проходит всего 50 первых запросов а дальше в лог возвращаются ошибки соединения и на короткое время пропадает доступ к интернету. Скорее всего исчерпываются свободные ресурсы на ноутбуке где я проводил тест. На сервер нареканий нет - в логах сервера всё чисто, а счётчики производительности показывают что все запросы были успешно обслужены. Но это уже детали.

Главное, что soapUI всё-таки полезная штука, и в который раз она меня выручает :)

Saturday, February 26, 2011

Я поэт

Есть у меня хорошая идея сделать онлайн бибиотеку поэзии. Вот моя небольшая презентация с Android Hackton, который проходил в Киеве в феврале.

Wednesday, February 23, 2011

Первое приложение на Android Market !

Скажу сразу что это было непросто, но в конце концов я это сделал! И теперь моё первое приложение можно увидеть по ссылке.
Надеюсь в ближайшее поделиться как это было :)

Friday, January 28, 2011

Создание инсталляции с помощью WIX и 7-Zip

Специально для этой статьи, я создал простую инсталляцию. Для этого с помощью визарда я создал Wix проект в Visual Studio и добавил файл license.txt, который будет копироваться в ходе инсталяции. Вот содержимое Product.wxs:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="39ef48c3-d7ca-4947-9644-4146e9d6c1ec" Name="WixExeSetup" Language="1033"
           Version="1.0.0.0" Manufacturer="WixExeSetup"
           UpgradeCode="d69d6b38-b736-44e8-98b8-d3fded2545da">
    
    <Package InstallerVersion="200" Compressed="yes" />
    <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
    <UIRef Id="WixUI_Minimal"/>
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLLOCATION" Name="WixExeSetup">
          <Component Id="license.txt" Guid="177b4634-0246-49db-89a9-c3b539ebd93d">
            <File Id="license.txt" Source=".\license.txt" KeyPath="no" />
          </Component>
        </Directory>
      </Directory>
    </Directory>
    <Feature Id="ProductFeature" Title="WixExeSetup" Level="1">
      <ComponentRef Id="license.txt" />
    </Feature>
  </Product>
</Wix>

Если скомпилировать этот проект, то будет получен инсталляционный msi-пакет. Что если я захочу инсталляцию не в виде .msi, а в виде .exe (неважно из каких соображений)? Оказывается, что WIX в текущей версии этого не позволяет. Итак, после пары часов поиска, я пришёл к тому что проще всего поместить .msi файл в самораспаковывающийся (sfx) архив, после запуска которого, содержимый .msi будет распаковываться во временную папку и затем запускаться.

Для создания sfx-архива я использовал 7-Zip 9.20. Я скачал сам архиватор, а также SFX Plugin.

После этого, я установил 7-Zip и распаковал SFX Plugin в папку с 7-Zip. Чтобы была возможность использовать архиватор из командной строки во время компиляции универсальным способом я также добавил путь к папке с 7-Zip в переменную окружения PATH.

Теперь, чтобы в процессе сборки проекта инсталляция упаковывать в sfx архив в post-build action нужно добавить следующую команду:
7z a -y -sfx7zS2.sfx $(TargetName).exe $(TargetFileName)

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

Теперь подробнее о том что происходит, когда запускается полученный .exe файл:

  1. Содержимое архива (.msi в нашем случае) распаковывается во временную директорию
  2. Из временной директории запускается .msi файл
  3. По окончанию инсталляции временная директория со всем содержимым удаляется

Следует заметить, что размер полученного архива в виде .exe файла в несколько раз меньше размера исходной инсталляции. В данном примере .exe файл в 2,5 раза меньше. Хоть это и небыло моей целью, т.к. мне была нужна инсталляция в виде exe-шника, этот "побочный" эффект весьма приятен :)

P.S.
В Wix 3.6 появилась тулза burn, с помощью которой можно создавать загрузчики (bootstrapper) для инсталляционных пакетов. Но, к сожалению, на момент написания статьи, версия Wix 3.6 находится в стадии разработки.

Скачать исходный код

Tuesday, January 18, 2011

Загрузка исходников Android под Windows или как я познакомился Git

Для контроля версий исходного кода Android используется Git. В общих чертах Git - это распределённая система контроля версий, где каждая локальная копия является обособленным репозиторием. Такая архитектура позволяет контролировать локальные изменения, даже если нет связи с удалённым центральным репозиторием.

Сразу оговорюсь что это моё первое знакомство с Git. До этого я работал только с TFS, CVS и преимущественно с SVN.

Что мне больше всего нравится в Android, это то что его код открыт и доступен каждому в сети. Исходный код, рекомендации по работе с ним а также множество связанной информации находится на сайте http://source.android.com. Для ознакомления непосредственно с кодом существует Web-интерфейс, который называется GitWeb. Чтобы его увидеть нужно перейти по ссылке http://android.git.kernel.org. При этом на страничке появится длинный список из репозиториев. Для каждого подпроекта создан отельный репозиторий, поэтому их так много. Такая организация обусловлена тем, что Git умеет работать только со всем репозиторием целиком, но никак не с отдельным его подкаталогами, как это делает SVN. И если бы исходный код Android, а он занимает порядка 2 Gb, хранился в одном репозитории, то даже если нужен только код одного небольшого приложения потребовалось скачать полностью все 2 Gb исходников.

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

На моём рабочем ноутбуке стоит Windows, поэтому чтобы скачать исходники мне понадобилось установить msysgit (Git клиент для Windows) и затем по-порядку:


  1.  Запустить Git Bash из меню Пуск или меню проводника
  2. mkdir c:/android - создать папку для загружаемого проекта
  3. cd c:/android - перейти в эту папку
  4. git clone http://android.googlesource.com/platform/packages/apps/DeskClock - клонирование репозитория с приложением DeskClock

Теперь исходный код приложения DeskClock находится в папке c:/android/DeskClock и я могу работать с ним локально.

Как альтернатива работе с msysgit клентом через командную строку есть возможность использовать GUI и команды контекстного меню из проводника, но мне они показались скудноватыми и не очень интуитивно-понятными. Поэтому я усатновил альтернативную оболочку TortoiseGit, которая очень похож на старый добрый TortoiseSVN и жизнь по-тихоньку начала налаживаться :) Чуть не забыл TortoiseGit обязательно необходимо наличие msysgit, иначе он будет выдавать ошибку что тот не найден.

И напоследок, поскольку репозиториев много, порядка 260, то чтобы скачать весь исходный код Android не очень удобно будет выкачивать их по-одному. Чтобы упростить задачу можно воспользоваться скриптом repo о котором упоминается здесь, но поскольку он написан на Python, то потребуется ещё устанавливать MinGW или что-то в этом духе. Как альтернатива есть обычный batch скрипт, который можно позаимствовать отсюда, однако со временем он может устареть, поскольку содержит захардкоженые адреса репозиториев.

EDIT 16/04/2012: Со времени написания статьи онлайн браузер исходников http://source.android.com перестал поддерживаться проектом Android. Есть по крайней мере три альтернативы быстро посмотреть код:
  1. GrepCode - очень удобный просмотровик кода. Простое переключение по веткам и тегам. Отображение Outline классов, как в Eclipse. Хорошо гуглится. Достаточно вбить GrepCode + <имя класса> в гугл.
  2. Сode Aurora - старый gitweb интерфейс, который хостится Code Aurora mobile open source community
  3. Также можно воспользоваться старым добрым GitHub, где хранится зеркало основных репозиториев

Для скачивания исходников целиком рекомендуется использовать утилиту repo. Как это сделать подробно описано здесь.

Monday, November 29, 2010

Наступая на первые грабли

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

Грабли №1 "No resource identifier found for attribute 'targetSdkVersion' in package 'android'"
AndroidManifest.xml содержит атрибут targetSdkVersion, появившийся только в Android 1.6 в то время как проект собирается для более ранней версии. В моём случае, это была версия 1.5.

Как лечится: Поскольку в моём демо проекте версия SDK не играет принципиальной роли, я просто указал чтобы проект собирался для Android 1.6. ( Project > Properties > Android > Project Build Target = Android 1.6 > Ok ).

Грабли №2 Project '<название проекта>' is missing required source folder: 'gen'
Иногда, при очистке проекта ( Project > Clean ) папка 'gen' удаляется. Причины по которым это происходит иногда, а не всегда, мне пока не ясны, но факт остаётся фактом.
Так вот, эта папка содержит автоматически сгенерированный java класс-обвёртку для доступа к ресурсам. Он генерируется компилятором ресурсов (aka Android Resource Manager) в самом начале сборки проекта лишь в том случае, если ресурсы приложения изменились. Если же ресурсы не менялись, то он не пересоздаётся, и соответственно папка gen, в которой он должен находится также. При этом отсутствие этой папки приводит к указанной ошибке.

Как лечится: Необходимо измените любые ресурсы, например, добавив дополнительный пункт меню, который позже можно удалить ( res > menu > menu.xml > add > item > ctrl + s > ctrl > + b ).

Tuesday, July 20, 2010

Запуск 32-битных ASP.NET веб приложений под IIS x64

По умолчанию, IIS установленный на 64-битной операционной системе не хостит веб приложения, которые скомпилированы под платформу x86. При попытке обратится к странице такого веб приложения вылетает следющее исключение:

Could not load file or assembly 'Web32' or one of its dependencies. An attempt was made to load a program with an incorrect format.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.BadImageFormatException: Could not load file or assembly 'Web32' or one of its dependencies. An attempt was made to load a program with an incorrect format.



Это происходит по причине того что IIS запущен как процесс c 64-битным адресным пространством и не может загрузить 32-битную динамическую библиотеку. Для того чтобы обойти данную проблему нужно выполнить следующий скрипт (Ссылка на источник):


cscript.exe %SystemDrive%\Inetpub\AdminScripts\adsutil.vbs set W3SVC/AppPools/Enable32BitAppOnWin64 true



После того как скрипт отработает, для обслуживания всех сайтов на сервере будeт запускаться 32-битная версия IIS, которая в свою очередь будет исполняться подсистемой эмуляции 32-битных приложений WOW64. Досадно, что эта настройка является глобальной и теперь для всех пулов будут запущенны 32-битные копии w3wp.exe из C:\Windows\SysWOW64\inetsrv\. 
Вообще, чтобы не сталкиваться с данной проблемой, лучше указать Any CPU в настройках проекта. Так не придётся заморачиваться с разрядностью и приложение будет работать как из-под 64-битной версии IIS, так и из 32-битной. Но к сожалению, это не всегда возможно и в крайне редких ситуациях без указания конкретной платформы не обойтись и здесь такое решение просто необходимо. Однако, использовать его нужно крайне осторожно, ведь оно может повлиять на работу других приложений на веб сервере. 


В IIS 7, данная настройка также может указываться для каждого  пула веб приложений отдельно. Сделать это можно либо через командную строку (Ссылка на источник):


%windir%\system32\inetsrv\appcmd set config -section:applicationPools -applicationPoolDefaults.enable32BitAppOnWin64:true


Либо с помощью редактирования Advanced Settings для пула приложений в консоли управления IIS (Ссылка на источник):

Tuesday, November 17, 2009

Версия VC90 runtime

На прошлой неделе я совершенно случайно натолкнулся на то что C++ приложения скомпилированные на Visual Studio 2008 SP1 по умолчанию используют RTM версию VC90 runtime. Как описано в блоге одного из разработчиков VC++ в Visual Studio 2008 изменилась стратегия привязки к верии runtime.

Это могло быть оставлено незамеченным, если бы мы не решили использовать регулярные выражения из TR1 которые появились в VC++ c выходом Visual Studio 2008 SP1.

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

Сначала оказалось, что при инсталяции нашего приложения, мы использовали инсталяционный модуль Microsoft_VC90_CRT_x86.msm предыдущей версии 9.0.21022.8. Но после того как это было исправлено и мы добавили модуль версии 9.0.30729.1 приложение перестало запускаться вовсе. В Event Viewer появились ошибки о том что не найдена сборка Microsoft.VC90.CRT версии 9.0.21022.8. Проблема оказалась в том, что в манифесте приложения указана RTM (9.0.21022.8) версия Runtime компонент.

В результате поиска решения, было найдено два варианта:

  1. Использовать директиву компиляции _BIND_TO_CURRENT_CRT_VERSION. Тогда в манифест попадёт версия того Runtime который используется студией при сборке проекта.
  2. Добавить в инсталяцию модуль policy_9_0_Microsoft_VC90_CRT_x86.msm. В этом случае, приложение будет использовать более новую версию Runtime, по отношению к той что указана в манифесте.

Wednesday, September 30, 2009

Microsoft готовит средства миграции приложений c LINQ to SQL на ADO.NET Entity Framework

Как известно, в последней версии .Net Framerork Microsoft предлагает два ORM решения: LINQ to SQL и ADO.NET Entity Framework. Первое появилось с выходом .Net 3.5 и Visual Studio 2008, в то время как ADO.NET Entity Framework вошёл в .NET 3.5 SP1 с поддержкой в Visual Studio 2008 SP1.

Но, несмотря на то, что ADO.NET Entity Framework появился позже LINQ to SQL и в функциональном плане пока уступает, он развивается более динамично и уже в следующей версии фреймворка будет значительно расширен новыми фитчами.

Более того, Microsoft уже выпустила первую версию шаблонов Visual Studio 2010 для миграции метаданных LINQ to SQL ( .dbml ) в метаданные ADO.NET Entity Framework ( .edmx ). Что подтверждает намерения Microsoft в ближайших версиях отказаться от LINQ to SQL в пользу ADO.NET Entity Framework.

Это вовсе не означает, что в один прекрасный момент, всё то что было написано с использованием LINQ to SQL перестанет работать, однако может оказаться серьёзным препядствием для миграции приложения на новые версии .Net Framerork. С этой точки зрения ADO.NET Entity Framework является более привлекательным для использования в реальных Enterprise проектах.