Tuesday, August 30, 2011

Правила хорошего тона

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

1. Константы, используемые как имена (а такжы и как возможные значения) для данных, передаваемых через Intent, должны начинаться с INTENT_. Использовать данные из Intent нужно только один раз в методе onCreate(). Все это позволяет программисту сразу видеть, какие данные передаются или могут передаваться в Activity. Мне не нравится, когда данные, передаваемые фактически как входные аргументы, инициализируются в середине работы Activity непонятно в каком методе.

2. Переопределять методы onCreate(), onResume(), onDestroy() и прочие методы класса Activity следует как protected, вызываться из других классов они не должны. Также не следует вручную обращаться к методам, начинающимся с "on", таким, как onActivityResult() и другим. Я никогда не догадаюсь смотреть Call Hierarchy какого-либо из этих методов.

3. Все свойства (SharedPreferences) приложения должны считываться и записываться через статические методы get-set одного специального класса (который можно назвать, например, Preferences). Так мы сможем, во-первых, всегда видеть, какие в принципе свойства приложения устанавливаются, во-вторых, анализировать в любой момент, откуда какое свойство считывается или записывается.

4. Забудьте про такой xml-атрибут объекта View, как android:onClick. Он нарушает все возможные правила по разделению интерфейса и логики.
Когда я просматриваю код Activity, я ожидаю, что я буду видеть всю логику в этом же файле. Когда я вижу незнакомый метод неочевидного назначения, который не вызывает ни один другой метод, я его со спокойной совестью удалю (или сначала закомментирую на недельку).

5. Подавляющее большинство слушателей (listeners) должны быть реализованы как поля класса с говорящими именами. Если я хочу изменить поведение кнопки button1, то я не хочу пролистывать весь код, чтобы найти место, в котором кнопке назначается какой-то сразу реализованный слушатель. Я хочу пролистать список полей класса, и увидеть что-то вроде button1Listener. Исключения:
1) Логика нескольких слушатетелей достаточно тесно переплетается. В этом случае может быть удобно создать отдельный listener сразу для нескольких элементов (и распознавать элементы через передаваемый аргумент View v).
2) В некоторых случаях слушателем может быть назначено само Activity. На мой взгляд, это уместно, если назначение метода слушателя очевидно внутри Activity. Например, когда весь Activity фактически представляет из себя ListView, то реализация самим Activity интерфейсов OnItemClickListener или OnItemLongClickListener вполне логична. Назначать же какой-нибудь OnSeekBarChangeListener для какого-то ползунка в углу экрана на целое Activity смысла не имеет и путает программиста.

6. Если обращение к какому-то View-элементу происходит из Activity не один раз, то его нужно выносить в поле класса с инициализацией и назначением всех слушателей в onCreate() (или отдельном методе, например, initUI(), вызываемом из onCreate()) сразу после setContentView(). Так можно сразу увидеть, какие графические элементы активны, т. е. связаны с логикой приложения, а какие - нет. findViewById(int) внутри произвольного метода недопустимо.

7. Все создаваемые диалоги должны храниться в полях класса, а в методе onDestroy() для каждого диалога должен вызываться метод dismiss(). Примерно так:

@Override
protected void onDestroy() {
    if (progressDialogGettingData != null) {
        progressDialogGettingData.dismiss();
        progressDialogGettingData = null;
    }
    super.onDestroy();
}

Иначе, если Activity по каким-то причинам закрылось (например, при перевороте девайса), а диалог нет, и через несколько секунд поток, работающий с этим диалогом, вместе с завершением работы попытается закрыть и диалог, то приложение ждет force close. Мелочь, а неприятно. Конечно, в файле манифеста у Activity может быть установлена одна ориентация, или запрет закрытия Activity при перевороте, тогда закрыть Activity при открытом not cancellable диалоге будет затруднительно, но точно сказать, как поменяется манифест, нельзя, а такая конструкция в любом случае нам никак не повредит.

8. Элементы раскладки, не контролируемые из кода, не должны иметь аттрибута android:id вовсе. Это позволит легко, глядя на xml-файл, различать активные и пассивные элементы.

9. Не писать в лог в геттерах, а также в циклах. Если очень надо, то ставить минимальный уровень приоритета (verbose), чтобы кучу дежурных сообщений можно было легко отфильтровать. Когда нужно выловить действительно важное сообщение, а лог пополняется сообщениями со стандартным приоритетом с завидной скоростью - это действительно раздражает.

No comments:

Post a Comment