Устройствата с Android имат много ядра, така че писането на гладки приложения е проста задача за всеки, нали? Неправилно. Тъй като всичко на Android може да се направи по много различни начини, изборът на най-добрия вариант може да бъде труден. Ако искате да изберете най-ефективния метод, трябва да знаете какво се случва под капака. За щастие не е нужно да разчитате на чувствата или обонянието си, тъй като има много инструменти, които могат да ви помогнат да намерите тесни места, като измервате и описвате какво се случва. Правилно оптимизираните и гладки приложения значително подобряват потребителското изживяване и освен това изразходват по-малко батерия.
Нека първо да видим някои цифри, за да разгледаме колко важна е наистина оптимизацията. Според a Пост Nimbledroid , 86% от потребителите (включително мен) имат деинсталирани приложения, след като са ги използвали само веднъж поради лоша производителност. Ако зареждате някакво съдържание, имате по-малко от 11 секунди, за да го покажете на потребителя. Само всеки трети потребител ще ви даде повече време. Възможно е също така да получите много лоши отзиви в Google Play.
Първото нещо, което всеки потребител забелязва отново и отново, е времето за стартиране на приложението. Според друга публикация в Nimbledroid , от 100-те най-добри приложения, 40 стартират за по-малко от 2 секунди, а 70 стартират за по-малко от 3 секунди. Така че, ако е възможно, обикновено трябва да покажете малко съдържание възможно най-скоро и да забавите проверките и актуализациите на заден план малко.
Винаги помнете, преждевременната оптимизация е коренът на всяко зло. Също така не трябва да губите твърде много време с микро оптимизация. Ще видите най-голяма полза от оптимизирането на код, който се изпълнява често. Например, това включва onDraw()
функция, която изпълнява всеки кадър, в идеалния случай 60 пъти в секунда. Рисуването е най-бавната операция там, така че опитайте да прерисувате само това, което трябва. Повече за това ще дойде по-късно.
Достатъчно теория, ето списък на някои от нещата, които трябва да имате предвид, ако представянето е от значение за вас.
Да кажем, че имате String и по някаква причина искате да добавите повече низове към него 10 хиляди пъти. Кодът може да изглежда нещо подобно.
String string = 'hello'; for (int i = 0; i <10000; i++) { string += ' world'; }
Можете да видите на мониторите на Android Studio колко неефективна може да бъде конкатенацията на низове. Има много събиране на боклука (GC).
Тази операция отнема около 8 секунди на доста доброто ми устройство, което има Android 5.1.1. По-ефективният начин за постигане на същата цел е използването на StringBuilder, като този.
StringBuilder sb = new StringBuilder('hello'); for (int i = 0; i <10000; i++) { sb.append(' world'); } String string = sb.toString();
На същото устройство това се случва почти моментално, за по-малко от 5ms. Визуализациите на процесора и паметта са почти напълно плоски, така че можете да си представите колко голямо е това подобрение. Забележете обаче, че за постигане на тази разлика трябваше да добавим 10 хиляди струни, което вероятно не правите често. Така че в случай, че добавяте само няколко низа веднъж, няма да видите никакво подобрение. Между другото, ако го направите:
String string = 'hello' + ' world';
Той се преобразува вътрешно в StringBuilder, така че ще работи добре.
Може би се чудите, защо конкатенацията на струни е първият начин толкова бавен? Причинява се от факта, че низовете са неизменни, така че след като бъдат създадени, те не могат да бъдат променяни. Дори ако мислите, че променяте стойността на String, вие всъщност създавате нов String с новата стойност. В пример като:
String myString = 'hello'; myString += ' world';
Това, което ще получите в паметта, не е 1 String „здравей, свят“, а всъщност 2 струни. String myString ще съдържа „здравей, свят“, както бихте очаквали. Оригиналният String със стойност „здравей“ все още е жив, без никаква препратка към него, чакащ да бъде събран боклук. Това е и причината, поради която трябва да съхранявате пароли в масив char вместо String. Ако съхранявате парола като низ, тя ще остане в паметта във формат, четим от човека, до следващия GC за непредсказуем период от време. Обратно към неизменността, описана по-горе, низът ще остане в паметта, дори ако му присвоите друга стойност, след като го използвате. Ако обаче изпразните масива char след използване на паролата, той ще изчезне отвсякъде.
Преди да започнете да пишете код, трябва да решите какви типове данни ще използвате за вашата колекция. Например, ако използвате Vector
или ArrayList
? Е, това зависи от вашата употреба. Ако имате нужда от колекция, безопасна за нишки, която ще позволи само една нишка наведнъж да работи с нея, трябва да изберете Vector
, тъй като тя е синхронизирана. В други случаи вероятно трябва да се придържате към ArrayList
, освен ако наистина нямате конкретна причина да използвате вектори.
Какво ще кажете за случая, когато искате колекция с уникални обекти? Е, вероятно трябва да изберете Set
. Те не могат да съдържат дубликати по дизайн, така че няма да се налага да се грижите сами за това. Има няколко типа набори, така че изберете такъв, който отговаря на вашия случай на употреба. За проста група уникални елементи можете да използвате HashSet
. Ако искате да запазите реда на елементите, в които са вмъкнати, изберете LinkedHashSet
. A TreeSet
сортира елементите автоматично, така че няма да се налага да извиквате никакви методи за сортиране върху него. Той също така трябва да сортира елементите ефективно, без да се налага да мислите алгоритми за сортиране .
Сортирането на цели числа или низове е доста лесно. Какво обаче, ако искате да сортирате клас по някакво свойство? Да предположим, че пишете списък с ястията, които ядете, и съхранявате техните имена и времеви клейма. Как бихте подредили храненията по времеви клеймо от най-ниското до най-високото? За щастие е доста просто. Достатъчно е да приложите Comparable
интерфейс в Meal
клас и замени compareTo()
функция. За да сортираме ястията по най-ниския времеви отпечатък до най-високия, бихме могли да напишем нещо подобно.
@Override public int compareTo(Object object) { Meal meal = (Meal) object; if (this.timestamp meal.getTimestamp()) { return 1; } return 0; }
Има много приложения, които събират местоположението на потребителя. За тази цел трябва да използвате API на Google Location Services, който съдържа много полезни функции. Има отделна статия за използването му, така че няма да го повтарям.
Бих искал само да подчертая някои важни моменти от гледна точка на представянето.
На първо място, използвайте само най-точното местоположение, от което се нуждаете. Например, ако правите някаква прогноза за времето, не се нуждаете от най-точното местоположение. Получаването само на много груба зона въз основа на мрежата е по-бързо и по-ефективно с батерията. Можете да го постигнете, като зададете приоритета на LocationRequest.PRIORITY_LOW_POWER
.
Можете също да използвате функция на LocationRequest
наречен setSmallestDisplacement()
. Ако зададете това в метри, приложението ви няма да бъде уведомявано за промяна на местоположението, ако е било по-малко от дадената стойност. Например, ако имате карта с близки ресторанти около вас и зададете най-малкото изместване на 20 метра, приложението няма да отправя заявки за проверка на ресторанти, ако потребителят просто се разхожда в една стая. Исканията биха били безполезни, тъй като така или иначе нямаше да има нов ресторант наблизо.
Второто правило е да изисквате актуализации на местоположението само толкова често, колкото имате нужда от тях. Това е напълно обяснимо. Ако наистина изграждате това приложение за прогноза на времето, не е необходимо да заявявате местоположението на всеки няколко секунди, тъй като вероятно нямате толкова точни прогнози (свържете се с мен, ако го направите). Можете да използвате setInterval()
функция за задаване на необходимия интервал, през който устройството ще актуализира приложението ви относно местоположението. Ако множество приложения продължават да изискват местоположението на потребителя, всяко приложение ще бъде уведомено при всяка нова актуализация на местоположението, дори ако имате по-висока setInterval()
комплект. За да предотвратите твърде честото известяване на приложението ви, уверете се, че винаги задавате най-бързия интервал на актуализация с setFastestInterval()
И накрая, третото правило изисква актуализации на местоположението само ако имате нужда от тях. Ако показвате някои близки обекти на картата на всеки x секунди и приложението отива във фонов режим, не е необходимо да знаете новото местоположение. Няма причина да актуализирате картата, ако потребителят все пак не може да я види. Не забравяйте да спрете да слушате актуализации на местоположението, когато е подходящо, за предпочитане в onPause()
. След това можете да възобновите актуализациите в onResume()
.
Има голяма вероятност приложението ви да използва интернет за изтегляне или качване на данни. Ако е, имате няколко причини, на които да обърнете внимание обработка на мрежови заявки . Един от тях са мобилните данни, които са много ограничени до много хора и не бива да ги пропилявате.
Вторият е батерията. Както WiFi, така и мобилните мрежи могат да консумират доста много от тях, ако се използват твърде много. Да кажем, че искате да изтеглите 1 kb. За да направите мрежова заявка, трябва да събудите клетъчното или WiFi радиото, след което можете да изтеглите данните си. Радиото обаче няма да заспи веднага след операцията. Той ще остане в доста активно състояние за още около 20-40 секунди, в зависимост от вашето устройство и оператор.
И така, какво можете да направите по въпроса? Партида. За да избегнете събуждането на радиото на всеки няколко секунди, предварително вземете неща, които потребителят може да се нуждае в следващите минути. Правилният начин на пакетиране е силно динамичен в зависимост от вашето приложение, но ако е възможно, трябва да изтеглите данните, от които потребителят може да се нуждае през следващите 3-4 минути. Може също така да се редактират параметрите на партидата въз основа на типа интернет на потребителя или състоянието на зареждане. Например, ако потребителят е в WiFi, докато се зарежда, можете предварително да изтеглите много повече данни, отколкото ако потребителят е в мобилен интернет с ниска батерия. Вземането под внимание на всички тези променливи може да бъде трудно нещо, което само малко хора биха направили. За щастие обаче на помощ е GCM Network Manager!
GCM Network Manager е наистина полезен клас с много адаптивни атрибути. Можете лесно да планирате повтарящи се и еднократни задачи. При повтарящи се задачи можете да зададете най-ниския, както и най-високия интервал на повторение. Това ще позволи пакетиране не само на вашите заявки, но и на заявки от други приложения. Радиото трябва да се събужда само веднъж за даден период и докато е включено, всички приложения в опашката изтеглят и качват това, което трябва. Този мениджър е наясно и с типа на мрежата на устройството и състоянието на зареждане, така че можете да регулирате съответно. Повече подробности и мостри можете да намерите в тази статия , Настоявам ви да го проверите. Примерна задача изглежда така:
Task task = new OneoffTask.Builder() .setService(CustomService.class) .setExecutionWindow(0, 30) .setTag(LogService.TAG_TASK_ONEOFF_LOG) .setUpdateCurrent(false) .setRequiredNetwork(Task.NETWORK_STATE_CONNECTED) .setRequiresCharging(false) .build();
Между другото, от Android 3.0, ако направите мрежова заявка на основната нишка, ще получите NetworkOnMainThreadException
. Това определено ще ви предупреди да не правите това отново.
Отражението е способността на класовете и обектите да изследват собствените си конструктори, полета, методи и т.н. Обикновено се използва за обратна съвместимост, за да се провери дали даден метод е наличен за определена версия на ОС. Ако трябва да използвате отражение за тази цел, не забравяйте да кеширате отговора, тъй като използването на отражение е доста бавно. Някои широко използвани библиотеки също използват Reflection, като Roboguice за инжектиране на зависимост. Това е причината, поради която трябва да предпочетете Dagger 2. За повече подробности относно отражението можете да проверите a отделен пост .
Автобоксирането и разопаковането са процеси на преобразуване на примитивен тип в тип Обект или обратно. На практика това означава преобразуване на int в Integer. За да постигне това, компилаторът използва Integer.valueOf()
функционират вътрешно. Преобразуването не е просто бавно, обектите отнемат и много повече памет от техните примитивни еквиваленти. Нека разгледаме малко код.
Integer total = 0; for (int i = 0; i <1000000; i++) { total += i; }
Въпреки че това отнема 500ms средно, пренаписването му, за да се избегне автоматичното боксиране, ще го ускори драстично.
int total = 0; for (int i = 0; i <1000000; i++) { total += i; }
Това решение работи с около 2ms, което е 25 пъти по-бързо. Ако не ми вярвате, изпробвайте го. Очевидно номерата ще са различни за всяко устройство, но все пак трябва да е много по-бързо. Освен това е наистина лесна стъпка за оптимизиране.
Добре, вероятно не създавате често такава променлива от тип Integer. Но какво да кажем за случаите, когато е по-трудно да се избегнат? Като в карта, където трябва да използвате Обекти, като Map
? Вижте решението, което много хора използват.
Map myMap = new HashMap(); for (int i = 0; i <100000; i++) { myMap.put(i, random.nextInt()); }
Вмъкването на 100 000 случайни части в картата отнема около 250 ms. Сега погледнете решението с SparseIntArray.
SparseIntArray myArray = new SparseIntArray(); for (int i = 0; i <100000; i++) { myArray.put(i, random.nextInt()); }
Това отнема много по-малко, приблизително 50ms. Това е и един от по-лесните методи за подобряване на производителността, тъй като не трябва да се прави нищо сложно и кодът също остава четим. Докато стартирахме ясно приложение с първото решение, отнех 13MB от паметта ми, използването на примитивни ints взе нещо под 7MB, така че само половината от него.
SparseIntArray е само една от страхотните колекции, която може да ви помогне да избегнете автобокса. Карта като Map
може да бъде заменен с SparseLongArray
, тъй като стойността на картата е от тип Long
. Ако погледнете програмен код от SparseLongArray
, ще видите нещо доста интересно. Под предния капак това са основно чифт масиви. Можете също да използвате SparseBooleanArray
по същия начин.
Ако сте прочели изходния код, може би сте забелязали бележка, че SparseIntArray
може да бъде по-бавен от HashMap
. Експериментирах много, но за мен SparseIntArray
винаги е бил по-добър както с памет, така и с производителност. Предполагам, че все още зависи от вас кой да изберете, експериментирайте с вашите случаи на употреба и вижте кой ви подхожда най-много. Определено имам SparseArrays
в главата си, когато използвате карти.
Както казах по-горе, когато оптимизирате производителността, вероятно ще видите най-голяма полза от оптимизирането на кода, който се изпълнява често. Една от много работещите функции е onDraw()
. Може да не ви изненада, че е отговорен за изчертаването на изгледи на екрана. Тъй като устройствата обикновено работят с 60 fps, функцията се изпълнява 60 пъти в секунда. Всеки кадър има 16 ms, за да бъде напълно обработен, включително неговата подготовка и рисуване, така че наистина трябва да избягвате бавните функции. Само основната нишка може да рисува на екрана, така че трябва да избягвате да правите скъпи операции върху нея. Ако замразите основната нишка за няколко секунди, може да получите скандалния диалогов прозорец Application Not Responding (ANR). За преоразмеряване на изображения, работа с база данни и т.н. използвайте фонова нишка.
Виждал съм някои хора, които се опитват да съкратят кода си, мислейки, че по този начин ще бъде по-ефективен. Това определено не е пътят, тъй като по-краткият код съвсем не означава по-бърз код. В никакъв случай не трябва да измервате качеството на кода по броя на редовете.
Едно от нещата, които трябва да избягвате в onDraw()
разпределя обекти като Paint. Подгответе всичко в конструктора, така че да е готово при рисуване. Дори да имате onDraw()
оптимизиран, трябва да го извиквате само толкова често, колкото трябва. Какво е по-добро от извикването на оптимизирана функция? Е, изобщо не извиква никаква функция. В случай, че искате да нарисувате текст, има доста изчистена помощна функция, наречена drawText()
, където можете да посочите неща като текста, координатите и цвета на текста.
Вероятно го знаете, но не мога да го пропусна. Шаблонът за проектиране на Viewholder е начин за улесняване на превъртането на списъците. Това е вид кеширане на изгледа, което може сериозно да намали повикванията до findViewById()
и раздуване на гледни точки, като ги съхранява. Може да изглежда нещо подобно.
static class ViewHolder { TextView title; TextView text; public ViewHolder(View view) { title = (TextView) view.findViewById(R.id.title); text = (TextView) view.findViewById(R.id.text); } }
След това, в getView()
функцията на вашия адаптер, можете да проверите дали имате полезен изглед. Ако не, създайте такъв.
ViewHolder viewHolder; if (convertView == null) { convertView = inflater.inflate(R.layout.list_item, viewGroup, false); viewHolder = new ViewHolder(convertView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.title.setText('Hello World');
Можете да намерите много полезна информация за този модел в интернет. Може да се използва и в случаите, когато изгледът на списъка ви съдържа множество различни видове елементи, като някои заглавки на раздели.
Шансовете са, че приложението ви ще съдържа някои изображения. В случай че изтегляте някои JPG файлове от мрежата, те могат да имат наистина огромни резолюции. Устройствата, на които ще бъдат показани, обаче ще бъдат много по-малки. Дори ако правите снимка с камерата на вашето устройство, тя трябва да бъде намалена, преди да се покаже, тъй като разделителната способност на снимката е много по-голяма от разделителната способност на дисплея. Преоразмеряването на изображенията преди показването им е от решаващо значение. Ако се опитате да ги покажете в пълна разделителна способност, паметта ви ще свърши доста бързо. Там е написано много за ефективно показване на растерни изображения в Android документите, ще се опитам да го обобщя.
Така че имате растерно изображение, но не знаете нищо за него. На ваше разположение има полезен флаг от растерни изображения, наречен inJustDecodeBounds, който ви позволява да разберете разделителната способност на растерното изображение. Да приемем, че вашето растерно изображение е 1024x768, а ImageView, използван за показването му, е само 400x300. Трябва да продължите да разделяте разделителната способност на растерното изображение на 2, докато все още е по-голямо от даденото ImageView. Ако го направите, той ще намали растерното изображение с коефициент 2, като ви даде растерно изображение от 512x384. Растерното изображение с демпфер използва 4 пъти по-малко памет, което ще ви помогне много с избягването на известната грешка OutOfMemory.
Сега, когато знаете как да го направите, не бива да го правите. ... Поне не, ако приложението ви разчита силно на изображения и не е само 1-2 изображения. Определено избягвайте неща като ръчно преоразмеряване и рециклиране на изображения, използвайте някои библиотеки на трети страни за това. Най-популярните са Пикасо от площада, Универсален Image Loader , Готино от Facebook или от моя любим, Плъзнете . Около него има огромна активна общност от разработчици, така че можете да намерите много полезни хора в раздела за проблеми на GitHub.
Стриктният режим е доста полезен инструмент за разработчици, за който много хора не знаят. Обикновено се използва за откриване на мрежови заявки или достъп до диска от основната нишка. Можете да зададете какво въпроси Стриктният режим трябва да търси и какво наказание трябва да предизвика. Пример на Google изглежда така:
public void onCreate() { if (DEVELOPER_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); } super.onCreate(); }
Ако искате да откриете всеки проблем, който стриктният режим може да намери, можете също да използвате detectAll()
. Както при много съвети за производителност, не бива да се опитвате сляпо да поправяте всички отчети в строг режим. Просто го проучете и ако сте сигурни, че не е проблем, оставете го на мира. Също така не забравяйте да използвате строг режим само за отстраняване на грешки и винаги да го деактивирате при производствените компилации.
Нека сега видим някои инструменти, които могат да ви помогнат да намерите тесни места или поне да покажете, че нещо не е наред.
Това е инструмент, вграден в Android Studio. По подразбиране можете да намерите Android Monitor в долния ляв ъгъл и можете да превключвате между 2 раздела там. Logcat и монитори. Разделът Монитори съдържа 4 различни графики. Мрежа, CPU, GPU и памет. Те са доста обяснителни, така че просто ще ги прегледам бързо. Ето екранна снимка на графиките, направени при анализирането на някои JSON, докато се изтеглят.
Мрежовата част показва входящия и изходящия трафик в KB / s. Частта на процесора показва използването на процесора в проценти. GPU мониторът показва колко време е необходимо за рендиране на кадрите на прозорец на потребителския интерфейс. Това е най-подробният монитор от тези 4, така че ако искате повече подробности за него, Прочети това .
И накрая имаме Монитор на паметта , които вероятно ще използвате най-много. По подразбиране показва текущото количество свободна и разпределена памет. С него можете да принудите и Колекция за боклук, за да проверите дали количеството на използваната памет спада. Той има полезна функция, наречена Dump Java Heap, която ще създаде HPROF файл, който може да се отвори с HPROF Viewer и анализатор . Това ще ви позволи да видите колко обекти сте разпределили, колко памет е заето от какво и може би кои обекти причиняват изтичане на памет. Да се научиш как да използваш този анализатор не е най-простата задача там, но си заслужава. Следващото нещо, което можете да направите с монитора на паметта, е да направите известно проследяване на разпределението по време, което можете да стартирате и спрете, както желаете. Може да бъде полезно в много случаи, например при превъртане или завъртане на устройството.
Това е прост помощен инструмент, който можете да активирате в Опции за програмисти, след като активирате режима за програмисти. Изберете Debug GPU overdraw, “Show overdraw areas” и екранът ви ще получи някои странни цветове. Добре е, това е, което трябва да се случи. Цветовете означават колко пъти дадена област е надвишена. Истинският цвят означава, че не е имало надвишаване, към това трябва да се стремите. Синьото означава едно надвишаване, зелено означава две, розово три, червено четири.
Въпреки че истинският цвят е най-добрият, винаги ще видите някои надвишения, особено около текстове, чекмеджета за навигация, диалогови прозорци и други. Затова не се опитвайте да се отървете напълно от него. Ако приложението ви е синкаво или зеленикаво, това вероятно е добре. Ако обаче видите твърде много червено на някои прости екрани, трябва да проучите какво се случва. Може да има твърде много фрагменти, подредени един върху друг, ако продължавате да ги добавяте, вместо да заменяте. Както споменах по-горе, рисуването е най-бавната част от приложенията, така че няма смисъл да рисувате нещо, ако върху него ще бъдат изтеглени повече от 3 слоя. Чувствайте се свободни да разгледате любимите си приложения с него. Ще видите, че дори приложения с над милиард изтегляния имат червени области, така че просто се успокойте, когато се опитвате да оптимизирате.
Това е друг инструмент от опциите за разработчици, наречен Профилно графично изобразяване . След като го изберете, изберете „На екрана като ленти“. Ще забележите, че на екрана ви се появяват цветни ленти. Тъй като всяко приложение има отделни ленти, странно лентата на състоянието има свои собствени и в случай, че имате софтуерни бутони за навигация, те също имат свои собствени ленти. Както и да е, лентите се актуализират, докато взаимодействате с екрана.
Лентите се състоят от 3-4 цвята и според документите на Android техният размер наистина има значение. Колкото по-малък, толкова по-добре. В долната част имате синьо, което представлява времето, използвано за създаване и актуализиране на списъците за показване на View. Ако тази част е твърде висока, това означава, че има много персонализиран изглед на изглед или много работа, извършена в onDraw()
функции. Ако имате Android 4.0+, ще видите лилава лента над синята. Това представлява времето, прекарано в прехвърляне на ресурси към нишката за рендиране. След това идва червената част, която представлява времето, прекарано от 2D визуализатора на Android, издаващ команди на OpenGL за изчертаване и преначертаване на списъци за показване. В горната част е оранжевата лента, която представлява времето, в което процесорът чака GPU да приключи работата си. Ако е твърде висок, приложението прави твърде много работа на графичния процесор.
Ако сте достатъчно добри, има още един цвят над оранжевия. Това е зелена линия, представляваща прага от 16 ms. Тъй като целта ви трябва да е да използвате приложението си при 60 fps, имате 16 ms, за да нарисувате всеки кадър. Ако не го направите, някои кадри могат да бъдат пропуснати, приложението може да стане нестабилно и потребителят определено ще забележи. Обърнете специално внимание на анимациите и превъртането, там гладкостта има най-голямо значение. Въпреки че можете да откриете някои пропуснати кадри с този инструмент, той наистина няма да ви помогне да разберете къде точно е проблемът.
Това е един от любимите ми инструменти там, тъй като е наистина мощен. Можете да го стартирате от Android Studio чрез Tools -> Android -> Android Device Monitor, или той също е във вашата папка sdk / tools като „monitor“. Там можете да намерите и самостоятелен изпълним файл на hierarachyviewer, но тъй като той е остарял, трябва да отворите монитора. Въпреки това отворите Android Device Monitor, превключете към перспективата на Hierarchy Viewer. Ако не виждате нито едно работещо приложение, присвоено на вашето устройство, там са няколко неща, които можете да направите, за да го поправите. Опитайте също да проверите това тема, има хора с всякакви проблеми и всякакви решения. Нещо трябва да работи и за вас.
С Hierarchy Viewer можете да получите наистина чист преглед на вашите йерархии на изгледи (очевидно). Ако виждате всяко оформление в отделен XML, лесно можете да забележите безполезни изгледи. Ако обаче продължите да комбинирате оформленията, лесно може да се объркате. Инструмент като този улеснява откриването, например, на RelativeLayout, който има само 1 дете, друго RelativeLayout. Това прави един от тях подвижен.
Избягвайте да извиквате requestLayout()
, тъй като това води до обхождане на цялата йерархия на изгледа, за да разберете колко голям трябва да бъде всеки изглед. Ако има някакъв конфликт с измерванията, йерархията може да бъде пресечена няколко пъти, което ако се случи по време на някаква анимация, определено ще накара някои кадри да бъдат пропуснати. Ако искате да научите повече за това как Android черпи своите възгледи, можете Прочети това . Нека разгледаме един изглед, както се вижда в Hierarchy Viewer.
Горният десен ъгъл съдържа бутон за максимизиране на визуализацията на конкретния изглед в самостоятелен прозорец. Под него можете да видите и действителния преглед на изгледа в приложението. Следващият елемент е число, което представя колко деца има даден изглед, включително самия изглед. Ако изберете възел (за предпочитане основния) и натиснете „Получаване на времена за оформление“ (3 цветни кръга), ще имате още 3 стойности, попълнени, заедно с цветни кръгове, които се появяват с етикет мярка, оформление и рисуване. Може да не е шокиращо, че фазата на измерване представлява времето, необходимо за измерване на дадения изглед. Фазата на оформлението е около времето за изобразяване, докато чертежът е действителната операция по рисуване. Тези стойности и цветове са относителни един към друг. Зеленият означава, че изгледът се показва в топ 50% от всички изгледи в дървото. Жълтото означава изобразяване в по-бавните 50% от всички изгледи в дървото, а червеното означава, че даденият изглед е един от най-бавните. Тъй като тези стойности са относителни, винаги ще има червени. Просто не можете да ги избегнете.
Под стойностите имате името на класа, като „TextView“, вътрешен ID на изгледа на обекта и android: id на изгледа, който сте задали в XML файловете. Призовавам ви да изградите навик да добавяте идентификатори към всички изгледи, дори ако не ги посочвате в кода. Това ще направи идентифицирането на изгледите в Hierarchy Viewer наистина лесно и в случай че имате автоматизирани тестове във вашия проект, това също ще направи насочването към елементите много по-бързо. Това ще спести малко време за вас и вашите колеги, които ги пишат. Добавянето на идентификатори към елементи, добавени в XML файлове, е доста лесно. Но какво да кажем за динамично добавените елементи? Е, оказва се и наистина просто. Просто създайте ids.xml файл във вашата папка със стойности и въведете задължителните полета. Може да изглежда така:
setId(R.id.item_title)
След това в кода можете да използвате LinearLayouts
. Не може да бъде по-просто
Има още няколко неща, на които трябва да обърнете внимание при оптимизиране на потребителския интерфейс. Обикновено трябва да избягвате дълбоки йерархии, докато предпочитате плитки, може би широки. Не използвайте оформления, които не ви трябват. Например вероятно можете да замените група вложени RelativeLayout
или с TableLayout
, или с LinearLayout
. Чувствайте се свободни да експериментирате с различни оформления, не винаги винаги използвайте RelativeLayout
и
|_+_|. Също така опитайте да създадете някои персонализирани изгледи, когато е необходимо, това може значително да подобри производителността, ако се направи добре. Например, знаете ли, че Instagram не използва TextViews за показване на коментари ?
Можете да намерите повече информация за Hierarchy Viewer в Сайт за разработчици на Android с описания на различни панели, с помощта на инструмента Pixel Perfect и др. Още нещо, което бих посочил, е улавяне на изгледите в .psd файл, което може да се направи с бутона „Заснемане на слоевете на прозореца“. Всеки изглед ще бъде в отделен слой, така че е много лесно да го скриете или промените във Photoshop или GIMP. О, това е друга причина да добавите идентификатор към всеки изглед, който можете. Това ще направи слоевете да имат имена, които всъщност имат смисъл.
Ще намерите много повече инструменти за отстраняване на грешки в опциите за програмисти, така че ви съветвам да ги активирате и да видите какво правят. Какво може да се обърка?
Сайтът за разработчици на Android съдържа набор от най-добри практики за изпълнение . Те обхващат много различни области, включително управление на паметта, за което всъщност не съм говорил. Пренебрегнах го мълчаливо, защото боравенето с паметта и проследяването на течове от паметта е съвсем отделна история. Използването на библиотека на трета страна за ефективно показване на изображения ще помогне много, но ако все още имате проблеми с паметта, разгледайте Изтичане на канарче направени от Square или прочетени това .
И така, това беше добрата новина. Лошото ново е, оптимизиране Android приложения е много по-сложно. Има много начини да направите всичко, така че трябва да сте запознати с плюсовете и минусите на тях. Обикновено няма решение за сребърни куршуми, което да има само предимства. Само като разберете какво се случва зад кулисите, ще можете да изберете най-доброто решение за вас. Само защото любимият ви разработчик казва, че нещо е добро, не означава непременно, че това е най-доброто решение за вас. Има много повече области за обсъждане и по-напреднали инструменти за профилиране, така че може да стигнем до тях следващия път.
Уверете се, че се учите от най-добрите разработчици и топ компании. Можете да намерите няколкостотин инженерни блога на адрес тази връзка . Очевидно това не са само неща, свързани с Android, така че ако се интересувате само от Android, трябва да филтрирате конкретния блог. Силно бих препоръчал блоговете на Facebook и Instagram . Въпреки че потребителският интерфейс на Instagram на Android е съмнителен, техният инженерен блог има няколко наистина страхотни статии. За мен е страхотно, че е толкова лесно да видя как се правят нещата в компании, които обработват стотици милиони потребители ежедневно, така че не четенето на техните блогове изглежда лудо. Светът се променя наистина бързо, така че ако не се опитвате постоянно да се усъвършенствате, да се учите от другите и да използвате нови инструменти, ще бъдете изоставени. Както каза Марк Твен, човек, който не чете, няма предимство пред този, който не може да чете.