четверг, 23 февраля 2012 г.

Использование Google Analytics в Android приложении

После того как вы создали и опубликовали своё приложение для Android, наступает приятный и интересный этап: вы наблюдаете, как к вам стекаются первые пользователи. Этот этап редко обходится без открытий и разочарований: клиенты ведут себя совсем не так как ожидалось.
Как же они ведут себя в приложении? Ответ на этот вопрос позволит скорректировать интерфейс и стратегию разработки, иначе подойти к продвижению продукта, если это необходимо. 
Инструментов для сбора статистики в мобильных приложениях достаточно много, но нам после разработки под web привычнее Google Analytics, да и интеграция в приложение у него, как мы увидим сейчас, предельно проста.

четверг, 19 января 2012 г.

Android: загрузка файла из сети с отображением прогресса

Хорошо сделанное Android-приложение (кроме всего прочего) не заставляет клиента угадывать что в данный момент происходит "по ту сторону экрана". Приятное и аккуратное приложение показывает при всех продолжительных операциях прогресс-бар, который реализует, как правило, с помощью класса AsyncTask. Давайте посмотрим как правильно использовать этот замечательный инструмент на примере загрузки файла из сети:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
 
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.TextView;
 
public class BackFLoaderActivity extends Activity {
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Button load = new Button(this);
  load.setText("Load file");
  load.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    downloadFile("http://anjedi.com/api_lib/2.2_level8.jar");
   }
  });
  setContentView(load, new LayoutParams(LayoutParams.WRAP_CONTENT,
    LayoutParams.WRAP_CONTENT));
 }
 

 private void downloadFile(String url) {
  final ProgressDialog progressDialog = new ProgressDialog(this);
 
  new AsyncTask<String, Integer, File>() {
   private Exception m_error = null;
 
   @Override
   protected void onPreExecute() {
    progressDialog.setMessage("Downloading ...");
    progressDialog.setCancelable(false);
    progressDialog.setMax(100);
    progressDialog
      .setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
 
    progressDialog.show();
   }
 
   @Override
   protected File doInBackground(String... params) {
    URL url;
    HttpURLConnection urlConnection;
    InputStream inputStream;
    int totalSize;
    int downloadedSize;
    byte[] buffer;
    int bufferLength;
 
    File file = null;
    FileOutputStream fos = null;
 
    try {
     url = new URL(params[0]);
     urlConnection = (HttpURLConnection) url.openConnection();
 
     urlConnection.setRequestMethod("GET");
     urlConnection.setDoOutput(true);
     urlConnection.connect();
 
     file = File.createTempFile("Mustachify", "download");
     fos = new FileOutputStream(file);
     inputStream = urlConnection.getInputStream();
 
     totalSize = urlConnection.getContentLength();
     downloadedSize = 0;
 
     buffer = new byte[1024];
     bufferLength = 0;
 
     // читаем со входа и пишем в выход, 
     // с каждой итерацией публикуем прогресс
     while ((bufferLength = inputStream.read(buffer)) > 0) {
      fos.write(buffer, 0, bufferLength);
      downloadedSize += bufferLength;
      publishProgress(downloadedSize, totalSize);
     }
 
     fos.close();
     inputStream.close();
 
     return file;
    } catch (MalformedURLException e) {
     e.printStackTrace();
     m_error = e;
    } catch (IOException e) {
     e.printStackTrace();
     m_error = e;
    }
 
    return null;
   }
 
   // обновляем progressDialog
   protected void onProgressUpdate(Integer... values) {
    progressDialog
      .setProgress((int) ((values[0] / (float) values[1]) * 100));
   };
 
   @Override
   protected void onPostExecute(File file) {
    // отображаем сообщение, если возникла ошибка
    if (m_error != null) {
     m_error.printStackTrace();
     return;
    }
    // закрываем прогресс и удаляем временный файл
    progressDialog.hide();
    file.delete();
   }
  }.execute(url);
 }
}

