- Instant Run: How Does it Work?!
- An Android Tool Time Deep Dive
- Let’s save the magic for Sunday nights
- Start with this simple flow chart of a typical build cycle
- Hot, Warm, and Cold Swaps aren’t references to games played during 70’s parties. As far as I know.
- When you hit run or debug, something like this happens
- The first time you hit Run or Debug with Instant Run enabled, Gradle performs some additional tasks
- Hot Swapping
- Warm Swapping
- Cold Swaps
- Instant Run is clever, but it can’t turn back time
- Instant Run Tips and Tricks
- Instant Run: как это работает?
- Горячая, тёплая и холодная замены
- Горячая замена
- Тёплая замена
- Холодная замена
- Советы и секреты
Instant Run: How Does it Work?!
An Android Tool Time Deep Dive
W hen something’s simple and helpful, most people are satisfied with that. But us engineers — we’re not normal people.
Take Instant Run. It’s a feature in Android Studio that uses ✨magic✨ to significantly reduce the build and deploy times for incremental code changes during your coding / testing / debugging lifecycle.
I say magic, because for the most part that’s how it looks. The first time you hit run or debug, it works like you’d expect — then each time you make a change and hit run or debug again (this time with a ⚡ on the icon), the changes are applied before I’ve had a chance to shift my attention to my phone.
Let’s save the magic for Sunday nights
I prefer my magic to come with a side-helping of dragons, political intrigue, and unexpected beheadings, so I got together with the Android Studio engineering team to get a peek behind the curtain, and learn how Instant Run actually works.
Start with this simple flow chart of a typical build cycle
The goals of Instant Run are really simple:
Remove as many of these steps as possible, and make whatever remains as fast as possible.
In practice that means:
- Build and deploy only the incremental changes.
- Don’t reinstall the app.
- Don’t restart the app.
- Don’t even restart the Activity.
Hot, Warm, and Cold Swaps aren’t references to games played during 70’s parties. As far as I know.
Hot Swap: Incremental code changes are applied and reflected in the app without needing to relaunch the app or even restart the current Activity. Can be used for most simple changes within method implementations.
Warm Swap: The Activity needs to be restarted before changes can be seen and used. Typically required for changes to resources.
Cold Swap: The app is restarted (but still not reinstalled). Required for any structural changes such as to inheritance or method signatures.
When you hit run or debug, something like this happens
Your manifest files are merged and packaged, along with your app’s resources, into an APK. Similarly, your source code .java files are compiled into bytecode, converted to .dex files, and they’re also included within your APK.
The first time you hit Run or Debug with Instant Run enabled, Gradle performs some additional tasks
Bytecode instrumentation is added to your .class files, and a new App Server class is injected into your app.
A new Application class definition is also added, which injects custom class loaders and will start the App Server. Accordingly, your manifest is modified to ensure your app uses it (if you’ve created your own Application class, the Instant Run version will proxy yours.)
Instant Run is now… running, so if your make code changes and hit run or debug again, Instant Run will try to shortcut as much of the full build process as possible using a Hot, Warm, or Cold swap.
Before applying Instant Run changes, Android Studio checks that there’s an open socket to an App Server running within an Instant Run enabled version of your app. It confirms the app is running in the foreground, and that its build ID is the version Android Studio expects.
Hot Swapping
Android Studio monitors which files are changed during development, and runs a custom Gradle task to generate .dex files for only the modified classes.
Those new .dex files are picked up by Android Studio, which deploys it to the App Server running within our app.
Because the original versions of our classes already exist in the running app instance — Gradle has transformed the “updated” versions such that they effectively override those pre-existing classes. Those transformed, updated classes are then loaded by the App Server using the custom class loaders.
From now on, each time a method gets called — anywhere within our app — the instrumentation injected into our original class files communicates with the App Server to see if they’ve been updated.
If so, execution is delegated to the new “override” classes and the new, modified, version of the method will execute instead.
Redirecting methods works well for changes to method implementations, but what about things that are loaded when the Activity starts?
Warm Swapping
A warm swap restarts the Activity. Resources are loaded when Activities are started, so modifying them requires an Activity restart to force a resource reload.
Currently, changes to any resource results in all of them being re-packaged and transmitted to your app — but we’re working on an incremental packager that will only package and deploy new or modified resources.
Note that a warm swap won’t work for changes to resources referenced within the Manifest — or changes to the manifest itself — because Manifest values are read when the APK is installed. Changes to the Manifest (or manifest-referenced resources) will trigger a full build and deploy.
Unfortunately, restarting the Activity won’t magically apply structural changes. Adding, removing, or changing annotations, fields, static or instance method signatures, or changing parent classes or static initializers will require a Cold Swap.
Cold Swaps
When deployed, your app and it’s sub-projects are divided into up to 10 slices, each its own dex file; classes are allocated to slices based on their package names. When applying a cold swap, a modified class will require all the other classes within the same slice to also be redexed before that slice is deployed to the target device.
This approach depends on the Android Runtime being capable of loading multiple .dex files, a feature introduced with ART, which is only guaranteed on Android 5.0 (API level 21) devices and higher.
For target devices running API level 20 or lower — and therefore possibly using the DALVIK runtime, Android Studio deploys a full APK.
Instant Run is clever, but it can’t turn back time
Code changes that might otherwise be applied through a Hot Swap, but which affect initializers that were run when the application was first run, you’ll need to restart your app for the changes to take effect.
Instant Run Tips and Tricks
Instant Run is controlled by Android Studio, so only start / restart your debug instance from the IDE — don’t start / restart your app from the device or things will get out of whack quickly.
A more detailed list of tips and tricks is available from the Android Documentation, but here’s a few bullet points to keep in mind.
- Tweak the resources you’re allocating to the Gradle process. If you have at least 2 gig assigned to the Gradle Daemon JVM via the jvmargs setting in the gradle.properties file, dex-in-process will be enabled, and will dramatically improve the speed of all builds — Instant Run and full / clean builds. You’ll want to experiment and observe the effect on your build times to find the value for you.
- The availability of ART in Android 21 means you’ll get the most out of Instant Run by setting your minSdkVersion to 21 or higher. You can create a new product flavor specifically for debugging that sets your minSDK to 21.
- Remember that changes to the manifest will trigger a full build and deploy cycle — so, if your build process automatically updates any part of the app manifest (for example automatically iterating versionCode or versionName) — you’ll want to disable that behavior in your debug build variants.
- Instant Run currently only instruments the main process, so if your app uses multiple processes, Hot and Warm swaps on the other processes will degrade to cold swaps — or full builds if you’re targeting an API Level less than 21.
- If you’re on Windows, Windows Defender Real-Time Protection might be causing Instant Run slowdowns. You can get around that by adding your project folder to the list of Windows Defender exclusions.
- As of this recording, Instant Run didn’t support the Jack compiler, Instrumentation Tests, or deploying to multiple devices simultaneously.
Instant Run is constantly evolving, with the team exploring new techniques to maximize the number of cases that allow for a hot swap, and minimizing the need for cold swaps or full builds.
For more Android Studio deep dives, protips, and release announcements Subscribe to Android Developers on YouTube, and tune in to Android Tool Time.
Источник
Instant Run: как это работает?
Всем привет! Данная статья является переводом заметки Android-разработчика и автора книги «Android 4. Программирование приложений для планшетных компьютеров и смартфонов» Рето Майера. Над переводом работал Android-отдел компании Лайв Тайпинг. Оригинальная статья доступна здесь.
Большинство людей вполне довольны, когда им в руки попадает что-то простое и полезное. Но мы, программисты, не относимся к этому большинству.
Взять, к примеру, Instant Run. Это фича Android Studio, которая при помощи «магии» сокращает время, затрачиваемое на сборку и деплой инкрементальных изменений кода в процессе написания / тестирования / дебаггинга.
Я называю это магией потому, что со стороны всё выглядит именно так. После первого нажатия Run или Debug всё работает так, как того и следует ожидать. Однако каждый раз, когда в код вносятся изменения и снова нажимается кнопка Run или Debug (но в этот раз — с иконкой в виде молнии), изменения деплоятся на мой телефон настолько быстро, что я не успеваю это заметить.
Но давайте оставим магию для воскресных посиделок у телевизора.
Лично меня больше устраивает, когда магия приходит ко мне в комплекте с драконами, политическими интригами и неожиданными поворотами сюжета. Поэтому я встретился с командой инженеров, работавших над Android Studio, чтобы выяснить, как же Instant Run работает на самом деле.
Начнём с этой простой блок-схемы типичного цикла сборки приложения.
Сборка, деплой/установка, запуск приложения, запуск активити.
Цели, которые преследует Instant Run, предельно просты: убрать максимально возможное количество этих шагов и максимально ускорить те, которые останутся в результате.
На практике это означает:
· сборка и деплой только инкрементальных изменений;
· не переустанавливать приложение;
· не перезапускать приложение;
· не перезапускать активити.
Горячая, тёплая и холодная замены
Instant Run = сборка инкрементальных изменений + горячая, тёплая или холодная замена.
Горячая замена: изменения вносятся в приложение без необходимости его перезапуска и даже без необходимости перезапуска текущей активити. Может быть использовано для большинства простейших правок внутри реализации методов.
Тёплая замена: для того, чтобы правки вступили в силу, необходимо перезапустить активити. Обычно требуется при изменении ресурсов.
Холодная замена: перезапуск приложения (без необходимости переустановки). Требуется при внесении структурных изменений, таких, как наследование или изменение сигнатуры методов.
Когда вы нажимаете Run или Debug, происходит что-то наподобие этого:
Манифесты собираются в один файл, который вместе с ресурсами и .dex-файлами упаковываются в APK.
Ваши манифесты собираются и упаковываются вместе с ресурсами в APK. Точно так же ваши .java-файлы компилируются в байткод, конвертируются в .dex-файлы и отправляются в тот же APK.
В первый раз, когда вы нажимаете Run или Debug со включенным Instant Run, Gradle выполняет несколько дополнительных тасков.
Java Bytecode Instrumentation и App Server внедрены в ваш debug APK-файл.
Bytecode Instrumentation добавляется в ваши .class-файлы и новый App Server класс заинжекчен в ваше приложение.
Помимо этого, добавляется новое определение класса Application, которое внедряет кастомные загрузчики классов и запускает App Server. Для того, чтобы ваше приложение смогло использовать эти изменения, изменяется файл Android-манифеста. Если вы создавали свой собственный класс Application, Instant Run подменит его.
Теперь Instant Run запущен и отслеживает все изменения, которые вы вносите в код. Так что в следующий раз, когда вы нажмёте Run или Debug, Instant Run постарается максимально сократить процесс сборки, используя горячую, тёплую или холодную замену.
Перед тем, как применить изменения, Android Studio проверяет наличие открытого сокета в App Server, запущенном внутри приложения со включенным Instant Run. Он подтверждает, что приложение запущено, а его buildID — то, которое ожидает Android Studio.
Горячая замена
Android Studio отслеживает изменения в файлах в процессе разработки и запускает специальный Gradle-таск, чтобы сгенерировать .dex-файлы только для изменённых классов. Потом Android Studio подхватывает эти файлы и деплоит их в App Server, работающий внутри приложения.
Поскольку оригинальные версии наших классов уже существуют в запущенном приложении, Gradle при помощи Transformation API подменяет их на новые. После этого изменённые классы подгружаются App Server’ом, используя кастомные загрузчики классов.
С этого момента при каждом вызове метода внутри нашего приложения специальные инструменты, внедрённые в наши исходные классы, взаимодействуют с App Server’ом, чтобы узнать, были ли они обновлены.
Если это так, исполнение делегируется новым подменённым классам, и вместо старого метода запускается его новая версия.
Если вы установите точки отладки, то в стектрейсе увидите вызовы метода класса с именем “override”.
Перенаправление методов хорошо срабатывает в случае изменения реализации самих методов, но что делать с теми вещами, которые загружаются во время запуска активити?
Тёплая замена
Тёплая замена перезапускает активити. Ресурсы загружаются до запуска активити. Поэтому любая их модификация требует перезагрузки активити для принудительной перезагрузки ресурсов.
В настоящий момент изменение любого ресурса приводит к тому, что все ресурсы перепаковываются и отправляются в ваше приложение. Но мы усиленно работаем над упаковщиком, который будет упаковывать и деплоить только новые или изменённые ресурсы.
Обратите внимание, что тёплая замена не будет работать для ресурсов, упомянутых в Android-манифесте, а также в случае изменений, внесённых в сам манифест, поскольку значения этого файла читаются в процессе инсталляции APK. Изменения, затрагивающие манифест, приведут к полному циклу сборки и установки приложения.
К сожалению, перезапуск активити не сможет волшебным образом применить структурные изменения. Добавление, удаление или изменение аннотаций, полей, сигнатур методов, изменение родительских классов или статических инициализаций требуют холодной замены.
Холодная замена
После деплоинга ваше приложение и его подпроекты разделены на несколько .dex-файлов. Классы распределены по этим файлам в зависимости от названий пакетов, в которых они находятся. При холодной замене .dex-файл, которому принадлежит изменённый класс, полностью перекомпилируется вместе с другими классами, входящими в него, и заново деплоится.
Этот подход опирается на способность Android Runtime загружать несколько .dex-файлов (фича, появившаяся в ART) и поэтому может быть использован только для устройств, работающих на Android версии 5.0 (API Level 21) и выше.
На устройствах, работающих на API Level 20 или ниже, а следовательно, использующих DALVIK, Android Studio деплоит APK целиком.
Хотя Instant Run очень умён, но обратить время вспять он не в силах. Изменения в коде, которые могли бы быть применены путём горячей замены, но которые влияют на инициализацию, запускаемую при первом старте приложения, потребуют от вас перезапуска всего приложения, чтобы вступить в силу.
Чтобы выполнить инкрементальную сборку и перезапустить приложение, нажмите Rerun (CTRL-CMD-r).
Советы и секреты
Instant Run контролируется Android Studio, поэтому запускайте/перезапускайте вашу debug-версию посредством IDE — не делайте этого напрямую с устройства, иначе весь ваш труд полетит коту под хвост.
Более детальный список советов доступен в Android-документации, но здесь приведены самые основные моменты, которые нужно всегда держать в памяти.
- Старайтесь выделить максимум ресурсов для Gradle. Если вы выделите как минимум 2 Гб памяти для Gradle Daemon JVM через jvmargs в файле gradle.properties, то будет включен dex-in-process, что значительно ускорит скорость всех сборок — как Instant Run, так и простых. Вы можете сами поэкспериментировать со значениями настроек и их эффектом на время сборки, чтобы найти оптимальный вариант.
- Возможности, которые предоставляет ART, позволяют вам выжать максимум из Instant Run, если вы присвоите minSdkVersion значение 21 или выше. Вы можете создать новый product flavor специально для дебаггинга и установить значение minSdk=21.
- Не забывайте, что внесение изменений в манифест запустит полный цикл сборки и установки. Поэтому, если ваш процесс сборки автоматически обновляет любую часть манифеста (например, изменяет versionCode или versionName), вам нужно отключить эту опцию в debug build variants.
- Instant Run работает только с основным процессом. Поэтому, если ваше приложение использует несколько процессов, во всех процессах кроме основного вместо горячей и тёплой замены будет применена холодная (либо полная пересборка, если ваш API Level меньше 21).
- Если вы работаете в Windows, Windows Defender может стать причиной медленной работы Instant Run. Вы можете избежать этого, добавив папку с вашим проектом в список исключений Windows Defender.
- На момент написания статьи Instant Run не поддерживал Jack Compiler, инструментальные тесты и деплоинг одновременно на несколько устройств.
Instant Run постоянно развивается, команда разработчиков исследует новые техники для увеличения количества кейсов, позволяющих применять горячую замену и уменьшения необходимость холодных замен или полной пересборки.
Источник