Android Interview Review
4.06K subscribers
232 links
Популярные вопросы и ответы с собеседований на Android-разработчика.

Канал для Java-разработчиков @JavaSobes

Связаться с нами @SobesAdmin

https://itsobes.ru
Download Telegram
to view and join the conversation
Как перевести dp в px?

Значение dp (density-independent pixels) переводится в px (pixels) по формуле:
px = dp * (dpi / 160)
, где dpi (dots-per-inch) – плотность экрана.

Значения dpi устройства соотносятся с квалификаторами ресурсов следующим образом:

-ldpi (low-density) ~120dpi; px = 0.75 * dp.
-mdpi (medium-density) ~160dpi; px = dp.
-hdpi (high-density) ~240dpi; px = 1.5 * dp.
-xhdpi (extra-high-density) ~320dpi; px = 2 * dp.
-xxhdpi (extra-extra-high-density) ~480dpi; px = 3 * dp.
-xxxhdpi (extra-extra-extra-high-density) ~640dpi; px = 4 * dp.

Размеры картинок в drawable-ресурсах должны соответствовать этим соотношениям.
Если для конфигурации текущего устройства нет ресурса, то система выбирает наиболее близкий по dpi и масштабирует до нужного размера.

#Resources
Кто такой Джейк Вортон?

Джейк Вортон (Jake Wharton) – контрибьютор в огромное количество библиотек и селебрити №1 в андроид комьюнити.

Вспомним, какой была разработка под андроид до того, как Джейк захватил наши умы:

• Приложения под андроид разрабатывали студенты, которые работали за еду. Ревьюили и обучали студентов бэкэндщики, потому что они знали Java. Каждое приложение имело свой маленький кровавый энтерпрайз. Некоторые из нас до сих пор видят в кошмарах классы NetworkFacade, DBManager, FactoryBridgeDecoratorInteractor.

• Для загрузки картинок писали свои библиотеки. Для каждого проекта новую, потому что предыдущая вызывала утечки памяти и крэши с OutOfMemoryError.

• О крэшах узнавали из отзывов на Android Market (до того как он стал Play Store), потому что Crashlytics еще не было.

• Google рекомендовал Eclipse как среду разработки.

• Коммит значил пуш, потому что SVN был наше всё.

• Бэкенд возвращал респонсы в XML.

• Проекты собирались тулзой под названием Ant, а о Gradle еще никто не слышал.

• Библиотеки добавлялись в проект в виде скачанных jar-файлов. Если в библиотеке был баг, то скачивали ее исходный код, подключали как модуль приложения и правили баг внутри проекта.

• Из-за этого многие не любили open source и предпочитали писать свои загрузчики картинок.

Но потом явился ОН.

Первой библиотекой Джейка Вортона была ActionBarSherlock, которая портировала функциональность ActionBar под андроид 2.x. Эта библиотека быстро набрала популярность и стала обращать внимание разработчиков на open source решения.

Далее была библиотека Otto (Event Bus). С релизом этой библиотеки коммьюнити впервые стало обсуждать архитектурные подходы в андроид проектах.

После поста Джейка о проблемах Ant, поднялись бурления, и Google стал двигаться в сторону Gradle.

2014-2015 года можно считать ренессансом разработки под Android. Джейк и компания Square, в которой он работал, релизят огромное количество полезных библиотек. В их числе OkHttp, Retrofit и Picassо.
Джейк рассказывает на конференциях о Dagger, RxJava, Kotlin, Annotation Processing и Code Generation. Все к чему он прикасается превращается в золото принимается комьюнити и становится новым мейнстримом.
Многие молодые разработчики выступают на конференциях и контрибьютят в open source, пытаясь повторить успех Джейка, что двигает коммьюнити вперед.

Сегодня нам приходится выбирать лучшее решение из нескольких хороших. Давайте не забывать, кому мы за это благодарны. Вопрос «‎Кто такой Джейк Вортон?»‎ должен быть на каждом собеседовании.

#1апреля
В чем разница между квалификаторами -nodpi и -anydpi?

Директория ресурсов с квалификатором -nodpi предназначена для ресурсов, которые не зависят от плотности экрана. Система не масштабирует картинки из drawables-nodpi/.

Квалификатор -anydpi был добавлен в API v21. Ресурсы из -anydpi подходят для всех конфигураций экрана. Приоритет -anydpi выше, чем у квалификаторов конкретной плотности экрана (-hdpi, -mdpi, и т.д.). Как правило -anydpi используется для векторных картинок.

#Resources
Какие существуют Launch Modes Activity?

