android - 如何在后台线程中打开 URL,以便我可以解析 XML 并获取值
问题描述
我想用 XmlPullParser 从 URL 解析 XML,然后在 UI 上显示结果(目前正在学习使用 Compose)。
我掌握了 XmlPullParser 的基础知识,但我不明白如何在另一个线程中访问 URL 并取回值。我正在努力理解协程并失败了。
主要活动
import ....
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp {
MyScreenContent(test())
}
}
}
//I want this to run async and return the parsed results
//................................................................................
fun test():List<String> {
val names = mutableListOf<String>()
val user = "User"
val url = URL("https://www.boardgamegeek.com/xmlapi/collection/$user?own=1")
val http: HttpURLConnection = url.openConnection() as HttpURLConnection
http.doInput = true
http.connect()
Log.d("bgg", "first" )
var games: List<Game>? = null
try {
val parser = XmlPullParserHandler()
val istream = http.inputStream
games = parser.parse(istream)
} catch (e: IOException) {
e.printStackTrace()
}
games?.forEach { it ->
it.name?.let { it1 -> names.add(it1) }
Log.d("bgg", "game" + it.name)
}
Log.d("bgg", "second" + names.toString())
return names
}
@Composable
fun MyApp(content: @Composable () -> Unit) {
SecontComposeTutorialTheme {
Surface(color = Color.Yellow) {
content()
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = name, modifier = Modifier.padding(all = 24.dp))
}
@Composable
fun NameList(names: List<String>, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
items(items = names) { name ->
Greeting(name = name)
Divider(color = Color.Black)
}
}
}
@Composable
fun MyScreenContent(names: List<String>) {
val counterState = remember { mutableStateOf(0) }
Column(modifier = Modifier.fillMaxHeight()) {
NameList(names = names, Modifier.weight(1f))
Counter(
count = counterState.value,
updateCount = { newCount ->
counterState.value = newCount
}
)
}
}
@Composable
fun Counter(count: Int, updateCount: (Int) -> Unit) {
Button(
onClick = {
updateCount(count + 1)
getBggGame()
},
colors = ButtonDefaults.buttonColors(
backgroundColor = if (count > 5) Color.Green else Color.White
)
) {
Text(text = "I've beek clicked on $count times")
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
MyApp {
// MyScreenContent()
}
}
}
XmlPullParserHandler(工作)
import android.util.Log
import org.xmlpull.v1.XmlPullParserException
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserFactory
import java.io.IOException
import java.io.InputStream
import java.net.HttpURLConnection
class XmlPullParserHandler {
private val collection = ArrayList<Game>()
private var game: Game? = null
private var text: String? = null
fun parse(inputStream: InputStream): List<Game> {
try {
val factory = XmlPullParserFactory.newInstance()
factory.isNamespaceAware = true
val parser = factory.newPullParser()
parser.setInput(inputStream, null)
var eventType = parser.eventType
while (eventType != XmlPullParser.END_DOCUMENT) {
val tagname = parser.name
when (eventType) {
XmlPullParser.START_TAG -> if (tagname.equals("item", ignoreCase = true)) {
// create a new instance of game
game = Game()
}
XmlPullParser.TEXT -> text = parser.text
XmlPullParser.END_TAG -> if (tagname.equals("item", ignoreCase = true)) {
// add game object to list
game?.let { collection.add(it) }
} else if (tagname.equals("id", ignoreCase = true)) {
game!!.id = Integer.parseInt(text)
} else if (tagname.equals("name", ignoreCase = true)) {
game!!.name = text
}
else -> {
}
}
eventType = parser.next()
}
} catch (e: XmlPullParserException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
return collection
}
}
更新.............................................
如果我使用以下内容,我会得到“android.os.NetworkOnMainThreadException”
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
setContent {
MyApp {
MyScreenContent(test())
}
}
}
}
fun test():List<String> {
val names = mutableListOf<String>()
val user = "Uset"
val url = URL("https://www.boardgamegeek.com/xmlapi/collection/$user?own=1")
val http: HttpURLConnection = url.openConnection() as HttpURLConnection
http.doInput = true
http.connect()
Log.d("bgg", "first" )
var games: List<Game>? = null
try {
val parser = XmlPullParserHandler()
val istream = http.inputStream
games = parser.parse(istream)
} catch (e: IOException) {
e.printStackTrace()
}
games?.forEach { it ->
it.name?.let { it1 -> names.add(it1) }
Log.d("bgg", "game" + it.name)
}
Log.d("bgg", "second" + names.toString())
return names
}
....
如果我用它标记测试函数suspend
告诉我它的冗余,并且我不能从非挂起或协程函数调用它
解决方案
您必须更改为挂起并在协程范围内调用它
suspend fun test():List<String> {...
setContent {
MyApp {
lifeCycleScope.launch {
val list = test()
MyScreenContent(list)
}
}
}
您正在使用 Jetpack compose,所以我不确定您是否可以使用它lifeCycleScope
,但请尝试移动它:
lifeCycleScope.launch {
setContent {...
}
否则问题可能是设计错误。
推荐阅读
- python - JSON 错误:json.decoder.JSONDecodeError:预期值:第 1 行第 1 列(字符 0)
- javascript - 如何解析多个数组的数组
- python - AttributeError:未知的属性插值
- javascript - 如何使用 Javascript 关注范围滑块?
- javascript - onClick 仅在-element上触发
- java - 如何在使用 javamail 发送电子邮件时安全地使用凭据
- c# - 文件未从 File 或 PhysicalFile IActionResult 下载
- doorkeeper - 将范围限制为某些用户
- python - 在成本函数中包含与预期模型输出的偏差
- c# - 有没有办法阻止我的页面在我导航到它们时只闪烁进出?