Летом друг подкинул пару заказов по разработке для Android. Первое это streaming проигрыватель видео для одного французского телевидения, второе — простенькая игрушка.
Во время разработки (мой первый опыт разработки на Android и на Java), я уяснил несколько правил, которые нужно соблюдать для корректной и устойчивой работы программ, которыми хочу поделится…
Возможно для профессионалов статья будет не интересной, но для новичков, которые впервые столкнулись с разработкой под мобильные устройства, информация будет полезной.
Примеры
Для наглядности я создал небольшой проект, в котором приведено несколько примеров к правилам ниже.
Исходники: fileshare.in.ua/3050399
APK: fileshare.in.ua/3050402
Правило 1. Не выполняйте сложные операции в UI потоке
Выполнение каких-либо сложных операция в UI потоке приведет к тому что UI будет недоступен пользователю (т. е. будет «висеть»). При этом любые действия пользователя, во время таких «подвисаний», могут привести к появлению сообщения о том что приложение зависло, с предложением закрыть его.
Поэтому все сложные вычисления стоит выносить в отдельный поток.
На примере:
Посмотреть как не нужно делать, и к чему это приводит можно нажав кнопку «Do all on UI thread». В момент нажатия, происходит создание дочернего Activity у которого в методе onCreate() стоит псевдо-сложная операция, которая занимает 20 секунд. После нажатия, при попытке нажать кнопку Back телефона, выскакивает сообщение о том что приложение «подвисло».
При нажатии кнопки «Do hard operation with separate thread» основного окна, произойдет создание Activity и потока для выполнения сложной операции.
Правило 2.Используйте адаптеры правильно
При работе с нестандартными списками, создание строк происходит по средствам скрытого вызова метода getView() адаптера, установленного для данного списка.
В getView() адаптера, разработчику предоставляется возможность создать строку любого вида. Например:
Пример:
Как не надо делать:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View view = mInflater.inflate(R.layout.row, null);
TextView tv_programm = (TextView) view.findViewById(R.id.channel_proramm);
TextView tv_starttime = (TextView) view.findViewById(R.id.time_start);
TextView tv_endtime = (TextView) view.findViewById(R.id.time_end);
ImageView ib_logo = (ImageView) view.findViewById(R.id.channel_logo);
ProgressBar pb_progress = (ProgressBar) view.findViewById(R.id.programm_progress);
tv_programm.setFocusable(false);
tv_starttime.setFocusable(false);
tv_endtime.setFocusable(false);
ib_logo.setFocusable(false);
pb_progress.setFocusable(false);
ib_logo.setClickable(false);
final String name = mNames[position];
if ( name != null ) {
tv_programm.setText(name);
}
return view;
}
* This source code was highlighted with Source Code Highlighter.
Тут для каждой строки списка вызывается создание нового объекта View. В результате, при перелистывании списка, мы получим засорение памяти объектами которые не отображаются. И следовательно Garbage Collector будет вызываться чаще (см. правило 3).
Как правильно делать:
private class ViewHolder {
TextView tv_programm;
TextView tv_starttime;
TextView tv_endtime;
ImageView ib_logo;
ProgressBar pb_progress;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if ( convertView == null ) {
convertView = mInflater.inflate(R.layout.row, null);
holder = new ViewHolder();
holder.tv_programm = (TextView) convertView.findViewById(R.id.channel_proramm);
holder.tv_starttime = (TextView) convertView.findViewById(R.id.time_start);
holder.tv_endtime = (TextView) convertView.findViewById(R.id.time_end);
holder.ib_logo = (ImageView) convertView.findViewById(R.id.channel_logo);
holder.pb_progress = (ProgressBar) convertView.findViewById(R.id.programm_progress);
holder.tv_programm.setFocusable(false);
holder.tv_starttime.setFocusable(false);
holder.tv_endtime.setFocusable(false);
holder.ib_logo.setFocusable(false);
holder.pb_progress.setFocusable(false);
holder.ib_logo.setClickable(false);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final String name = mNames[position];
if ( name != null ) {
holder.tv_programm.setText(name);
}
return convertView;
}
* This source code was highlighted with Source Code Highlighter.
В этом примере будет создано только то количество объектов, которое помещается на экране. И для прорисовки новых строк списка, будут использоваться объекты не отображенные в данный момент. Т. е. объекты будут переиспользоваться, без создания новых.
В чем выигрыш?
В примере к правилу 2 (кнопки «Use incorrect list adapter» и «Use correct list adapter»), создается список из 1000 строк.
При запуске, приложения использует 3% общей памяти устройства.
При перелистывании списка с неправильным использованием адаптера, необходимо дополнительно 14% памяти устройства, что бы пролистать список до последнего элемента. При этом прорисовка происходит дерганно.
Если использовать адаптер правильно, дополнительно используется <=1% и перелистывание проходит плавно.
Правило 3. Поменьше выделений памяти (Java и Garbage Collector)
Это правило наверное знакомо каждому разработчику Java. Но т. к. я раньше программировал на C++, Garbage Collector (aka GC) был для меня в новинку.
Чем чаще происходит создание и удаление объектов, тем чаще вызывается GC. А каждый вызов GC занимает 100-200мс. При этом все потоки на время выполнения GC останавливаются. При этом прорисовка кадров может быть заметна (порой <10 fps).
Заключение:
Это только малая часть того что мне удалось почерпнуть, пока работал с Android, и если тема кому-то будет интересна, буду писать следующую порцию советов и трюков.
Полезные ссылки:
android-developers.blogspot.com — блог разработчиков Android
developer.android.com/videos/index.html — видео о разработке под Android
Особенно советую посмотреть:
Google I/O 2009 — Make your Android UI Fast and Efficient. — Правило 2 взято отсюда.
Google I/O 2009 — Writing Real-Time Games for Android. — Правило 3.
UPD: спасибо за карму, перенес в Android
UPD2: спасибо хабрапользователю i_home за еще одну полезную ссылку small-coding.blogspot.com в комментариях