Launch Mode – это инструкция, которая говорит системе, как запустить Активити. Launch Mode задается в манифесте атрибутом android:launchMode у элемента <activity>.
Заданный в манифесте Launch Mode можно изменить при старте Активити с помощью Intent-флага.

Существует четыре Launch Modes:

1. standard – используется по-умолчанию;

2. singleTop;

3. singleTask;

4. singleInstance.

Каждый из них разберем подробнее в следующих постах.

#OS
Опишите standard и singleTop launch modes

standard launch mode используется по-умолчанию. В этом режиме активити всегда создается на верхушке бэкстека.

Допустим бэкстек содержит следующие активити: A -> B -> C -> D. D стартует активити B с launch mode standard. Бэкстек будет выглядеть так: A -> B -> C -> D -> B.

singleTop позволяет создать только одну активити данного типа на верхушке бэкстэка.

Пусть бэкстек имеет активити A -> B -> C -> D. D стартует активити С c launch mode singleTop. Бэкстек переходит в состояние A -> B -> C -> D -> C. Если теперь снова запустить С в singleTop, то бэкстек будет выглядеть также, а на активити C вызовется метод onNewIntent().

#Activity
#OS
Опишите singleTask и singleInstance launch modes

В режиме singleTask создается единственный инстанс активити. Если активити не содержится в бэкстеке, то она создается, как при режиме standard. Если же активити была создана ранее, то на ней вызывается метод onNewIntent(). При этом уничтожаются все активити, которые находятся выше в бэкстеке.

Пример 1. Допустим, есть бэкстек A -> B -> C. Активити D запускается в режиме singleTask. Бэкстек переходит в состояние A -> B -> C -> D.

Пример 2. Бэкстек имеет вид A -> B -> C -> D. Активити B запускается в режиме singleTask. Бэкстек переходит в состояние A -> B. На активити B вызывается onNewIntent(), а на активити C и D – onDestroy().

Режим singleInstance позволяет запустить активити в новой задаче. Бэкстек этой задачи будет содержать только созданную активити.

Пример. Пусть бэкстек состоит из активити A -> B -> C. Активити D стартует в режиме singleInstance. В системе создается отдельная задача и бэкстеки выглядят так:
Task 1: A -> B -> C
Task 2: D

Если после этого стартовать активити E в режиме standard и снова запустить D, то бэкстеки будут иметь вид:
Task 1: A -> B -> C -> E
Task 2: D

Если активити уже запущена в режиме singleInstance, то при повторном старте будет вызван метод onNewIntent().

#Activity
#OS
Что такое App Bundle?

App Bundle – это формат архива, который используется для загрузки скомпилированного кода и ресурсов приложения в Google Play Store.

Файл App Bundle имеет расширение .aab, но по сути является zip-архивом, также как .apk и .aar. По своей структуре .aab-пакет похож на .apk-файл. App Bundle содержит dex-файлы, ресурсы и ассеты приложения.

App Bundle используется для сборки .apk-файлов, оптимизированных под конкретные конфигурации устройств.
Если в Play Store загрузить приложение в виде .apk-файла, то этот файл будет доставляться всем пользователям, кто скачивает приложение. Каждый пользователь получит приложение с картинками для всех плотностей экрана, строками для всех языков, и т.д.
Если же опубликовать приложение в виде .aab-файла, то Play Store использует загруженный App Bundle для создания нескольких .apk-файлов. Каждый .apk-файл будет отвечать конкретной конфигурации устройства. При установке приложения пользователь получит только те ресурсы, которые необходимы для корректной работы на его устройстве.

#Build
Что такое Android NDK?

Android NDK (Native Development Kit) – набор инструментов, которые помогают работать с кодом, написанным на языках C и C++.

NDK предоставляет API для доступа к физическим компонентам девайса, таким как сенсоры. Кроме того NDK позволяет скомпилировать и включить в APK C/C++ код, используя Gradle. NDK часто используется в геймдеве для увеличения производительности и для ручного управления памятью.

Для взаимодействия нативного и Java-кода используется JNI или JNA.

#Build
Что такое IdlingResource?

IdlingResource – это механизм тестового фреймворка Espresso. IdlingResource используется для ожидания результата асинхронной операции при выполнении UI теста.
Самые частые случаи использования IdlingResource – это ожидание запроса в сеть или базу данных.

IdlingResource может применяться только к операциям в MessageQueue. Другими словами, в большинстве случаев IdlingResource ожидает обновления UI и не знает ничего о том какие операции выполняются в фоновых потоках.

В случае с ожиданием запроса из сети, IdlingResource регистрируется на обновление Visibility определенной View. Например IdlingResource может блокировать выполнение теста, пока отображается ProgressBar. Как только ProgressBar перешел в состояние GONE, тест возобновляет работу.

