Форум программистов, компьютерный форум, киберфорум
mobDevWorks
Войти
Регистрация
Восстановить пароль

Вопросы на собеседовании по Android

Запись от mobDevWorks размещена 14.03.2025 в 22:25
Показов 1670 Комментарии 0
Метки android, interview, java, kotlin

Нажмите на изображение для увеличения
Название: 4842807d-0c8d-4d41-a564-e55a17cc8f78.jpg
Просмотров: 170
Размер:	98.0 Кб
ID:	10404
По данным статистики, Android занимает более 70% мирового рынка мобильных операционных систем, что делает платформу привлекательной как для начинающих разработчиков, так и для опытных профессионалов. Конкуренция за рабочие места становится всё жёстче, и работодатели предъявляют всё более высокие требования к кандидатам. В таких условиях подготовка к техническому собеседованию играет критическую роль. Хорошая новость заключается в том, что к большинству вопросов и задач на собеседованиях можно подготовиться заранее. Анализируя собственный опыт прохождения десятков интервью и проведения их как интервьюер, я заметил, что многие вопросы повторяются из компании в компанию. И понимание этих "базовых" вопросов дает серьезное преимущество.

Именно поэтому я решил составить эту статью — своеобразный гид по самым распространенным вопросам на собеседованиях Android-разработчиков. Мы рассмотрим как фундаментальные темы, так и более продвинутые аспекты, добавим практические примеры и обсудим психологические аспекты прохождения интервью. И начать стоит с самого основополагающего — жизненного цикла Activity и Fragment. Эти вопросы задаются почти на каждом собеседовании и удивительно как часто даже опытные разработчики спотыкаются на них.

Фундаментальные вопросы: жизненный цикл Activity и Fragment



Понимание жизненного цикла компонентов Android является краеугольным камнем разработки. Практически каждое собеседование начинается с этих вопросов, поскольку они показывают, насколько глубоко кандидат понимает работу платформы.

Жизненный цикл Activity



Activity — основной компонент пользовательского интерфейса в Android. Его жизненный цикл включает множество состояний, между которыми происходят переходы при взаимодействии пользователя с приложением. Вот ключевые методы жизненного цикла Activity, знание которых часто проверяют на собеседованиях:

onCreate() — вызывается при первом создании Activity. Здесь обычно инициализируют статические компоненты: создают представления, привязывают данные к спискам и т.д. Этот метод получает параметр Bundle, который содержит предыдущее сохраненное состояние, если оно было.

Java
1
2
3
4
5
6
7
8
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // Инициализация компонентов
    TextView textView = findViewById(R.id.textView);
    textView.setText("Инициализировано в onCreate");
}
onStart() — вызывается, когда Activity становится видимым для пользователя. Например, после onCreate() или когда Activity, ранее скрытое, снова становится видимым.
onResume() — вызывается непосредственно перед тем, как Activity начинает взаимодействие с пользователем. Именно здесь стоит запускать анимации или обновлять UI на основе состояния, которое могло измениться, пока приложение было на паузе.
onPause() — вызывается, когда система собирается возобновить работу предыдущей Activity. Этот метод часто используют для сохранения несохраненных данных или остановки действий, потребляющих CPU.
onStop() — вызывается, когда Activity больше не видимо для пользователя. Это может произойти, например, когда запущена новая Activity или свёрнуто приложение.
onDestroy() — вызывается перед уничтожением Activity. Это последний вызов, который получит Activity, прежде чем будет уничтожена.
onRestart() — вызывается после onStop(), когда Activity снова запускается.

Один из хитрых вопросов, который мне задавали на собеседовании: "Какие методы жизненного цикла гарантированно будут вызваны при повороте экрана?" Ответ: Activity полностью пересоздается, поэтому последовательность будет onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume.

Сохранение состояния Activity



Еще одна тема, которую часто затрагивают на собеседованиях — сохранение состояния Activity при повороте экрана или при нехватке памяти.
Для сохранения данных при повороте экрана используйте метод onSaveInstanceState():

Java
1
2
3
4
5
6
7
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString("key_data", "Данные для сохранения");
    // Сохраняем текущую позицию прокрутки
    outState.putInt("scroll_position", scrollView.getScrollY());
}
А для восстановления данных проверяйте Bundle в onCreate() или используйте onRestoreInstanceState():

Java
1
2
3
4
5
6
7
8
9
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState != null) {
        String data = savedInstanceState.getString("key_data");
        int scrollPosition = savedInstanceState.getInt("scroll_position", 0);
        scrollView.post(() -> scrollView.scrollTo(0, scrollPosition));
    }
}

Жизненный цикл Fragment



Фрагменты представляют собой модульные секции Activity, имеющие свой собственный жизненный цикл, но зависящие от жизненного цикла родительской Activity.

Помимо методов, схожих с Activity (onCreate, onStart, onResume, onPause, onStop, onDestroy), у Fragment есть несколько уникальных:
onAttach() — вызывается, когда Fragment прикрепляется к Activity.
onCreateView() — вызывается для создания пользовательского интерфейса Fragment. Здесь вы должны вернуть View, которое будет корнем иерархии вашего Fragment.
onViewCreated() — вызывается сразу после onCreateView(). Это удобное место для доступа к созданным View и их настройки.
onActivityCreated() (устарел в новых версиях Android) — вызывается, когда Activity завершает свой onCreate().
onDestroyView() — вызывается, когда представление Fragment уничтожается.
onDetach() — вызывается, когда Fragment отделяется от Activity.

Базовая реализация Fragment:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, 
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_my, container, false)
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // Настройка UI и инициализация компонентов
        view.findViewById<Button>(R.id.button).setOnClickListener {
            // Обработка клика
        }
    }
}
Я однажды наткнулся на ошибку, когда в Fragment попытался получить доступ к View в методе onCreate(). Приложение крашнулось, и это напомнило мне о важности понимания жизненного цикла — View еще не создано на этом этапе!

Распространенные задачи при работе с Fragment



Вопросы по фрагментам часто выходят за рамки базового жизненного цикла. Несколько тем, которые регулярно всплывают на собеседованиях:

Передача данных между фрагментами



Передача данных между фрагментами — частая задача, и интервьюеры любят проверять, знаете ли вы различные подходы к её решению. Наиболее популярные методы:

1. Через Bundle и аргументы:
Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Создание фрагмента с аргументами
fun newInstance(data: String): DetailFragment {
    val fragment = DetailFragment()
    val args = Bundle()
    args.putString("KEY_DATA", data)
    fragment.arguments = args
    return fragment
}
 
// Получение данных в фрагменте
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    arguments?.let {
        val data = it.getString("KEY_DATA")
        // Использовать data
    }
}
2. Через ViewModel, которая часто является предпочтительным способом:
Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Общая ViewModel
class SharedViewModel : ViewModel() {
    val selectedItem = MutableLiveData<String>()
    
    fun select(item: String) {
        selectedItem.value = item
    }
}
 
// В первом фрагменте
private lateinit var viewModel: SharedViewModel
 
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
    // При клике на элемент
    viewModel.select("выбранные данные")
}
 
// Во втором фрагменте
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
    viewModel.selectedItem.observe(viewLifecycleOwner) { item ->
        // Обновляем UI на основе полученных данных
    }
}

Обработка BackStack и навигация



Работа с BackStack — ещё один популярный вопрос. Фрагменты можно добавлять в BackStack, что позволяет вернуться к предыдущему фрагменту при нажатии кнопки Назад:

Kotlin
1
2
3
4
supportFragmentManager.beginTransaction()
    .replace(R.id.container, newFragment)
    .addToBackStack(null)
    .commit()
В новых проектах часто используется Navigation Component:

Kotlin
1
2
3
4
5
6
7
8
9
10
// В XML-навигационном графе
<navigation>
    <fragment android:id="@+id/listFragment" android:name=".ListFragment">
        <action android:id="@+id/action_to_detail" app:destination="@id/detailFragment" />
    </fragment>
    <fragment android:id="@+id/detailFragment" android:name=".DetailFragment" />
</navigation>
 
// В коде для навигации
findNavController().navigate(R.id.action_to_detail)

Особые случаи Fragment



На собеседовании меня как-то спросили, что произойдет, если вызвать commit() после onSaveInstanceState(). Ответ — возникнет исключение IllegalStateException, поскольку нельзя изменять состояние фрагментов после сохранения состояния Activity. Вместо этого следует использовать commitAllowingStateLoss(), если вы уверены, что потеря состояния в этот момент некритична.

Утечки памяти при работе с Fragment



Еще одна типичная ошибка — утечки памяти в фрагментах. Они часто происходят:

1. При создании статических ссылок на View или Context:
Kotlin
1
2
3
4
companion object {
    // Утечка!
    private var staticViewReference: View? = null
}
2. При использовании анонимных классов, создающих неявную ссылку на внешний класс:
Kotlin
1
2
3
4
5
6
// Потенциальная утечка, если обработчик живёт дольше фрагмента
SomeManager.getInstance().registerCallback(object : Callback {
    override fun onEvent() {
        updateUI() // Ссылка на фрагмент
    }
})
Правильный подход — всегда отписываться от слушателей и освобождать ресурсы в методах жизненного цикла, таких как onDestroyView() или onDestroy():

Kotlin
1
2
3
4
5
override fun onDestroyView() {
    super.onDestroyView()
    SomeManager.getInstance().unregisterCallback(callback)
    binding = null // Очистка ссылки на binding
}
Понимание этих нюансов работы с фрагментами демонстрирует глубокое знание платформы Android и существенно повышает ваши шансы на успешное прохождение технического собеседования.

Для работающих Java-программистом. Вопросы на собеседовании
Народ (кто работает или уже пытался устроиться), можете поделиться вопросами, которые вам задавал работодатель на собеседовании?

Задача на собеседовании
Доброго времени суток, написал задачу с собеседования прошу Вас посмотреть, и указать мои ошибки- недостатки. Код написан в NetBeans вложил в архив....

задача на собеседовании
На днях был на собеседовании, дали такую задачу В деревянные бруски забиты гвозди. Каждый гвоздь на некоторой длине выступает из дерева. Вы...

Задача на собеседовании
Добрый день. Являюсь начинающим разработчиком и пытался проходить одно собеседование на позиции стажера Junior Java и после моего предложенного...


Принципы Material Design и архитектурные паттерны



Material Design в вопросах собеседования



Material Design — это визуальный язык, разработанный Google, определяющий принципы и рекомендации по созданию пользовательского интерфейса для Android-приложений. Знание этих принципов часто проверяется на собеседованиях. Ключевые принципы Material Design, о которых могут спросить:
Материальность и метафора — интерфейс основан на тактильных реалиях бумаги и чернил, но с гибкостью и возможностями цифровых технологий. Материальные поверхности существуют в трехмерном пространстве с освещением и тенями.
Насыщенная анимация — движения естественны и логично отражают взаимодействие пользователя с элементами интерфейса. Один из стандартных вопросов: "Как реализовать анимацию перехода между активностями?"

Kotlin
1
2
3
4
5
6
7
// Настройка анимации перехода
val options = ActivityOptions.makeSceneTransitionAnimation(
    this,
    Pair(imageView, "shared_image")
)
val intent = Intent(this, DetailActivity::class.java)
startActivity(intent, options.toBundle())
Адаптивный дизайн — интерфейс должен корректно отображаться на устройствах с различными размерами экранов. Впервые я столкнулся с этим вопросом, когда меня спросили, как я буду реализовывать разный UI для телефонов и планшетов.
Компоненты Material Design — список часто упоминаемых в вопросах компонентов:
BottomNavigationView
NavigationDrawer
FloatingActionButton (FAB)
CardView
RecyclerView
ConstraintLayout
CoordinatorLayout

Чаще всего на собеседовании просят описать отличия между различными компонентами и объяснить, когда и как их использовать. Например, в чем разница между BottomNavigationView и NavigationDrawer?
Ответ: BottomNavigationView идеален для 3-5 основных разделов приложения, обеспечивает быстрый доступ к ним и должен использоваться на всех экранах приложения. NavigationDrawer подходит для приложений с более сложной структурой, когда есть много разделов или когда требуется иерархическая навигация.

Архитектурные паттерны



Понимание архитектурных паттернов, пожалуй, самый ценный скилл, который проверяют рекрутеры. Я до сих пор помню, как на одном из собеседований полчаса обсуждали с интервьюером, почему я выбрал MVVM для своего pet-проекта.

MVC (Model-View-Controller)



Классический паттерн, где:
Model — данные и бизнес-логика,
View — UI,
Controller — связующее звено между Model и View.

В контексте Android, Activity или Fragment часто служат и View, и Controller, что размывает границы и приводит к "Божественным активностям" (God Activities) — огромным классам, трудным для поддержки.

MVP (Model-View-Presenter)



Улучшенная версия MVC для Android:
Model — данные и бизнес-логика,
View — UI (Activity, Fragment),
Presenter — посредник между View и Model.

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Пример интерфейса View в MVP
interface MainView {
    fun showLoading()
    fun hideLoading()
    fun showUsers(users: List<User>)
    fun showError(message: String)
}
 
// Пример Presenter
class MainPresenter(
    private val view: MainView,
    private val repository: UserRepository
) {
    fun loadUsers() {
        view.showLoading()
        repository.getUsers(object : Callback<List<User>> {
            override fun onSuccess(data: List<User>) {
                view.hideLoading()
                view.showUsers(data)
            }
 
            override fun onError(error: Exception) {
                view.hideLoading()
                view.showError(error.message ?: "Неизвестная ошибка")
            }
        })
    }
}
MVP решает проблему смешивания логики и UI, но создает сильную связь между Presenter и View.

MVVM (Model-View-ViewModel)



Наиболее популярный паттерн в современной Android-разработке:
Model — данные и бизнес-логика,
View — UI (Activity, Fragment),
ViewModel — промежуточный слой, который подготавливает данные для View и реагирует на пользовательские действия.

MVVM использует механизм привязки данных (data binding) или наблюдаемые потоки данных (LiveData, Flow), что делает архитектуру более реактивной:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// ViewModel в MVVM
class UserViewModel(private val repository: UserRepository) : ViewModel() {
    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> = _users
 
    private val _loading = MutableLiveData<Boolean>()
    val loading: LiveData<Boolean> = _loading
 
    private val _error = MutableLiveData<String?>()
    val error: LiveData<String?> = _error
 
    fun loadUsers() {
        _loading.value = true
        viewModelScope.launch {
            try {
                val result = repository.getUsers()
                _users.value = result
                _error.value = null
            } catch (e: Exception) {
                _error.value = e.message
            } finally {
                _loading.value = false
            }
        }
    }
}
В View (Activity или Fragment) мы просто наблюдаем за изменениями:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    viewModel.users.observe(viewLifecycleOwner) { users ->
        adapter.submitList(users)
    }
    
    viewModel.loading.observe(viewLifecycleOwner) { loading ->
        progressBar.isVisible = loading
    }
    
    viewModel.error.observe(viewLifecycleOwner) { error ->
        error?.let { errorTextView.text = it }
        errorContainer.isVisible = error != null
    }
    
    viewModel.loadUsers()
}

MVI (Model-View-Intent)



Относительно новый паттерн, основанный на односторонних потоках данных (unidirectional data flow):
Model — состояние UI,
View — отображает состояние и отправляет намерения (intents),
Intent — действия пользователя или системы, которые влияют на состояние.

MVI создает предсказуемую и отслеживаемую архитектуру, особенно хорошо работает с Kotlin Flow и StateFlow:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Состояние UI
data class UiState(
    val users: List<User> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null
)
 
// Действия
sealed class UserIntent {
    object LoadUsers : UserIntent()
    object RefreshUsers : UserIntent()
    data class FilterUsers(val query: String) : UserIntent()
}
 
// ViewModel в стиле MVI
class UserViewModel(private val repository: UserRepository) : ViewModel() {
    private val _state = MutableStateFlow(UiState())
    val state: StateFlow<UiState> = _state.asStateFlow()
 
    fun processIntent(intent: UserIntent) {
        when (intent) {
            is UserIntent.LoadUsers -> loadUsers()
            is UserIntent.RefreshUsers -> loadUsers(forceRefresh = true)
            is UserIntent.FilterUsers -> filterUsers(intent.query)
        }
    }
 
    private fun loadUsers(forceRefresh: Boolean = false) {
        viewModelScope.launch {
            _state.update { it.copy(isLoading = true, error = null) }
            try {
                val users = repository.getUsers(forceRefresh)
                _state.update { it.copy(users = users, isLoading = false) }
            } catch (e: Exception) {
                _state.update { it.copy(isLoading = false, error = e.message) }
            }
        }
    }
 
    private fun filterUsers(query: String) {
        // Логика фильтрации
    }
}
Выбор архитектурного паттерна сильно зависит от размера проекта, команды и уже используемой архитектуры. На собеседовании важно не только знать, как работает каждый паттерн, но и уметь объяснить их преимущества и недостатки, а также ситуации, когда один паттерн предпочтительнее другого.

Когда меня спрашивали о предпочитаемой архитектуре, я всегда говорил, что в небольших проектах предпочитаю MVVM из-за его простоты и хорошей интеграции с Android Architecture Components, а для сложных проектов с большой командой рассматриваю MVI или многомодульную архитектуру с элементами Clean Architecture.

Особенности работы с потоками и хранением данных



Многопоточность в Android



Работа с потоками в Android — тема, которая вызывает множество вопросов на собеседованиях. Основной принцип, который должен знать каждый Android-разработчик: главный (UI) поток предназначен исключительно для операций с пользовательским интерфейсом. Все тяжёлые операции, такие как запросы в сеть, работа с базой данных или сложные вычисления, должны выполняться в фоновых потоках.

Способы работы с потоками, о которых обязательно спросят на собеседовании:

Thread и Runnable



Базовый механизм создания потоков в Java и Android:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new Thread(new Runnable() {
    @Override
    public void run() {
        // Выполняем долгую операцию
        final String result = fetchDataFromServer();
        
        // Обновляем UI в главном потоке
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText(result);
            }
        });
    }
}).start();
Главный недостаток этого подхода — управление жизненным циклом потока. Если Activity или Fragment уничтожены, поток продолжит работу, что может привести к утечке памяти или к исключениям при попытке обновить несуществующий UI.

AsyncTask (устаревший)



AsyncTask был популярным решением для выполнения фоновых операций:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private class MyAsyncTask extends AsyncTask<Void, Integer, String> {
    @Override
    protected String doInBackground(Void... voids) {
        // Выполняем долгую операцию
        return fetchDataFromServer();
    }
 
    @Override
    protected void onProgressUpdate(Integer... values) {
        // Обновляем прогресс
        progressBar.setProgress(values[0]);
    }
 
    @Override
    protected void onPostExecute(String result) {
        // Обновляем UI с результатом
        textView.setText(result);
    }
}
 
// Вызов
new MyAsyncTask().execute();
Однако в новых версиях Android AsyncTask помечен как устаревший, и на собеседовании вам могут задать вопрос: "Почему не стоит использовать AsyncTask?" Ответ: AsyncTask имеет проблемы с жизненным циклом, не переживает поворот экрана и имеет другие ограничения, например, выполняется в одном потоке, начиная с Android 3.0.

Handler и HandlerThread



Другой подход — использование Handler:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Создаём HandlerThread для фоновых операций
HandlerThread handlerThread = new HandlerThread("BackgroundThread");
handlerThread.start();
 
// Создаём Handler, привязанный к этому потоку
Handler backgroundHandler = new Handler(handlerThread.getLooper());
 
// Выполняем операцию в фоновом потоке
backgroundHandler.post(new Runnable() {
    @Override
    public void run() {
        final String result = fetchDataFromServer();
        
        // Создаём Handler для основного потока
        Handler mainHandler = new Handler(Looper.getMainLooper());
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                textView.setText(result);
            }
        });
    }
});
 
// Не забываем остановить HandlerThread при выходе
@Override
protected void onDestroy() {
    super.onDestroy();
    handlerThread.quit();
}
Handler позволяет отправлять и обрабатывать сообщения и Runnable объекты в очередь сообщений, связанную с потоком.

ExecutorService



Интерфейс ExecutorService предоставляет методы для управления пулами потоков и задачами:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
    @Override
    public void run() {
        String result = fetchDataFromServer();
        
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText(result);
            }
        });
    }
});
 
// Закрытие ExecutorService
executor.shutdown();
Этот подход предпочтительнее прямого создания потоков, т.к. позволяет эффективно переиспользовать потоки.

WorkManager



WorkManager — современное API для выполнения отложенных фоновых задач:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
    .setInputData(workDataOf("key" to "value"))
    .setConstraints(Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build())
    .build()
 
WorkManager.getInstance(context).enqueue(workRequest)
 
// Определение Worker
class MyWorker(context: Context, params: WorkerParameters) 
    : Worker(context, params) {
    
    override fun doWork(): Result {
        val inputData = inputData.getString("key") ?: ""
        
        // Выполняем долгую операцию
        // ...
        
        // Возвращаем результат
        val outputData = workDataOf("result" to "completed")
        return Result.success(outputData)
    }
}
WorkManager помогает выполнять задачи даже если приложение закрыто или устройство перезагружено. Он умеет запускать задачи по расписанию, с ограничениями (например, только при наличии Wi-Fi) и гарантирует выполнение задачи.

Механизмы хранения данных



Вторым важным аспектом, который часто проверяют на собеседованиях, являются механизмы хранения данных в Android.

SharedPreferences



Самый простой механизм для хранения небольших наборов ключ-значение:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
// Запись данных
val sharedPrefs = getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
val editor = sharedPrefs.edit()
editor.putString("username", "VasukiDr")
editor.putInt("score", 100)
editor.apply() // асинхронная запись
// или editor.commit() - синхронная запись
 
// Чтение данных
val username = sharedPrefs.getString("username", "")
val score = sharedPrefs.getInt("score", 0)
На собеседовании могут спросить о разнице между apply() и commit(). Ответ: apply() записывает изменения асинхронно и не возвращает результат, в то время как commit() записывает синхронно и возвращает boolean, указывающий, была ли запись успешной.

Room Database



Room — это библиотека для работы с SQLite баз данными:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Определение Entity
@Entity(tableName = "users")
data class User(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "name") val name: String,
    @ColumnInfo(name = "age") val age: Int
)
 
// DAO (Data Access Object)
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAll(): List<User>
    
    @Insert
    fun insert(user: User)
    
    @Delete
    fun delete(user: User)
}
 
// Database
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
 
// Инициализация базы данных
val db = Room.databaseBuilder(
    applicationContext,
    AppDatabase::class.java, "my-database"
).build()
 
// Использование
viewModelScope.launch(Dispatchers.IO) {
    val users = db.userDao().getAll()
    withContext(Dispatchers.Main) {
        // Обновить UI
    }
}
На собеседованиях часто спрашивают, как правильно организовать работу с Room, чтобы избежать блокировки UI-потока. Ответ: все операции с базой данных должны выполняться в фоновом потоке. Room автоматически выбрасывает исключение, если вы пытаетесь обратиться к базе данных из главного потока, если не настроили явно иное поведение через .allowMainThreadQueries().

DataStore



DataStore — новый механизм хранения данных, который пришёл на смену SharedPreferences:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Определение схемы хранения
val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
 
// Создание DataStore
val dataStore = context.createDataStore(
    name = "settings"
)
 
// Запись данных
viewModelScope.launch {
    dataStore.edit { settings ->
        val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
        settings[EXAMPLE_COUNTER] = currentCounterValue + 1
    }
}
 
// Чтение данных
val exampleCounterFlow: Flow<Int> = dataStore.data
    .map { preferences ->
        preferences[EXAMPLE_COUNTER] ?: 0
    }
В отличие от SharedPreferences, DataStore полностью основан на Kotlin Coroutines и Flow, что делает его более современным и удобным в использовании. При этом он не блокирует основной поток и поддерживает транзакции, обеспечивая более надёжное хранение данных.

ContentProvider



ContentProvider используется для совместного использования данных между приложениями. Это более сложный механизм, но некоторые системные функции, такие как выбор изображений или контактов, работают через ContentProvider. На собеседовании могут попросить объяснить, для чего он нужен и как с ним работать:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Пример запроса к ContentProvider для получения контактов
val contentResolver = contentResolver
val cursor = contentResolver.query(
    ContactsContract.Contacts.CONTENT_URI,
    arrayOf(ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME),
    null,
    null,
    null
)
 
cursor?.use { 
    while (it.moveToNext()) {
        val id = it.getString(it.getColumnIndex(ContactsContract.Contacts._ID))
        val name = it.getString(it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
        // Обработка данных
    }
}

Продвинутые темы: работа с сетью и оптимизация производительности



Если на собеседовании вы уверенно справились с базовыми вопросами, то следующий блок почти наверняка будет посвящён работе с сетью и оптимизации приложения. Эти темы показывают насколько глубоко кандидат понимает внутренние механизмы Android и умеет создавать эффективные приложения.

Работа с сетевыми запросами



На собеседованиях часто задают вопросы о том, как правильно организовать сетевое взаимодействие в Android-приложениях. Наиболее популярным инструментом является Retrofit — библиотека для типобезопасной работы с REST API. Типичная реализация работы с Retrofit:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Определение API интерфейса
interface ApiService {
    @GET("users")
    suspend fun getUsers(): List<User>
    
    @GET("users/{id}")
    suspend fun getUserById(@Path("id") userId: Int): User
    
    @POST("users")
    suspend fun createUser(@Body user: User): Response<User>
}
 
// Настройка Retrofit
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()
 
val apiService = retrofit.create(ApiService::class.java)
 
// Использование в корутине
viewModelScope.launch {
    try {
        val users = apiService.getUsers()
        // Обработка результата
    } catch (e: Exception) {
        // Обработка ошибки
    }
}
Один из сложных вопросов на собеседовании: "Как обрабатывать токены авторизации и автоматическое обновление токенов при помощи Retrofit?" Решение этой задачи обычно включает создание Interceptor:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class AuthInterceptor(
    private val tokenProvider: TokenProvider
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val token = tokenProvider.getToken()
        
        val requestWithToken = originalRequest.newBuilder()
            .header("Authorization", "Bearer $token")
            .build()
            
        val response = chain.proceed(requestWithToken)
        
        if (response.code == 401) { // Unauthorized
            synchronized(this) {
                val newToken = tokenProvider.refreshToken()
                if (newToken != null) {
                    val newRequest = originalRequest.newBuilder()
                        .header("Authorization", "Bearer $newToken")
                        .build()
                    return chain.proceed(newRequest)
                }
            }
        }
        
        return response
    }
}

Кэширование и офлайн-режим



Еще одна важная тема — это организация кэширования данных для работы в офлайн-режиме. Хорошее решение — использование паттерна Repository с кэшированием в локальной базе данных:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class UserRepository(
    private val apiService: ApiService,
    private val userDao: UserDao
) {
    suspend fun getUsers(forceRefresh: Boolean = false): List<User> {
        // Если не нужно обновлять из сети и в кэше есть данные
        if (!forceRefresh) {
            val cachedUsers = userDao.getAll()
            if (cachedUsers.isNotEmpty()) {
                return cachedUsers
            }
        }
        
        // Если нужно обновить или кэш пуст
        return try {
            val users = apiService.getUsers()
            userDao.insertAll(users)
            users
        } catch (e: IOException) {
            // При проблемах с сетью вернуть кэш, если он есть
            val cachedUsers = userDao.getAll()
            if (cachedUsers.isNotEmpty()) {
                return cachedUsers
            }
            throw e
        }
    }
}

Оптимизация производительности



Вторая часть этого блока вопросов обычно касается оптимизации приложения. Интервьюеры часто спрашивают:

Как обнаружить утечки памяти?



Основной инструмент — Android Profiler, который позволяет мониторить использование памяти, CPU и сети. При обнаружении утечки памяти (например, при постоянном росте потребления памяти при повторении одних и тех же действий) можно сделать dump памяти и проанализировать его с помощью инструментов вроде LeakCanary:

Kotlin
1
2
3
dependencies {
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}

Как оптимизировать RecyclerView?



RecyclerView — один из самых критичных для производительности UI-компонентов. Типичные оптимизации:

1. Использовать DiffUtil для эффективного обновления списка:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
val diffCallback = object : DiffUtil.ItemCallback<User>() {
    override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
        return oldItem.id == newItem.id
    }
 
    override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
        return oldItem == newItem
    }
}
 
val adapter = UserAdapter(diffCallback)
2. Использовать RecycledViewPool для переиспользования views между несколькими RecyclerView:

Kotlin
1
2
3
val viewPool = RecyclerView.RecycledViewPool()
recyclerView1.setRecycledViewPool(viewPool)
recyclerView2.setRecycledViewPool(viewPool)
3. Избегать сложных вычислений и загрузки изображений в методе onBindViewHolder.

Kotlin Coroutines, Flow и Dependency Injection



Kotlin Coroutines



Kotlin Coroutines — это мощный инструмент для асинхронного программирования, который произвел революцию в Android-разработке. Если на собеседовании спросят о преимуществах корутин перед другими механизмами асинхронной обработки, можно отметить:
  • Последовательный стиль кода вместо колбэков.
  • Лёгкость (не блокируют потоки).
  • Встроенная отмена и обработка исключений.
  • Структурированный подход к конкурентности.

Базовая работа с корутинами выглядит так:

Kotlin
1
2
3
4
5
6
7
8
9
// Запуск корутины в ViewModel
viewModelScope.launch {
    try {
        val users = userRepository.getUsers()
        _uiState.value = UiState.Success(users)
    } catch (e: Exception) {
        _uiState.value = UiState.Error(e.message)
    }
}
Один из самых коварных вопросов на собеседовании: "В чём разница между launch, async и withContext?" Ответ:
launch запускает корутину и не возвращает результат (fire-and-forget).
async запускает корутину и возвращает отложенный результат (Deferred).
withContext просто переключает контекст выполнения и ждёт завершения блока.

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Параллельное выполнение запросов с async
viewModelScope.launch {
    val usersDeferred = async { api.getUsers() }
    val postsDeferred = async { api.getPosts() }
    
    val users = usersDeferred.await()
    val posts = postsDeferred.await()
    
    // Обработка результатов
}
 
// Переключение контекста с withContext
viewModelScope.launch {
    val result = withContext(Dispatchers.IO) {
        // Тяжелая операция в IO-потоке
        database.getItems()
    }
    // Продолжение в основном потоке
    updateUI(result)
}
CoroutineScope и диспетчеры — еще одна важная тема. На собеседованиях часто спрашивают: "Какие типы CoroutineScope вы знаете и где их применять?"
  • viewModelScope — привязан к жизненному циклу ViewModel.
  • lifecycleScope — привязан к жизненному циклу Activity/Fragment.
  • GlobalScope — живет на протяжении всего приложения (использовать с осторожностью).

Диспетчеры определяют, в каком потоке будет выполняться корутина:
  • Dispatchers.Main — UI-поток.
  • Dispatchers.IO — оптимизирован для операций ввода/вывода.
  • Dispatchers.Default — оптимизирован для CPU-интенсивных задач.

Flow



Flow — это реактивный поток данных, построенный на корутинах. Он заменяет LiveData в более сложных сценариях и предоставляет богатые возможности для трансформации данных:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class UserRepository(private val api: ApiService, private val db: UserDatabase) {
    fun getUsers(): Flow<List<User>> = flow {
        // Сначала эмитим данные из базы
        emit(db.getUsers())
        
        // Затем получаем с сервера
        try {
            val fresh = api.getUsers()
            db.saveUsers(fresh)
            emit(fresh)
        } catch (e: Exception) {
            // Ошибка обрабатывается, поток не прерывается
        }
    }.flowOn(Dispatchers.IO)
}
 
// Использование в ViewModel
val users = userRepository.getUsers()
    .map { users -> users.sorted() }
    .catch { e -> _error.value = e.message }
    .stateIn(
        viewModelScope,
        SharingStarted.WhileSubscribed(5000),
        emptyList()
    )
Важно понимать разницу между разными типами Flow:
  • Flow — холодный поток, запускается при сборе.
  • StateFlow — горячий поток с состоянием, всегда имеет значение.
  • SharedFlow — горячий настраиваемый поток без состояния.

Dependency Injection



Dependency Injection (DI) — паттерн, при котором зависимости объекта предоставляются извне, а не создаются внутри. Это улучшает тестируемость, модульность и гибкость кода.

Dagger 2



Dagger 2 долгое время был стандартом для DI в Android:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Module
class NetworkModule {
    @Provides
    @Singleton
    fun provideApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }
    
    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}
 
@Component(modules = [NetworkModule::class, DatabaseModule::class])
@Singleton
interface AppComponent {
    fun inject(activity: MainActivity)
    fun inject(fragment: UserFragment)
}

Hilt



Hilt — более новое официальное решение от Google, основанное на Dagger, но с меньшим количеством шаблонного кода:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@HiltAndroidApp
class MyApplication : Application()
 
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var userRepository: UserRepository
    
    // ...
}
 
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Provides
    @Singleton
    fun provideUserRepository(
        api: ApiService,
        db: UserDatabase
    ): UserRepository {
        return UserRepositoryImpl(api, db)
    }
}

Koin



Koin — лёгкая библиотека DI для Kotlin, которая использует DSL вместо аннотаций:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
val appModule = module {
    single { Room.databaseBuilder(get(), AppDatabase::class.java, "app-db").build() }
    single { get<AppDatabase>().userDao() }
    single { Retrofit.Builder().baseUrl("https://api.example.com").build().create(ApiService::class.java) }
    single { UserRepositoryImpl(get(), get()) as UserRepository }
    viewModel { UserViewModel(get()) }
}
 
// В Application
startKoin {
    androidContext(this@MyApplication)
    modules(appModule)
}
 
// Использование
class UserFragment : Fragment() {
    private val viewModel: UserViewModel by viewModel()
}
Выбор между этими инструментами часто вызывает споры на собеседованиях. Важно уметь объяснить, почему вы предпочитаете тот или иной инструмент, учитывая размер проекта, опыт команды и требуемую гибкость.

Jetpack Compose и практические задания на собеседовании



Jetpack Compose: основные вопросы



Jetpack Compose — относительно новый инструментарий для разработки UI в Android, который использует декларативный подход и Kotlin. Если у вас в резюме указан опыт с Compose, то будьте готовы отвечать на подробные вопросы.
Несколько распространённых вопросов по Compose, которые могут встретиться на собеседовании:

1. Что такое Composable-функции и как их объявлять?

Composable-функции — это функции с аннотацией @Composable, которые описывают часть пользовательского интерфейса:

Kotlin
1
2
3
4
@Composable
fun Greeting(name: String) {
    Text(text = "Привет, $name!")
}
2. В чём разница между remember и rememberSaveable?

remember сохраняет состояние только до перекомпозиции, а rememberSaveable сохраняет состояние даже при пересоздании Activity (например, при повороте экрана):

Kotlin
1
2
3
4
5
// Не переживает пересоздание Activity
val count = remember { mutableStateOf(0) }
 
// Переживает пересоздание Activity
val persistentCount = rememberSaveable { mutableStateOf(0) }
3. Что такое Recomposition и когда она происходит?

Recomposition — это процесс повторного вызова Composable-функций при изменении их состояния. Происходит только для тех функций, состояние которых изменилось, что делает обновление UI эффективным.

4. Как работать с состоянием в Compose?

Состояние управляется через объекты State:

Kotlin
1
2
3
4
5
val expanded = remember { mutableStateOf(false) }
 
Button(onClick = { expanded.value = !expanded.value }) {
    Text(if (expanded.value) "Свернуть" else "Развернуть")
}
5. В чём разница между LazyColumn и Column?

LazyColumn — аналог RecyclerView, создаёт только видимые элементы, подходит для длинных списков. Column рендерит все элементы сразу, используется для короткого, предсказуемого содержимого.

Практические задания на собеседованиях



Помимо теоретических вопросов, на техническом интервью вам почти наверняка придётся выполнять практические задания. Вот наиболее распространённые типы:

1. Алгоритмические задачи



Часто просят решить несложную алгоритмическую задачу, чтобы оценить ваше логическое мышление:
  • Найти палиндром в строке.
  • Отсортировать массив по определённому правилу.
  • Реализовать поиск в дереве.

Мой совет: потренируйтесь в решении таких задач на LeetCode или HackerRank перед собеседованием.

2. Тестирование кода



Могут попросить написать unit-тесты для определённого фрагмента кода:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
@Test
fun `когда введён корректный email, функция возвращает true`() {
    // Arrange
    val emailValidator = EmailValidator()
    val validEmail = "test@example.com"
    
    // Act
    val result = emailValidator.isValid(validEmail)
    
    // Assert
    Truth.assertThat(result).isTrue()
}

3. Refactoring



Может быть дан фрагмент кода с плохими практиками, который нужно улучшить:

Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// До рефакторинга
fun process() {
    val data = getData() // Блокирующий вызов API
    val processed = data.map { it.uppercase() }
    saveToDatabase(processed) // Блокирующий вызов БД
    updateUI(processed)
}
 
// После рефакторинга
fun process() {
    viewModelScope.launch {
        try {
            val data = withContext(Dispatchers.IO) { getData() }
            val processed = data.map { it.uppercase() }
            withContext(Dispatchers.IO) { saveToDatabase(processed) }
            updateUI(processed)
        } catch (e: Exception) {
            handleError(e)
        }
    }
}

4. Live coding



Возможно самая стрессовая часть собеседования — написание кода в режиме реального времени. Обычно дают задачу реализовать несложную функциональность:
  • Создать экран списка элементов с загрузкой из сети.
  • Реализовать диалог с формой ввода и валидацией.
  • Написать простой алгоритм поиска или сортировки.

Ключ к успеху в таких заданиях — спокойствие и методичность. Обсуждайте свой подход с интервьюером, задавайте уточняющие вопросы, думайте вслух. Лучше написать рабочее, пусть даже не идеальное решение, чем застрять в попытках найти идеальный алгоритм.

Психологические аспекты и техники самопрезентации



Техническая подготовка — лишь половина успеха на собеседовании. Вторая половина — умение эффективно презентовать свои навыки и справляться с психологическим давлением. Как человек, прошедший через множество собеседований, могу сказать: часто побеждает не самый технически подкованный кандидат, а тот, кто лучше умеет продать себя.

Как справиться с волнением



Волнение перед собеседованием — абсолютно нормальное явление. Даже опытные разработчики испытывают стресс. Вот несколько проверенных способов снижения тревожности:
  • Подготовка к худшему сценарию. Представьте самые сложные вопросы и подготовьте ответы.
  • Дыхательные техники. Глубокое дыхание по схеме 4-7-8 (вдох на 4 счёта, задержка на 7, выдох на 8) помогает снизить уровень стресса.
  • Визуализация успеха. Представьте, как успешно отвечаете на вопросы и получаете оффер.

Помню свой опыт: на собеседовании в крупную компанию я так волновался, что забыл, как реализуется Singleton в Kotlin. Мозг просто "завис". Интервьюер, заметив это, предложил сделать паузу и выпить воды. Этот небольшой перерыв помог мне собраться и продолжить.

Техники ответов на "сложные" вопросы



Существуют вопросы, которые часто ставят кандидатов в тупик:

"Расскажите о своём крупнейшем провале"

Плохой ответ: "У меня не было провалов" или "Однажды я полностью завалил проект из-за своей некомпетентности".
Хороший ответ: "В проекте X я недооценил сложность интеграции с платёжной системой, что привело к задержке релиза. Я извлёк урок — всегда выделять больше времени на изучение новых API и составлять подробный план интеграции с тестовыми сценариями".

"Какая ваша самая слабая сторона как разработчика?"

Стратегия ответа: укажите реальную слабость, но не критичную для позиции, и расскажите, как вы над ней работаете.
"Иногда я слишком долго оптимизирую код, стремясь к идеальному решению. Я работаю над этим, устанавливая конкретные временные рамки и фокусируясь на MVP-подходе — сначала рабочее решение, потом улучшения".

Как выгодно представить свои проекты



При обсуждении прошлого опыта используйте структуру "Ситуация - Задача - Действие - Результат":
Ситуация: "В нашем приложении с миллионом пользователей..."
Задача: "Нужно было уменьшить потребление трафика на 30%..."
Действие: "Я реализовал стратегию кэширования и оптимизировал размер загружаемых изображений..."
Результат: "Потребление трафика снизилось на 45%, рейтинг в магазине вырос с 4.2 до 4.6".

Во время рассказа выделяйте именно свой вклад, используя "я" вместо размытого "мы", но не приписывайте себе чужие заслуги.

Вопросы интервьюеру



Ваши вопросы в конце собеседования — ещё одна возможность произвести впечатление:
"Какие ключевые метрики успеха для этой позиции в первые 6 месяцев?"
"Какие самые сложные технические задачи стоят перед командой в ближайшее время?"
"Как организована работа с техническим долгом в проекте?"

Избегайте вопросов только о зарплате, отпуске или удалёнке — это создаст впечатление, что вас интересуют лишь бонусы, а не сама работа.

Задание на собеседовании - в чём ошибка?
Вот что попалось: Отпишите кто может. Всё-таки интересно какие же тут есть ошибки.

Я школьник (9 класс). Хочу изучать Java для Android/Android TV в особенности
Вообщем, задал вопрос на Mail.ru ответах по поводу Java. Говорят, лучше сейчас изучать уже начинать. Так вот: как? В яндексе рекламируют...

Не работает мобильный интернет после перепрошивки на с Android 4.2.2 на Android 4.2.2
Всем привет. После перепрошивки телефона почему-то перестал работать мобильный интернет. До этого все было гуд. Прошивал с помощью...

Виртуальный android на реальном android-смартфоне. Реально ли?
Имею смартфон с android 5.1 lollipop(philips xenium v377). Есть ли какие-то виртуализаторы систем? Virtualbox для android нету, bochs, qemu...

Оболочка Android 4 на Android 5+. Как сделать?
Всем привет! Я собрался покупать новый телефон. Но как я в последнее время вижу, что нынешние Android оболочки выглядят довольно убого и так себе...

Как вернуться от прошивки Android 4.0 к Android 2.3?
Как вернуться от прошивки Android 4.0 к Android 2.3? Телефон Sony Ericsson xperia ark. Обновил до последней прошивки, но пожалел об этом. Телефон...

Стили в Android. Есть ли аналог firebug или инструментов разработчика как в Chrome? Как верстают под android?
Здравствуйте, дорогие форумчане! Стою в начале пути разработки под Android! В ходе разработки первых приложений столкнулся с кучей проблем в работе с...

Портирование приложения с Android 9 на Android 8.1
Всем доброго времени суток! Может кто поможет портировать приложение с Android 9 на Android 8.1?

Вопросы по Java
Пожалуйста, помогите с 21м и 23м вопросами из вложения. Возможен вариант за вознаграждение

Вопросы по java
Всем привет! Я студентка и только начала изучать Java. Помогите пожалуйста решить следующую задачу. Буду Вам очень признательна! Задание: Напишите...

Вопросы по TextArea
Подскажите, плииз: Есть обыкновенный java.awt.TextArea в Апплете 1. Как сделать так чтобы набираемый с клавиватуры в него текст перекрывал...

Вопросы по Swing...
Доброго времени суток! Опишу проблему: В JFrame заданих размеров(например 480 на 640) вводиться текст из файла. Когда достигаеться высота...

Метки android, interview, java, kotlin
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru