Development for Android
December 2009 28

Спокойной ночи!

image Около месяца назад я приобрел HTC Hero. Основной причиной, по которой был выбран этот телефон, а не iPhone, была возможность полноценно разрабатывать приложения под Windows.

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

Техзадание


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

Как раз для этого и нужна программа. Она отключает все оповещения в заданный отрезок времени.

Среда разработки


Eclipse — практически единственное полноценное средство для разработки под Android. Для работы я выбрал Eclipse Classic 3.5.1.

Также вам понадобится Android SDK. Его можно найти на странице загрузки. Там же есть краткое руководство по установке.

С чего начать


Для меня разработка под Java и Android — совершенно новое занятие. Поэтому первым делом я внимательно изучил уроки, которые представлены в разделе Resources.

Также в разделе References вы найдете объемный справочник как по Andriod SDK, так и по стандартным java-пакетам. Единственное чего там не хватает, так это примеров использования. В результате сперва находишь нужный класс, а потом с помощью Google ищешь то, как им пользоваться в контексте приложения.

Интерфейс пользователя
image

Разработка интерфейса пользователя происходит во встроенном редакторе и понятна каждому, кто сталкивался с конструкторами форм:
image

Советы по проектированию интерфейсов для Android можно прочесть в специальном разделе Dev Guide. Там же находятся рекомендации по проектированию иконок:
image

Мне с трудом удалось вписать свой прямоугольник в перспективу, требуемую для иконок. Как делать что-то более сложное я не представляю. Тем не менее там есть набор шаблонов для Photoshop и Illustrator, который значительно облегчает создание иконок.

Хранение пользовательских настроек


Для работы с пользовательскими настройками в Android существует класс PreferenceManager. Метод getSharedPreferences() принимает в качестве первого аргумента имя набора настроек, а в качестве второго — режим создания (закрытый, открытый для чтения или открытый для записи). В ответ мы получаем объект класса SharedPreferences, который позволяет записывать и считывать настройки. Если такого набора нет, то он будет создан:
SharedPreferences preferences = getSharedPreferences("goodnight", MODE_PRIVATE);

Для чтения различных типов данных специальные методы. Первый аргумент — название поля, а второй — значение по умолчанию:
preferences.getBoolean("isEnabled", false)
preferences.getInt("startHour", 23)

Для записи необходимо получить объект SharedPreferences.Editor с помощью метода edit(), а в конце вызвать метод commit():
Editor prefEditor =preferences.edit();
prefEditor.putBoolean("isEnabled",isEnabledCheckBox.isChecked());
prefEditor.putInt("startHour", startTimePicker.getCurrentHour());
prefEditor.putInt("startMinute", startTimePicker.getCurrentMinute());
prefEditor.putInt("endHour", endTimePicker.getCurrentHour());
prefEditor.putInt("endMinute", endTimePicker.getCurrentMinute());
prefEditor.commit();

Сервисы


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

Для создания сервиса необходимо создать собственный класс, унаследованный от android.app.Service и переопределяющий метод onBind():

public class GoodnightService extends Service {
  @Override
  public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    return null;
  }
}

Также могут пригодиться методы onCreate() и onDestroy():
@Override
  public void onCreate() {
    super.onCreate();
    Log.i("GoodnightService", "Service created");
  }
@Override
  public void onDestroy() {
    super.onDestroy();
    Log.i("GoodnightService", "Service destroyed");
  }

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

Для этого необходимо создать файл с расширением .aidl и в нем прописать интерфейс:
interface IGoodnightService {
  void UpdateSettings();
}

Теперь необходимо скомпилировать проект для того, чтобы сгенерировать из файла .aidl IDL интерфейс. После компиляции можно обновить метод onBind() и создать новое свойство binder:
  @Override
  public IBinder onBind(Intent intent) {
    return binder;
  }

private final IGoodnightService.Stub binder = new IGoodnightService.Stub() {
    @Override
    public void UpdateSettings() {
      Init();
      Update();
    }
  };

