У меня очень простой вопрос. Я следую руководству по разработке кодлабы в предложенном порядке и терплю неудачу в разделе 6 «Введение в отладку»: «Запись работающего приложения». Я нахожу объяснение очень запутанным, потому что оно довольно минимальное для новичка.
Я с радостью удалю и начну снова, если что-то не работает должным образом, но мне также нужно быть уверенным, что по мере моего продвижения отсутствие желаемого результата не связано с моей средой.
В этом уроке нам нужно изменить существующий код, чтобы обновить приложение Текстовый вид с помощью инструкции Лог.д, чтобы мы увидели обновленный ID Division_textview на экране телефона.
Я пробовал множество способов добавить окончательный оператор
findViewById<TextView>(R.id.division_textview).setText("${numerator / denominator}")
но не могу добавить его куда угодно, чтобы значения обновлялись в моем эмуляторе. Я не получаю ошибок, но поскольку в этом уроке нет «кода решения», а все предыдущие изображения урока служат довольно запутанной картине того, как именно должен выглядеть окончательный код, может ли кто-нибудь помочь мне с этим?
Мне действительно нужно убедиться, что мой эмулятор работает правильно (об ошибках не сообщается) и что отсутствие результатов — это мой код, а не моя установка.
Это урок, о котором идет речь (спасибо за совет!)
Код из моего MainActivity.kt в дополнение к полезному предложению ниже. Обратите внимание, что я понимаю, что это не «оптимальный» код, я просто пытаюсь следовать инструкциям на данный момент.
package com.example.debugging
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.TextView
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val helloTextView: TextView = findViewById(R.id.division_textview)
helloTextView.text = "Hello, debugging!"
logging()
division()
}
fun division() {
val numerator = 60
var denominator = 4
repeat(4) {
Thread.sleep(3)
findViewById<TextView>(R.id.division_textview).setText("${numerator / denominator}")
Log.v(TAG, "${numerator / denominator}")
denominator--
}
}
fun logging() {
Log.e(TAG, "ERROR: a serious error like an app crash")
Log.w(TAG, "WARN: warns about the potential for serious errors")
Log.i(TAG, "INFO: reporting technical information, such as an operation succeeding")
Log.d(TAG, "DEBUG: reporting technical information useful for debugging")
Log.v(TAG, "VERBOSE: more verbose than DEBUG logs")
}
}
если честно, я думаю, что даже просьба о ссылке может быть не такой уж полезной, эти кодовые лаборатории могут исчезнуть в будущем, есть ли шанс, что вы можете опубликовать весь соответствующий код здесь, на SO? Я предполагаю, что это действие, не могли бы вы опубликовать это в своем вопросе?
Я полностью согласен с @Tenfour04 выше и действительно заметил, как внутри самой Android Studio предположил, что мое значение Int для Thread.sleep будет в миллисекундах. Итак, я попытался манипулировать этим значением, например, 3000, но ничего не изменилось. Я также задавался вопросом, является ли моя «проблема» просто тем, что из-за времени, необходимого для фактической перекомпиляции представления эмулятора, процедура завершилась. Я не хочу критиковать Google или того, кто написал руководства, но, честно говоря, я нашел несколько случаев противоречивых или отсутствующих инструкций, и я только что закончил введение!
Я думаю, что это очень небрежно написанный и неправильный учебник. (Да, даже если это от Google.) Если вы спите в основном потоке, вы не увидите изменения содержимого текстового представления до тех пор, пока не будет выполнен весь цикл repeat
, поэтому вы увидите только окончательное значение после того, как denominator
уже уменьшен до 0. Кроме того, в учебнике утверждается, что вы увидите, что он будет меняться каждые три секунды, хотя код с Thread.sleep(3)
будет приостанавливаться только на 3 Миллиseconds. А затем в своем реальном примере они использовали Thread.sleep(1)
, что противоречит их инструкциям.
Вместо этого попробуйте этот код:
// added to imports:
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*
// modified division function
fun division() = lifecycleScope.launch {
val numerator = 60
var denominator = 4
repeat(4) {
delay(3000)
findViewById<TextView>(R.id.division_textview).setText("${numerator / denominator}")
Log.v(TAG, "${numerator / denominator}")
denominator--
}
}
Использование = lifecycleScope.launch {
превращает вашу функцию в сопрограмму, которая позволяет вам использовать функцию delay()
вместо Thread.sleep()
, поэтому основной поток получает возможность фактически обновить текстовое представление вместо того, чтобы ждать завершения всего цикла repeat
.
Вот альтернативный способ сделать это без сопрограмм, но он не такой красивый:
fun division() = with(Handler(Looper.getMainLooper())) {
val numerator = 60
var denominator = 4
repeat(4) { i ->
postDelayed({
findViewById<TextView>(R.id.division_textview).setText("${numerator / denominator}")
Log.v(TAG, "${numerator / denominator}")
denominator--
}, i * 3000L)
}
}
Спасибо тебе за это! Однако я отмечаю, что при добавлении ваших правок, и хотя я понимаю смысл того, чего они должны достичь, мой экземпляр Android Studio жалуется на оба импорта, заявляя, что они являются «неразрешенной ссылкой». Так что, по-видимому, что-то еще не так с моей общей конфигурацией? Я использую рекомендуемые настройки, например. API 31 и Pixel 5 ... и, насколько я могу судить (очень новичок во всем этом), у меня установлен правильный SDK, только что проверил «синхронизацию» и т. д. Немного застрял в отношении следующих необходимых шагов.
В вашем проекте build.gradle добавьте это внутри плагинов: id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
. В разделе dependencies
вашего модуля build.gradle найдите строку, в которой упоминается android.core, и замените ее на implementation 'androidx.core:core-ktx:1.7.0'
. Или просто добавьте эту строку, если еще нет записи android.core.
оба эти параметра конфигурации уже установлены! Ему особенно не нравятся два термина: «lifecycleScope» и «kotlinx».
Просто чтобы быть уверенным, там написано core-ktx
, а не просто core
?
да, он настроен на именно тот текст, который вы предложили.
может я это искал? developer.android.com/topic/libraries/architecture/coroutines
Ага, попробуй те. Я в недоумении, в чем может быть проблема. Если вы создаете новый проект с последней версией Android Studio, обычно сопрограммы работают «из коробки». На этом сайте есть более полный список необходимых вам зависимостей, но я думал, что appcompat в сочетании с core-ktx уже включает их как транзитивные зависимости (те, которые загружаются автоматически при загрузке, потому что они зависят от них). Как и в проекте, который я смотрю, у меня не указано lifecycle-runtime-ktx
, но оно загружено. Хотя, возможно, я что-то упустил.
Вы можете проверить, какие зависимости загружены (включая все транзитивные), переключив раскрывающийся список Android в верхнем левом углу на Project, а затем развернув Внешние зависимости. Кстати, я только что добавил другой способ сделать это без сопрограмм на случай, если вы просто захотите попробовать.
Спасибо за ваше более позднее решение, оно работает точно так, как ожидалось/предназначено. Я подозреваю, что некоторые из моих проблем с импортом связаны с упаковкой. Я предпочитаю, чтобы мой менеджер пакетов устанавливал вещи, поэтому я избегал ручной установки Android Studio из пакета .zip. Я мог пропустить другие включенные deps. Ничего, теперь все работает и ладно. Самое главное, я понимаю, что это был не совсем «я», а код :D
Может помочь нам связать инструкции, которым вы следуете...