Не могу вернуть ArrayList из потока после парсинга
У меня есть отдельный класс, метод в котором генерирует мне ArrayList. В нем обычный поток, а в потоке в try/catch парсится и наполняется инфой ArrayList. Дальше этот ArrayList нужно как-то вернуть из try/catch(с этим разобрался в принципе) и вернуть из потока(а вот с этим нет). Говорят юзать корутины, но там для меня та же проблема. Пока единственным решением видится... async task
val url: String = "https://ranobelib.ru/ranobe/"
fun listMain(): ArrayList<String> {
Thread {
var listMain = ArrayList<String>()
try {
val document: Document = Jsoup.connect(url)
.userAgent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
)
.timeout(100000).get()
val items: Elements = document.getElementsByClass("item")
for (item in items) {
listMain.add(
BookData(
item.select("a").text(),
item.select("a").text(),
item.select("img").first().attr("src"),
item.select("a").first().attr("href")
).toString()
)
}
} catch (e: IOException) {
e.printStackTrace()
}
}.start()
return listMain
}
BookData:
data class BookData(
var title: String,
var info: String,
var image_url: String,
var book_url: String
)
Ответы (2 шт):
Запрос в сеть должен происходить не на главном потоке, асинхронно. Засим нельзя написать код такого рода:
fun getDataFromNetwork() : Something {
/* Тут асинхронный код, который неизвестно сколько будет выполнятся
и выполнится ли вообще успешно.
Вдруг сети нет или она пропадёт в процессе или сервер сломается */
}
Соответственно, вы не сможете этот метод использовать из главного потока так:
someView.showDataOnScreen(getDataFromNetwork())
Поступать надо иначе: только в тот момент, когда будет успешно завершён сетевой запрос - вызвать код, который полученные данные выведет на экран. Выглядеть это может примерно так:
fun getDataFromNetwork() {
/* Тут асинхронный код, который неизвестно сколько будет выполнятся
и выполнится ли вообще успешно.
Вдруг сети нет или она пропадёт в процессе или сервер сломается */
showDataOnScreen(variableWithDataFromNetwork)
}
fun showDataOnScreen(variableWithDataFromNetwork: Something) {
someView.showDataOnScreen(variableWithDataFromNetwork)
}
Важно запускать showDataOnScreen() на главном потоке. Сделать это можно по разному. Если этот метод в активити - то всё его содержимое вызывайте через runOnUiThread() метод активити.
Решил вот так.
MainActivity.OnCreate():
GlobalScope.launch(){
var listMain = listMain()
withContext(Dispatchers.Main) {
val MainAdapter: RecyclerView.Adapter<*> = MainAdapter(listMain)
books_recycler.adapter = MainAdapter
}
}
Parser:
companion object {
@JvmStatic
fun listMain(): ArrayList<String> {
var listMain = ArrayList<String>()
try {
val document: Document = Jsoup.connect("https://ranobelib.ru/ranobe/")
.userAgent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
)
.timeout(100000).get()
val items: Elements = document.getElementsByClass("item")
for (item in items) {
listMain.add(
BookData(
item.select("a").text(),
item.select("a").text(),
item.select("img").first().attr("src"),
item.select("a").first().attr("href")
).toString()
)
}
} catch (e: IOException) {
e.printStackTrace()
}
return listMain
}
}