Если у вас нет метода Stub(), то скорее всего файл с интерфейсом имеет расширение .java, а не .aidl.

Еще необходимо добавить ваш сервис в манифест приложения AndroidManifest.xml:
image

Сервисы на стороне клиента

Чтобы использовать созданный интерфейс, при нажатии на кнопку «Применить», необходимо выполнить несколько простых действий. Во-первых нам понадобится объект с типом нашего интерфейса:
private IGoodnightService service;

Во-вторых, необходимо создать своиство типа ServiceConnection, которое отслеживает состояние сервиса и необходимо для связывания с ним интерфейса:
private ServiceConnection svcConn=new ServiceConnection() {
    public void onServiceConnected(ComponentName className,
    IBinder binder) {
      service=IGoodnightService.Stub.asInterface(binder);
   
    }
    public void onServiceDisconnected(ComponentName className) {
    service=null;
    }
    };


И, наконец, нужно связать сервис с нашим приложением с помощью метода bindService():
bindService(new Intent(this,GoodnightService.class), svcConn, BIND_AUTO_CREATE);

Теперь можно смело обращаться к методам, описанным в IGoodnightService:
try {
  service.UpdateTimers();
} catch (RemoteException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

Оповещения


Для того, чтобы хоть как-то показать работоспособность сервиса я воспользовался механизмом оповещений. Это может быть вибрация, звуковые сигналы или маленькая иконка в строке состояния (вторая слева):
image

Если открыть строку состояния, то можно увидеть оповещение в более развернутом виде:
image

Текстовые оповещения бывают двух видов: сообщающие о новых событиях (например, сообщение в твиттере :-)) или о том, что программа просто работает.

Оповещения первого типа можно удалить из списка, нажав на кнопку «Очистить уведомления». Сперва я делал их каждую минуту для того, чтобы они не пропадали. Практика показала, что спустя 5-6 часов все начинало дико тормозить, а оповещения больше не обновлялись. :-) После чего был найден второй способ.

Для создания оповещений нам понадобится NotificationManager, который можно получить с помощью метода getSystemService():
final NotificationManager mgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);


Теперь нужно создать само оповещение (Notification):
Notification note = new Notification(R.drawable.status,
    getResources().getString(R.string.startMessage),
    System.currentTimeMillis());
note.flags |= Notification.FLAG_ONGOING_EVENT;
PendingIntent i = PendingIntent.getActivity(this, 0,
    new Intent(this, Setup.class), 0);
String interval = String.format("%d:%02d – %d:%02d", startHour,
    startMinute, endHour, endMinute);

note.setLatestEventInfo(this, interval,
    getResources().getString(R.string.notificationMessage), i);

Здесь PendingIntent — это ссылка на приложение, которое будет запущено после щелчка по уведомлению. А Notification.FLAG_ONGOING_EVENT задает второй тип оповещений.

Для отправки оповещения используется метод notify():
mgr.notify(NOTIFY_ME_ID, note);

Для того, чтобы удалить оповещение из строки состояния нужно вызвать метод cancel():
mgr.cancel(NOTIFY_ME_ID);


Выключение звука


Для того, чтобы выключить или включить звук, нужно получить AudioManager:
AudioManager manager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

И установить соответствующий режим с помощью setRingerMode():
manager.setRingerMode(AudioManager.RINGER_MODE_SILENT);


Финальная сборка приложения


Для того, чтобы собрать приложение в работающий apk-контейнер, нужно выбрать проект, из контекстного меню запустить мастер экспорта, расположенный в Android Tools, и сделать все, что он просит:
image
image
Подробнее об этом написано в разделе Publishing.

Микро-заключение


Писать приложения для Android не так просто, как расширения для Chrome. Тем не менее в этом нет ничего сверхъестественно сложного. :-) Главное начать.

Ссылка на приложение


Бонус для тех, кто дочитал:
image
http://x.product-studio.ru/Goodnight.apk

Буду рад услышать критику, пожелания и вопросы.
+113
11.5k 179
Comments 72
Top of the day