#Test
Приведите примеры реализаций IdlingResource, основанных на счетчиках

CountingIdlingResource по типу работы похож на семафор. Внутри CountingIdlingResource хранится счетчик, значение которого меняется методами increment() и decrement(). Когда счетчик принимает значение 0, ресурс переходит в состояние idle.

UriIdlingResource имеет счетчики, ассоциированные с конкретным URI. Значения счетчика меняется методами beginLoad(uri: String) и endLoad(uri: String). При создании UriIdlingResource задается значние timeoutMs. UriIdlingResource переходит в состояние idle когда счетчик равен нулю в течение заданного таймаута.
Этот ресурс полезен, когда выполняется несколько запросов друг за другом. Если в этом случае использовать CountingIdlingResource, то счетчик примет значние 0 после первого запроса и ресурс перейдет в состояние idle.

#Test
Как создать кастомный IdlingResource?

Для этого необходимо реализовать интерфейс IdlingResource, который имеет три метода:

getName() – возвращает имя ресурса;

isIdleNow() – возвращает true, если ресурс idle. При реализации этого метода необходимо задать и проверять критерий перехода в состояние idle кастомного IdlingResource.

registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) – в этом методе предоставляется callback. Этот callback следует сохранить в поле класса, который реализует IdlingResource.
ResourceCallback используется для того, чтобы оповестить тестовый фреймворк о том, что ресурс перешел в состояние idle, до того как вызовется isIdleNow(). Когда IdlingResource становится idle, следует вызывать callback.onTransitionToIdle().

#Test
Как ViewModel переживает пересоздание фрагмента? (1/2)

Ранние версии библиотеки полагались на механизм setRetainInstanceState(), реализация которого подробно описана в статье. На сегодняшний день setRetainInstanceState() больше не используется

Здесь говорим о библиотеках fragment-ktx версии 1.2.4 и lifecycle-viewmodel-ktx версии 2.2.0. Реализация библиотек может быть изменена в будущих версиях.

Для начала разберемся как создается ViewModel:

1. ViewModel рекомендуется создавать через вызов Delegated Property by viewModel, который принимает две функции: ownerProducer: () -> ViewModelStoreOwner и factoryProducer: (() -> Factory)?. По-умолчанию ownerProducer – это функция, возвращающая this, где this – это фрагмент, на котором вызван by viewModel.

2. by viewModel создает и возвращает объект типа ViewModelLazy. При создании этого объекта одним из параметров передается функция storeProducer: () -> ViewModelStore. Эта функция создается следующим образом: { ownerProducer().viewModelStore }, где ownerProducer – функция, заданная на предыдущем шаге.

3. ViewModelLazy имеет один метод get(), который вызывается при обращении к property, заданной через by viewModel. При первом вызове метода get() создается объект ViewModel и сохраняется в классе ViewModelLazy. При последующих вызовах get() возвращается уже созданный ViewModel.

4. Для создания ViewModel сначала создается объект ViewModelProvider, после этого на нем вызывается get() c классом модели в качестве параметра:
ViewModelProvider(store, factory).get(viewModelClass.java)

5. Метод ViewModelProvider.get() создает объект ViewModel с помощью переданной Factory и сохраняет его во ViewModelStore, который хранит значения в HashMap. При последующих вызовах метода ViewModelProvider.get(), ViewModel достается из ViewModelStore.

При описанной реализации граф ссылок на объект ViewModel во фрагменте выглядит так:

Fragment -> ViewModelLazy -> ViewModelProvider -> ViewModelStore -> ViewModel


Т.е. если Fragment уничтожен, то ViewModel тоже будет удалена.
Как же ViewModel переживает пересоздание фрагмента? Ответ кроется во ViewModelStoreOwner, который использовался в by viewModel на первом шаге.

В следующем посте мы разберем как и где сохраняется ViewModelStoreOwner.

#Jetpack
Как ViewModel переживает пересоздание фрагмента? (2/2)

В прошлом посте мы разобрали как создается ViewModel. Теперь рассмотрим как ViewModelStoreOwner переживает пересоздание фрагмента.

Как было упомянуто ранее, by viewModel одним из аргументов принимает функцию ownerProducer: () -> ViewModelStoreOwner. По умолчанию используется функция, возвращающая текущий фрагмент: ownerProducer = { this }.

Класс Fragment из Jetpack реализует интерфейс ViewModelStoreOwner, который имеет только один метод getViewModelStore(): ViewModelStore.
Но сам Fragment не хранит объект ViewModelStore, а делегирует вызов во FragmentManager:

mFragmentManager.getViewModelStore(this), где this – текущий фрагмент


FragmentManager, в свою очередь, делегирует вызов в класс FragmentManagerViewModel. Этот класс хранит HashMap, в котором ключами выступают внутренний uuid фрагмента, а значениями – объекты ViewModelStore. Если HashMap не имеет ViewModelStore для запрашиваемого фрагмента, то создается новый инстанс ViewModelStore и сохраняется в HashMap.

Граф ссылок на ViewModel фрагмента выглядит так:

Fragment (as ViewModelStoreOwner) -> FragmentManager -> FragmentManagerViewModel -> HashMap<String, ViewModelStore> -> ViewModelStore -> ViewModel


FragmentManagerViewModel переживает пересоздание фрагмента, а вместе с ним сохраняется и ViewModel.

Следующий вопрос: как FragmentManagerViewModel переживает пересоздание фрагмента?
Трюк заключается в том, что класс FragmentManagerViewModel – это ViewModel, и он сохраняется также как и любой другой объект ViewModel.
Объектом ViewModelStoreOwner для FragmentManagerViewModel выступает FragmentActivity:

Fragment (as ViewModelStoreOwner) -> FragmentManager -> FragmentManagerViewModel -> FragmentActivity (as ViewModelStore) -> …


В следующем посте разберем, как ViewModel переживает пересоздание активити.

#Jetpack
Как ViewModel переживает пересоздание активити?

В прошлом посте мы разбирали, как ViewModel переживает пересоздание фрагмента. Закончили на том, что класс FragmentManagerViewModel сохраняет объекты ViewModel для фрагмента.

Сам FragmentManagerViewModel – это ViewModel, для которого объектом ViewModelStoreOwner выступает FragmentActivity.

FragmentActivity наследуется от класса ComponentActivity, который реализует метод getViewModelStore(): ViewModelStore интерфейса ViewModelStoreOwner.

ComponentActivity использует переопределенный метод Activity.onRetainNonConfigurationInstance() для сохранения объекта ViewModelStore. Этот метод вызывается между onStop() и onDestroy() и возвращает произвольный объект, который сохраняется системой во время пересоздания активити.

При вызове getViewModelStore(), ComponentActivity получает сохраненный ViewModelStore с помощью метода getLastNonConfigurationInstance().

#Jetpack
Что такое Room?

Room – это библиотека из набора Android Jetpack. Room реализует уровень абстракции над базой данных SQLite.

API библиотеки Room заточено под использование для кэширования данных, полученных с бэкенда.
Сегодня Room – это стандарт де-факто при работе с базой данных в Android-приложениях.

Существуют RxJava и Guava расширения API Room, которые можно подключить отдельной зависимостью.

#Jetpack
Из каких компонентов состоит Room?

Основные компоненты Room:

Database – это точка доступа к базе данных. Класс, аннотированный @Database должен удовлетворять следующим критериям:

1. Быть абстрактным и наследоваться от RoomDatabase;
2. Иметь абстрактные методы без аргументов, которые возвращают DAO-объекты;
3. Аннотация должна включать список Entity-классов, которые принадлежат этой базе данных: @Database(entities = arrayOf(Student::class, School::class)).

DAO (Data Access Object) – класс, который имеет методы для доступа к базе данных.

Entity – класс, отражающий таблицу в базе данных.

Инстанс Database-компонента создается билдером, который можно получить с помощью метода Room.databaseBuilder() или Room.inMemoryDatabaseBuilder().
Database-класс дает доступ к объектам DAO, ассоциированным с базой данных.
DAO-объекты используются для получения и сохранения объектов Entity. А Entity – для получения значений, которые хранятся в таблицах базы данных.

#Jetpack
Какие существуют способы добавить Primary Key в Room?

Primary Key (первичный ключ) – это основной ключ в таблице реляционной базы данных.

Primary Key задается у Entity-класса, который отражает структуру таблицы. В Room необходимо явно задавать Primary Key, даже если Entity-класс имеет только одно поле.

Primary Key можно задать двумя способами:

1. Аннотацией @PrimaryKey у поля класса Entity. Эта аннотация принимает булевый атрибут autoGenerate, который по умолчанию имеет значение false. Если autoGenerate = true, то база данных будет автоматически генерировать значения для Primary Key.

2. Атрибутом primaryKeys у аннотации @Entity (см. скриншот). Этот атрибут используется для создания составного Primary Key.

#Jetpack
Как игнорировать поля родительского Entity-класса в Room?

Room создает колонки в таблице для каждого поля Entity-класса.
Для того, чтобы проигнорировать поле используется аннотация @Ignore.

Для полей, унаследованных из родительского класса, также создаются колонки таблицы. Чтобы проигнорировать родительское поле в классе-наследнике, используется атрибут ignoredColumns аннотации @Entity.

#Jetpack