2. Обзор
Руки прочь от UI Thread!
Немного об SQLite
Память и GC:
Нативная куча
Строки/массивы/массивы объектов
Страшные сказки на ночь
Как разобраться с памятью?
Что делать, если всё-таки виноват CPU?
4. "ANR" получает программа, которая
за 5 секунд не ответила на действие
пользователя
или за 10 секунд не закончился
BroadcastReceiver
Но даже раньше этого пользователи
жалуются, что программа "тормозит"
5. "ANR" обычно получает тот, кто
Делает какую-то существенную работу,
Читает или пишет файлы,
Обращается к сети,
или просто общается с базой данных
в основном потоке программы
(Main/UI Thread)
7. Что выполняется "медленно"
Очевидные вещи, вроде увеличения всех
чисел в базе данных на 1, взлома шифра
RSA, и т.д.
Но и менее очевидные:
Запись на флеш: 5-200(!)мс
Сеть: пинг = 100мс--10с--больше (GPRS)
Соединение+HTTP+6k = 1-6с (3G)
Garbage collector run -- ?мс
8. Как отложить работу на потом
android.os.AsyncTask
android.app.IntentService
подробнее:
http://developer.android.com/reference
9. Как показать это пользователю
Пользователь нажал на кнопку, вы
запустили какое-то действие
Вы не знаете, сколько оно продлится
Советы бывалых:
Сразу же выключите кнопку (disable)
Запустите действие и таймер
Если действие не завершилось за
200-500мс, включите progress bar или
какую-нибудь ещё анимацию
10. А в моей программе всё ок?
Android 2.3 (Gingerbread): StrictMode
Следит за вашей программой
Определяет, где вы делаете опасные
вызовы в основном потоке
И наказывает вас!
11. А в моей программе всё ок?
Вставьте что-то вроде этого в начало
программы:
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
И вы получите кучу сообщений в log со
стеком и рассказом, как вы неправы.
12. SQLite
Транзакция обязательно пишет журнал
новый файл, запись, удалить файл
если места мало, скорость падает
катастрофически (с 5мс до 60мс)
Индексы
EXPLAIN, EXPLAIN QUERY PLAN
sqlite-wrapper.pl (by bradfitz)
http://code.google.com/p/zippy-android
13. А нужен ли SQLite?
Пишете лог? Лучше добавлять строчки в
файл
Только читаете? Нужна простая структура?
Подумайте, не обойтись ли простой
структурой в файле
SQLite -- не Oracle, он всё делает
прямолинейно
14. GC/использование памяти
for (Element el: elements) {
Wrapper wrapper = new Wrapper(el);
wrapper.doTask();
// wrapper is deleted
}
Wrapper wrapper = new Wrapper();
for (Element el: elements) {
wrapper.setElement(el);
wrapper.doTask();
// GC is happy!
}
15. Использование памяти
16Мб на процесс (24Мб на N1/Desire)
Это включает себя "нативную кучу", где
хранятся Bitmap
Если памяти остаётся мало, GC начинает
включаться чаще
Если пытаться занять больше, получите
OOME
16. Нативная куча
Bitmap bitmap = ...(1024x768 pixels, 24bpp);
Проблема: с точки зрения GC объект bitmap
занимает ~15байт, поэтому, когда мало
памяти, его удалять нет особого смысла.
Однако: bitmap отъедает 2.3Мб от тех 24Мб,
которые выделены процессу
17. Нативная куча
Поэтому не полагайтесь на GC:
Bitmap bitmap = ...(1024x768, 24bpp);
... use it ...
... when you don't need it any more:
bitmap.recycle();
18. Сказки у камина (aka joys of no JIT)
class Item {
private int x, y;
public int getX() { return x; }
public int getY() { return y; }
}
List<Item> array = new ArrayList<Item>(10^9);
Это
создаёт огромное множество объектов Item
на куче
с оверхедом в два раза
20. Сказки у камина (aka joys of no JIT)
Обращение к элементу происходит так:
array.get(55).getX()
1. Находим в объекте array виртуальную
таблицу, соответствующую интерфейсу List
2. Отыскиваем ней метод get
3. Вызываем его
4. Он проверяет границы...
21. Сказки у камина (aka joys of no JIT)
array.get(55).getX()
5. Находит 55-ый элемент массива
6. В таблице виртуальных методов Item ищет
getX()
7. Метод getX() прибавляет смещение к
началу объекта, и...
8. Возвращает нам x!
22. Сказки у камина (aka joys of no JIT)
class ItemArray {
private int[] xx;
private int[] yy;
public final int getX(int idx) {...}
public final int getY(int idx) {...}
}
Нет оверхеда по памяти
Вызов гораздо быстрее (одно обращение к
массиву с проверкой границ)
23. Сказки у камина (aka joys of no JIT)
class ItemArray {
private int[] xx;
private int[] yy;
public final int getX(int idx) {...}
public final int getY(int idx) {...}
}
Компилятор может подставлять прямой
вызов или даже inline из-за final
24. Сказки у камина (aka joys of no JIT)
Как прочесть строку из файла в String?
В файле живут char'ы
Есть StringBuilder (прочли что-то --
добавили)
Дальше -- builder.toString()
Но тут происходит копирование
4Mb в файле (== 4Mchar) -->
--> 8Mb в памяти (char==2bytes) -->
--> 16Mb при копировании -->
--> OOME!!!
25. Сказки у камина (aka joys of no JIT)
Кроме этого, нельзя без копирования
перевести из char[] в String и обратно (хотя
String состоит всего лишь из char[]!)
Взятие подстроки всегда копирует
Парсинг любых текстовых форматов
становится сущим мучением
(Don't even think of using XML for your next
project!)
(Впрочем, если вы реализуете парсинг в
нативном коде...)
26. Сказки у камина (aka joys of no JIT)
В запущенных случаях хочется все строки
выкинуть и заменить на один большой char
[] со смещениями
Немедленно все проблемы решаются --
можно делать slice, доступ быстрый
(никаких там виртуальных функций)
Даже проверка границ в x[i] происходит в
нативном коде, а не в интерпретируемом!
27. Использование памяти
Профайлер памяти:
http://www.eclipse.org/mat/
В нужный момент сбросить кучу на диск:
Debug.dumpHprofData
("/data/data/<package>/dump.hprof");
Далее:
adb pull /data/data/<package>/dump.hprof
hprof_conv dump.hprof dump-conv.hprof
Всё, можно загружать в Eclipse!
28. Если всё же виноват CPU
Нет, вы этого так просто знать не можете!
Сначала запустите профайлер
Потом найдите виновника...
...и перепишите его на C++
31. NDK
Парсинг текстовых форматов
И не текстовых тоже
Тяжёлые вычисления
Mercator java -> native -> table
Всё, про что профайлер говорит, что оно
занимает слишком много времени
Поиск подстроки в длинной строке,
например (std->КМП = 0.8, std->native=0.1)
40. Совсем Страшные сказки
class Magic {
static private int n = 0;
static public void doMagic(Object obj) {
if (obj != null) n++;
}
static public int getMagic() { return n; }
}
41. Совсем Страшные сказки
class Magic {
static private int n = 0;
static public void doMagic(Object obj) {
if (obj != null) n++;
}
static public int getMagic() { return n; }
}
while(true) {
Task task = queue.getTaskBlocking();
task.execute();
task = null;
Magic.doMagic(task);
}