В этом маленьком приложении при нажатии на кнопку запускается загрузка файла, при этом пользователь наблюдает прогресс-бар. Не забудьте добавить в манифест запрос разрешений на доступ к интернет и файловой системе.
Основой примера является метод, который я взял отсюда (и поправил пару ошибок).  
Этот метод принимает url файла, который нужно загрузить, загружает файл, отображая при этом горизонатальный прогресс-бар. При этом прогресс-бар реально показывает какая часть файла в данный момент загружена. По окончании загрузки файл удаляется.

Разберём работу метода подробнее:

Главная часть метода - создание AcyncTask-а и переопределение его методов.
В методе onPreExecute мы запускаем progressDialog, установив предварительно текст сообщения и максимальное значение прогресса: 100%.
В методе doInBackground - выполняем собственно загрузку файла. Файл читаем  из urlConnection порциями по 1024 байт, каждый раз прибавляя размер полученной порции к общему счётчику. Счётчик и общий размер файла передаём при каждой итерации в метод publishProgress, благодаря чему в методе onProgressUpdate мы получаем эти данные и обновляем текущий статус progressDialog-а.
И, наконец, в методе onPostExecute мы прячем диалог и удаляем временный файл.
Особенностью использования AsyncTask-a является способ, как он объявляется и как в него передаются параметры. Типы, которыми параметризуется экземпляр AsyncTask-a определяю по порядку: тип входящего значения, тип параметра, опреляющего прогресс опреации и тип результата фоновой операции (то что возвращает doInBackground и принимает onPostExecute). Кроме того конструктор и чаcть методов AsyncTask-а принимает varargs, т.е. произвольное число параметров, что весьма удобно в некоторых случаях.

понедельник, 16 января 2012 г.

Android: делаем редактор исходного кода с подсветкой синтаксиса

Иногда, когда под рукой только мой верный Android, хочется чего-нибудь почитать. Например, исходники любимого проекта :) Есть минимум десяток приложений, которые отображают файлы исходников с удобной подсветкой синтаксиса, некоторые из них ещё и позволяют редактировать файлы. Вот мне и стало и нтересно, насколько сложно реализуется такой функционал. Оказалось - очень просто.
 
Итак, для приготовления "блокнота с подсветкой синтаксиса" в Android нам понадобится:
  1. Хорошая библиотека для подсветки синтаксиса на JavaScript+CSS. Можно выбрать тут, мне больше всего понравилась CodeMirror, её и будем использовать. 
  2. WebView для интеграции всего этого богатства с нашим приложением
  3. Немного кода, для взаимодействия с WebView и работающим в нём JavaScript.

понедельник, 26 декабря 2011 г.

Java vs. Android

Мобильная платформа Google поддерживает относительно большое множество функционала Java Standard Edition. Но существуют части J2SE, которые по тем или иным соображениям не включены в Android(к примеру, java.applet, javax.print, java.awt, ...).
Итак, что же общего у Андроида и Java(сравнение с версией Java 2 Platform Standard Edition 5.0)?

Общее:

  • java.io - Файловый и потоковый ввод/вывод
  • java.lang (кроме java.lang.management) - Языковая поддержка и поддержка исключений
  • java.math - Большие числа, округление, точность
  • java.net - Сетевой ввод/вывод, URL-ы, сокеты
  • java.nio - Файловый и канальный ввод/вывод
  • java.security - Авторизация, сертификаты, открытые ключи
  • java.sql - Интерфейс баз данных
  • java.text - Форматирование, естественный язык, сопоставление
  • java.util (включай java.util.concurrent) - Lists, maps, sets, массивы, коллекции
  • javax.crypto - Шифры, открытые ключи
  • javax.net - Socket factories, SSL
  • javax.security (кроме javax.security.auth.kerberos, javax.security.auth.spi, и javax.security.sasl)
  • javax.sound - Звуковые и музыкальные эффекты
  • javax.sql (кроме javax.sql.rowset) - Интерфейсы БД
  • javax.xml.parsers - XML парсинг
  • org.w3c.dom (но не суб-пакеты) - DOM ноды и элементы
  • org.xml.sax - Примеры API для XML

