Назад к вопросам
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
    }
}