View и Context нельзя получать в конструкторе Fragment; работу нужно привязать к lifecycle, геолокацию — к разрешениям, а UI обновлять только на Main. Порог проверяют после увеличения счётчика.
Исправление багов во View‑фрагменте, потоках и работе с LocationManager
View и Context нельзя получать в конструкторе Fragment; работу нужно привязать к lifecycle, геолокацию — к разрешениям, а UI обновлять только на Main. Порог проверяют после увеличения счётчика.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Найдите и исправьте ошибки в следующем коде фрагмента на Kotlin. Программа должна показывать количество нажатий, менять текст кнопки и после достижения нужного количества запускать запрос к LocationManager из отдельного потока, однако в текущей реализации нарушены правила жизненного цикла фрагмента и работы с потоками. Предоставьте исправленную версию.
class Frmt(var targetCount: Int) : Fragment() {
private val contextRef = requireContext()
private val textView: TextView = requireView().findViewById(R.id.tv)
private val button: Button = requireView().findViewById(R.id.btn)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.asdasd, container, true)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
var clicks = 0
textView.text = "Для подтверждения операции нажмите далее $targetCount раз"
button.text = "Нажать"
button.setOnClickListener {
if (clicks < targetCount) {
clicks += 1
} else {
Thread {
(contextRef.getSystemService(Context.LOCATION_SERVICE) as LocationManager)
.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
500,
500f
) {
val result = executeRequest(
it.latitude,
it.longitude
)
Handler().post {
if (result) {
textView.text = "Операция произведена успешно"
} else {
throw Exception("Операция произведена с ошибкой, повторите позже")
}
}
}
}
}
}
}
}
Ответ
View и Context нельзя получать в конструкторе Fragment; работу нужно привязать к lifecycle, геолокацию — к разрешениям, а UI обновлять только на Main. Порог проверяют после увеличения счётчика.
Исправленная структура обработчика после получения View в onViewCreated:
var clicks = 0
var started = false
button.setOnClickListener {
if (started) return@setOnClickListener
clicks++
if (clicks < targetCount) return@setOnClickListener
started = true
viewLifecycleOwner.lifecycleScope.launch {
try {
val location = awaitAuthorizedLocation()
val ok = withContext(Dispatchers.IO) {
executeRequest(location.latitude, location.longitude)
}
textView.text = if (ok) "Готово" else "Не удалось выполнить операцию"
} catch (cancelled: CancellationException) {
throw cancelled
} catch (error: Exception) {
textView.text = "Повторите попытку позже"
} finally {
started = false
}
}
}
Это фрагмент исправления, не самостоятельный класс: awaitAuthorizedLocation должна проверять runtime-разрешение, иметь timeout и снимать listener при отмене. targetCount передают через arguments/SavedStateHandle, inflate вызывают с attachToRoot=false, ссылки binding очищают в onDestroyView. Если операция должна переживать пересоздание View, счётчик и выполнение переносят во ViewModel. Thread без Looper и Handler() без явного Main из исходника здесь не нужны.