Теперь о том, что не поддерживает платформа Android

Отсутствует поддержка следующих вещей:

  • java.applet
  • java.awt
  • java.beans
  • java.lang.management
  • java.rmi
  • javax.accessibility
  • javax.activity
  • javax.imageio
  • javax.management
  • javax.naming
  • javax.print
  • javax.rmi
  • javax.security.auth.kerberos
  • javax.security.auth.spi
  • javax.security.sasl
  • javax.swing
  • javax.transaction
  • javax.xml (кроме javax.xml.parsers)
  • org.ietf.*
  • org.omg.*
  • org.w3c.dom.* (субпакеты)

И наоборот:

В свою очередь, существуют плюшки, которые Андроид включает в себя, но которые отсутствуют в ствндартных поставках Java 2 Platform Standard Edition 5.0(так называемые, third-party libraries). Среди них следующее:

  • org.apache.commons.codec - Утилиты для кодировки и декодировки
  • org.apache.commons.httpclient - HTTP аутентификация, cookies, методы, и протокол
  • org.bluez - поддержка Bluetooth
  • org.json - JavaScript Object Notation

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

Оригинальная статья датирована 14 января 2008 года. Поэтому, данные могут быть не совсем достоверными. Я постараюсь по мере накопления знаний корректировать свой пост. Если что-то не так - не судите строго) Полезная критика приветствуется.

понедельник, 19 декабря 2011 г.

Пишем игру для Android с помощью AndEngine и Physics Box2D Extension

В жизни каждого программиста наступает время, когда ему надоедает писать унылые формы и обработчики и душа просит настоящего творчества.. Например, написать игру.
Давайте посмотрим, как это делается для нашей любимой платформы Android.
Всякая игра базируется на наборе логики, которая реализует поведение игровых объектов на экране, т.н. "графическом движке". В комплексе с этой логикй работает игровая физика, "звуковой" движок, и масса другого, очень непростого кода. Если у нас нет лишних полгода жизни или полмиллиона баксов, лучше не пытаться реализовать эту логику самому. Благо под Android создано уже несколько достаточно мощных "двигателей". Один из них мы сегодня и применим. Это AndEngine - свободно распространяемый 2D игровой движок, базирующийся на OpenGL.
Чтобы было интереснее и ближе к "жизни", мы используем в нашем приложении расширение Physics Box2D, которое позволит нам реализовать в игре гравитацию. Наша игра будет простой но вполне играбильной: мы будем складывать конструкции из деталей, которые будут появляться "из воздуха".
Итак, приступим.

понедельник, 12 декабря 2011 г.

Работаем с очередью RabbitMQ в java


Ничто так не помогает справиться с нагрузкой на web-приложение, как асинхронная обработка. Можете это цитировать :)
Одним из лучших решений для организации очередей сообщений для асинхронной обработки является на сегодня RabbitMQ. Тут я опишу как без особых проблем установить и задействовать в своём java-проекте этот замечательный инструмент.

Итак, устанавливаем:
  1. sudo aptitude install erlang
  2. sudo aptitude install rabbitmq-server
Готово.

Для работы с сервером очередей нам понадобится официальный java-клиент RabbitMQ. Берём его отсюда.
Из архива понадобится "rabbitmq-client.jar" и "commons-io-1.2.jar".

Теперь перед нами стоит классическая задача: реализовать передачу сообщения от одного java-приложения другому. При этом выключение получателя не будет приводить к потере сообщений: пока их не обработают RabbitMQ позаботится о их сохранности. Также мы реализуем многопоточную обработку сообщений получателем. Уверен, что это потребуется, ведь в реальных задачах время обработки сообщения существенно превышает время отправки.
Итак, приступим: