Назад к вопросам
Middle+
104
questionbank
Расскажите о примерах антипаттернов в разработке на Android и как их избежать.
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Замена findViewById на библиотеку view binding.
<!-- Плохо: ручной поиск -->
<TextView
android:id="@+id/myTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
// Плохо: findViewById
val textView: TextView = findViewById(R.id.myTextView)
textView.text = "Hello"
// Хорошо: в build.gradle (применение view binding)
android {
buildFeatures {
viewBinding true
}
}
// Хорошо: использование view binding
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.myTextView.text = "Hello"
}
Использование синглтона для хранения состояния UI.
// Плохо: синглтон для состояния UI
object AppState {
var isLoggedIn: Boolean = false // Глобальное состояние
}
class LoginActivity : AppCompatActivity() {
// Использование AppState в Activity
}
// Хорошо: использование ViewModel
class LoginViewModel : ViewModel() {
private val _isLoggedIn = MutableLiveData<Boolean>()
val isLoggedIn: LiveData<Boolean> get() = _isLoggedIn
fun login(username: String, password: String) {
// Логика логина
_isLoggedIn.value = true // Обновление состояния в ViewModel
}
}
class LoginActivity : AppCompatActivity() {
private val viewModel: LoginViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Наблюдение за ViewModel
viewModel.isLoggedIn.observe(this, Observer { isLoggedIn ->
// Обновление UI на основе isLoggedIn
})
}
}
Прямое обращение к View из бизнес-логики или других слоев.
// Плохо: прямое обращение к View из класса логики
class LoginManager {
fun performLogin(username: String, password: String, statusTextView: TextView) {
// Логика
statusTextView.text = "Login successful" // Прямое обновление View
}
}
// Хорошо: использование Model-View-ViewModel (MVVM)
class LoginViewModel : ViewModel() {
private val _status = MutableLiveData<String>()
val status: LiveData<String> get() = _status
fun performLogin(username: String, password: String) {
// Логика
_status.value = "Login successful" // Обновление LiveData
}
}
class LoginActivity : AppCompatActivity() {
private val viewModel: LoginViewModel by viewModels()
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// Наблюдение за ViewModel
viewModel.status.observe(this, Observer { status ->
binding.statusTextView.text = status // Обновление View из Observer
})
}
}
Использование контекста для хранения данных или зависимостей.
// Плохо: хранение объекта с Context
class AppContextHolder private constructor(private val context: Context) {
// Хранение и использование Context
}
// Хорошо: инъекция зависимостей (например, Dagger Hilt)
// Пример с Hilt
@HiltAndroidApp
class MyApp : Application()
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase {
// Использование @ApplicationContext
return Room.databaseBuilder(
appContext,
AppDatabase::class.java,
"app_database"
).build()
}
}
Чрезмерное использование статичных полей.
// Плохо: статичные поля для хранения данных
object DataCache {
var cachedData: List<Item>? = null // Статичное поле
}
// Хорошо: использование репозитория с ViewModel
class ItemRepository {
private var cachedData: List<Item>? = null
fun getItems(): List<Item> {
if (cachedData == null) {
// Загрузка данных
}
return cachedData!!
}
}
class ItemViewModel(private val repository: ItemRepository) : ViewModel() {
val items: LiveData<List<Item>> = liveData {
emit(repository.getItems()) // Использование репозитория
}
}
Отсутствие обработки жизненного цикла компонентов.
// Плохо: утечка памяти из-за необработанных слушателей
class MyActivity : AppCompatActivity() {
private val mySensorManager: MySensorManager by lazy { MySensorManager(this) }
override fun onStart() {
super.onStart()
mySensorManager.startListening { data ->
// Обработка данных, но слушатель не удаляется при остановке
}
}
}
// Класс, который можетHolding ссылку на Activity
class MySensorManager(private val context: Context) { // Потенциальная утечка
private var listener: ((String) -> Unit)? = null
fun startListening(listener: (String) -> Unit) {
this.listener = listener
// Запуск слушателя
}
}
// Хорошо: использование Scope или Flow с жизненным циклом
class MyActivity : AppCompatActivity() {
private val mySensorManager: MySensorManager by lazy { MySensorManager() }
override fun onStart() {
super.onStart()
lifecycleScope.launch { // Использование lifecycleScope
mySensorManager.sensorData.collect { data ->
// Обработка данных
}
}
}
}
// Класс с Flow
class MySensorManager {
private val _sensorData = MutableSharedFlow<String>()
val sensorData: SharedFlow<String> = _sensorData
init {
// Запуск сбора данных, emit в _sensorData
}
}