Android: Как написать приложение, которое не тормозит

42
Андроид Как написать приложение, которое не тормозит Максим Ушаков, Google

Transcript of Android: Как написать приложение, которое не тормозит

Page 1: Android: Как  написать приложение, которое не тормозит

АндроидКак написать приложение, которое не

тормозит

Максим Ушаков, Google

Page 2: Android: Как  написать приложение, которое не тормозит

Обзор

Руки прочь от UI Thread!Немного об SQLiteПамять и GC:

Нативная кучаСтроки/массивы/массивы объектовСтрашные сказки на ночь

Как разобраться с памятью?Что делать, если всё-таки виноват CPU?

Page 3: Android: Как  написать приложение, которое не тормозит

Руки прочь от UI Thread!

Page 4: Android: Как  написать приложение, которое не тормозит

"ANR" получает программа, которая

за 5 секунд не ответила на действие пользователяили за 10 секунд не закончился BroadcastReceiver

Но даже раньше этого пользователи жалуются, что программа "тормозит"

Page 5: Android: Как  написать приложение, которое не тормозит

"ANR" обычно получает тот, кто

Делает какую-то существенную работу,Читает или пишет файлы,Обращается к сети,или просто общается с базой данных

в основном потоке программы(Main/UI Thread)

Page 6: Android: Как  написать приложение, которое не тормозит

Не делайте этого

Page 7: Android: Как  написать приложение, которое не тормозит

Что выполняется "медленно"

Очевидные вещи, вроде увеличения всех чисел в базе данных на 1, взлома шифра RSA, и т.д.Но и менее очевидные:

Запись на флеш: 5-200(!)мсСеть: пинг = 100мс--10с--больше (GPRS)Соединение+HTTP+6k = 1-6с (3G)Garbage collector run -- ?мс

Page 8: Android: Как  написать приложение, которое не тормозит

Как отложить работу на потом

android.os.AsyncTaskandroid.app.IntentServiceподробнее:http://developer.android.com/reference

Page 9: Android: Как  написать приложение, которое не тормозит

Как показать это пользователю

Пользователь нажал на кнопку, вы запустили какое-то действиеВы не знаете, сколько оно продлитсяСоветы бывалых:

Сразу же выключите кнопку (disable)Запустите действие и таймерЕсли действие не завершилось за200-500мс, включите progress bar или какую-нибудь ещё анимацию

Page 10: Android: Как  написать приложение, которое не тормозит

А в моей программе всё ок?

Android 2.3 (Gingerbread): StrictMode

Следит за вашей программойОпределяет, где вы делаете опасные вызовы в основном потокеИ наказывает вас!

Page 11: Android: Как  написать приложение, которое не тормозит

А в моей программе всё ок?

Вставьте что-то вроде этого в начало программы:

StrictMode.setThreadPolicy( new StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() .build());

И вы получите кучу сообщений в log со стеком и рассказом, как вы неправы.

Page 12: Android: Как  написать приложение, которое не тормозит

SQLite

Транзакция обязательно пишет журналновый файл, запись, удалить файлесли места мало, скорость падает катастрофически (с 5мс до 60мс)

ИндексыEXPLAIN, EXPLAIN QUERY PLANsqlite-wrapper.pl (by bradfitz)

http://code.google.com/p/zippy-android

Page 13: Android: Как  написать приложение, которое не тормозит

А нужен ли SQLite?

Пишете лог? Лучше добавлять строчки в файлТолько читаете? Нужна простая структура? Подумайте, не обойтись ли простой структурой в файлеSQLite -- не Oracle, он всё делает прямолинейно

Page 14: Android: Как  написать приложение, которое не тормозит

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!}

Page 15: Android: Как  написать приложение, которое не тормозит

Использование памяти

16Мб на процесс (24Мб на N1/Desire)Это включает себя "нативную кучу", где хранятся BitmapЕсли памяти остаётся мало, GC начинает включаться чащеЕсли пытаться занять больше, получите OOME

Page 16: Android: Как  написать приложение, которое не тормозит

Нативная куча

Bitmap bitmap = ...(1024x768 pixels, 24bpp);

Проблема: с точки зрения GC объект bitmap занимает ~15байт, поэтому, когда мало памяти, его удалять нет особого смысла.

Однако: bitmap отъедает 2.3Мб от тех 24Мб, которые выделены процессу

Page 17: Android: Как  написать приложение, которое не тормозит

Нативная куча

Поэтому не полагайтесь на GC:

Bitmap bitmap = ...(1024x768, 24bpp);... use it ...... when you don't need it any more:bitmap.recycle();

Page 18: Android: Как  написать приложение, которое не тормозит

Сказки у камина (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 на кучес оверхедом в два раза

Page 19: Android: Как  написать приложение, которое не тормозит

Сказки у камина (aka joys of no JIT)

Page 20: Android: Как  написать приложение, которое не тормозит

Сказки у камина (aka joys of no JIT)

Обращение к элементу происходит так:

array.get(55).getX()

1. �Находим в объекте array виртуальную таблицу, соответствующую интерфейсу List

2. Отыскиваем ней метод get3. Вызываем его4. Он проверяет границы...

Page 21: Android: Как  написать приложение, которое не тормозит

Сказки у камина (aka joys of no JIT)

array.get(55).getX()

5. Находит 55-ый элемент массива6. В таблице виртуальных методов Item ищет getX()7. Метод getX() прибавляет смещение к началу объекта, и...8. Возвращает нам x!

Page 22: Android: Как  написать приложение, которое не тормозит

Сказки у камина (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) {...}}

�Нет оверхеда по памятиВызов гораздо быстрее (одно обращение к массиву с проверкой границ)

Page 23: Android: Как  написать приложение, которое не тормозит

Сказки у камина (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

Page 24: Android: Как  написать приложение, которое не тормозит

Сказки у камина (aka joys of no JIT)

Как прочесть строку из файла в String?В файле живут char'ыЕсть StringBuilder (прочли что-то -- добавили)Дальше -- builder.toString()Но тут происходит копирование4Mb в файле (== 4Mchar) -->--> 8Mb в памяти (char==2bytes) -->--> 16Mb при копировании -->--> OOME!!!

Page 25: Android: Как  написать приложение, которое не тормозит

Сказки у камина (aka joys of no JIT)

Кроме этого, нельзя без копирования перевести из char[] в String и обратно (хотя String состоит всего лишь из char[]!)Взятие подстроки всегда копируетПарсинг любых текстовых форматов становится сущим мучением(Don't even think of using XML for your next project!)(Впрочем, если вы реализуете парсинг в нативном коде...)

Page 26: Android: Как  написать приложение, которое не тормозит

Сказки у камина (aka joys of no JIT)

В запущенных случаях хочется все строки выкинуть и заменить на один большой char[] со смещениямиНемедленно все проблемы решаются -- можно делать slice, доступ быстрый (никаких там виртуальных функций)Даже проверка границ в x[i] происходит в нативном коде, а не в интерпретируемом!

Page 27: Android: Как  написать приложение, которое не тормозит

Использование памяти

Профайлер памяти: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!

Page 28: Android: Как  написать приложение, которое не тормозит

Если всё же виноват CPU

Нет, вы этого так просто знать не можете!Сначала запустите профайлерПотом найдите виновника......и перепишите его на C++

Page 29: Android: Как  написать приложение, которое не тормозит

Android NDK

http://developer.android.com/sdk/ndk/index.html

Объявляете фукнции в java как nativeОбрабатываете java-файл:javah -jni org.some.package.MyClassПолучаете .h-файлПишете реализацию функций

Page 30: Android: Как  написать приложение, которое не тормозит

Если функции общаются с примитивными типами, больше

знать ничего не надо(почти)

Page 31: Android: Как  написать приложение, которое не тормозит

NDK

Парсинг текстовых форматовИ не текстовых тожеТяжёлые вычисления

Mercator java -> native -> tableВсё, про что профайлер говорит, что оно занимает слишком много времени Поиск подстроки в длинной строке, например (std->КМП = 0.8, std->native=0.1)

Page 32: Android: Как  написать приложение, которое не тормозит

Что почитать

Page 33: Android: Как  написать приложение, которое не тормозит

Ссылки

�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/(несколько важных статей про разные тонкости)

Page 34: Android: Как  написать приложение, которое не тормозит

Спасибо!теперь спрашивайте

Page 35: Android: Как  написать приложение, которое не тормозит

backup slides

Page 36: Android: Как  написать приложение, которое не тормозит

Страшные сказки

while(true) { Task task = queue.getTaskBlocking(); task.execute();}

Page 37: Android: Как  написать приложение, которое не тормозит

Страшные сказки

Task task;while(true) { task = queue.getTaskBlocking(); task.execute();}

Page 38: Android: Как  написать приложение, которое не тормозит

Страшные сказки

while(true) { Task task = queue.getTaskBlocking(); task.execute(); task = null;}

Page 39: Android: Как  написать приложение, которое не тормозит

Страшные сказки

while(true) { Task task = queue.getTaskBlocking(); task.execute();}

Page 40: Android: Как  написать приложение, которое не тормозит

Совсем Страшные сказки

class Magic { static private int n = 0; static public void doMagic(Object obj) { if (obj != null) n++; } static public int getMagic() { return n; }}

Page 41: Android: Как  написать приложение, которое не тормозит

Совсем Страшные сказки

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);}

Page 42: Android: Как  написать приложение, которое не тормозит

Уфф...

мурашки по спине