android: Как написать приложение, которое не тормозит
Post on 15-Jun-2015
1.679 Views
Preview:
TRANSCRIPT
АндроидКак написать приложение, которое не
тормозит
Максим Ушаков, Google
Обзор
Руки прочь от UI Thread!Немного об SQLiteПамять и GC:
Нативная кучаСтроки/массивы/массивы объектовСтрашные сказки на ночь
Как разобраться с памятью?Что делать, если всё-таки виноват CPU?
Руки прочь от UI Thread!
"ANR" получает программа, которая
за 5 секунд не ответила на действие пользователяили за 10 секунд не закончился BroadcastReceiver
Но даже раньше этого пользователи жалуются, что программа "тормозит"
"ANR" обычно получает тот, кто
Делает какую-то существенную работу,Читает или пишет файлы,Обращается к сети,или просто общается с базой данных
в основном потоке программы(Main/UI Thread)
Не делайте этого
Что выполняется "медленно"
Очевидные вещи, вроде увеличения всех чисел в базе данных на 1, взлома шифра RSA, и т.д.Но и менее очевидные:
Запись на флеш: 5-200(!)мсСеть: пинг = 100мс--10с--больше (GPRS)Соединение+HTTP+6k = 1-6с (3G)Garbage collector run -- ?мс
Как отложить работу на потом
android.os.AsyncTaskandroid.app.IntentServiceподробнее:http://developer.android.com/reference
Как показать это пользователю
Пользователь нажал на кнопку, вы запустили какое-то действиеВы не знаете, сколько оно продлитсяСоветы бывалых:
Сразу же выключите кнопку (disable)Запустите действие и таймерЕсли действие не завершилось за200-500мс, включите progress bar или какую-нибудь ещё анимацию
А в моей программе всё ок?
Android 2.3 (Gingerbread): StrictMode
Следит за вашей программойОпределяет, где вы делаете опасные вызовы в основном потокеИ наказывает вас!
А в моей программе всё ок?
Вставьте что-то вроде этого в начало программы:
StrictMode.setThreadPolicy( new StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() .build());
И вы получите кучу сообщений в log со стеком и рассказом, как вы неправы.
SQLite
Транзакция обязательно пишет журналновый файл, запись, удалить файлесли места мало, скорость падает катастрофически (с 5мс до 60мс)
ИндексыEXPLAIN, EXPLAIN QUERY PLANsqlite-wrapper.pl (by bradfitz)
http://code.google.com/p/zippy-android
А нужен ли SQLite?
Пишете лог? Лучше добавлять строчки в файлТолько читаете? Нужна простая структура? Подумайте, не обойтись ли простой структурой в файлеSQLite -- не Oracle, он всё делает прямолинейно
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!}
Использование памяти
16Мб на процесс (24Мб на N1/Desire)Это включает себя "нативную кучу", где хранятся BitmapЕсли памяти остаётся мало, GC начинает включаться чащеЕсли пытаться занять больше, получите OOME
Нативная куча
Bitmap bitmap = ...(1024x768 pixels, 24bpp);
Проблема: с точки зрения GC объект bitmap занимает ~15байт, поэтому, когда мало памяти, его удалять нет особого смысла.
Однако: bitmap отъедает 2.3Мб от тех 24Мб, которые выделены процессу
Нативная куча
Поэтому не полагайтесь на GC:
Bitmap bitmap = ...(1024x768, 24bpp);... use it ...... when you don't need it any more:bitmap.recycle();
Сказки у камина (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 на кучес оверхедом в два раза
Сказки у камина (aka joys of no JIT)
Сказки у камина (aka joys of no JIT)
Обращение к элементу происходит так:
array.get(55).getX()
1. �Находим в объекте array виртуальную таблицу, соответствующую интерфейсу List
2. Отыскиваем ней метод get3. Вызываем его4. Он проверяет границы...
Сказки у камина (aka joys of no JIT)
array.get(55).getX()
5. Находит 55-ый элемент массива6. В таблице виртуальных методов Item ищет getX()7. Метод getX() прибавляет смещение к началу объекта, и...8. Возвращает нам x!
Сказки у камина (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) {...}}
�Нет оверхеда по памятиВызов гораздо быстрее (одно обращение к массиву с проверкой границ)
Сказки у камина (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
Сказки у камина (aka joys of no JIT)
Как прочесть строку из файла в String?В файле живут char'ыЕсть StringBuilder (прочли что-то -- добавили)Дальше -- builder.toString()Но тут происходит копирование4Mb в файле (== 4Mchar) -->--> 8Mb в памяти (char==2bytes) -->--> 16Mb при копировании -->--> OOME!!!
Сказки у камина (aka joys of no JIT)
Кроме этого, нельзя без копирования перевести из char[] в String и обратно (хотя String состоит всего лишь из char[]!)Взятие подстроки всегда копируетПарсинг любых текстовых форматов становится сущим мучением(Don't even think of using XML for your next project!)(Впрочем, если вы реализуете парсинг в нативном коде...)
Сказки у камина (aka joys of no JIT)
В запущенных случаях хочется все строки выкинуть и заменить на один большой char[] со смещениямиНемедленно все проблемы решаются -- можно делать slice, доступ быстрый (никаких там виртуальных функций)Даже проверка границ в x[i] происходит в нативном коде, а не в интерпретируемом!
Использование памяти
Профайлер памяти:http://www.eclipse.org/mat/В нужный момент сбросить кучу на диск:Debug.dumpHprofData ("/data/data/<package>/dump.hprof");Далее:adb pull /data/data/<package>/dump.hprofhprof_conv dump.hprof dump-conv.hprofВсё, можно загружать в Eclipse!
Если всё же виноват CPU
Нет, вы этого так просто знать не можете!Сначала запустите профайлерПотом найдите виновника......и перепишите его на C++
Android NDK
http://developer.android.com/sdk/ndk/index.html
Объявляете фукнции в java как nativeОбрабатываете java-файл:javah -jni org.some.package.MyClassПолучаете .h-файлПишете реализацию функций
Если функции общаются с примитивными типами, больше
знать ничего не надо(почти)
NDK
Парсинг текстовых форматовИ не текстовых тожеТяжёлые вычисления
Mercator java -> native -> tableВсё, про что профайлер говорит, что оно занимает слишком много времени Поиск подстроки в длинной строке, например (std->КМП = 0.8, std->native=0.1)
Что почитать
Ссылки
�http://source.android.com/(очень полезное чтение, кладезь знаний)(и горестных мыслей)http://developer.android.com/guide/index.html(много рассказов, "как надо")http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jni.html (JNI)http://android-developers.blogspot.com/(несколько важных статей про разные тонкости)
Спасибо!теперь спрашивайте
backup slides
Страшные сказки
while(true) { Task task = queue.getTaskBlocking(); task.execute();}
Страшные сказки
Task task;while(true) { task = queue.getTaskBlocking(); task.execute();}
Страшные сказки
while(true) { Task task = queue.getTaskBlocking(); task.execute(); task = null;}
Страшные сказки
while(true) { Task task = queue.getTaskBlocking(); task.execute();}
Совсем Страшные сказки
class Magic { static private int n = 0; static public void doMagic(Object obj) { if (obj != null) n++; } static public int getMagic() { return n; }}
Совсем Страшные сказки
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);}
Уфф...
мурашки по спине
top related