如何从 "GlobalScope.launch" 块中获取作为 return 值的字符串
How to get String as a return value from "GlobalScope.launch" block
在这个应用程序中,我使用 HttpURLConnection 下载 Feed RSS“From XML link”,然后解析它并查看列表视图,但是在我 运行 应用程序之后,我得到了空列表视图
代码
private fun downloadXML(urlPath: String? ): String {
val xmlResult = StringBuilder()
GlobalScope.launch(Dispatchers.IO) {
kotlin.runCatching {
try {
Log.d(TAG,"Dispatchers.IO Called")
val url = URL(urlPath)
val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
val responseCode = connection.responseCode
Log.d(TAG, "DownloadXML: the response code is $responseCode")
connection.inputStream.buffered().reader().use { xmlResult.append(it.readText()) }
Log.e(TAG,"xmlResult: is ${xmlResult}")
}catch (ex:Exception){
when(ex){
is MalformedURLException -> ex.message.toString()
is IOException -> ex.message.toString()
is SecurityException -> ex.message.toString()
else -> ex.message.toString()
}
}
}
}
Log.e(TAG,xmlResult.toString()) //====> THIS CODEDOES NOT EXECUTED
return xmlResult.toString() // AND THIS
}
并像这样使用它
private var feedURL:String = "http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/topfreeapplications/limit=10/xml"
private var feedLimit = 10
private var feedCashedURL = "INVALIDAED"
private val STATE_URL = "feedURL"
private val STATE_LIMIT = "feedLimit"
private val parseApplication = ParseApplication()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
downloadURL(feedURL.format(feedLimit))
val feedAdapter = FeedAdapter(this,R.layout.list_record,parseApplication.application)
binding.listView.adapter = feedAdapter
Log.d(TAG,"onCreate called")
}
private fun downloadURL(feedURL: String) {
if(feedURL != feedCashedURL){
Log.d(TAG, "download URL starting AsyncTask")
val rssFeed = downloadXML(feedURL)
parseApplication.parse(rssFeed)
feedCashedURL = feedURL
Log.d(TAG, "downloadURL done")
}else{
Log.d(TAG, "downloadURL URL not changed")
}
}
看起来这两行没有执行,因为即使我收到响应代码 200 并且打印了 xmlResult 但在 GlobalScope.launch
块,那么如何从协程块中得到这个 return 结果?
Log.e(TAG,xmlResult.toString())
return xmlResult.toString()
这两行正在执行,但不是你想的顺序。 GlobalScope.launch()
开始异步操作,它不会等待它完成,所以 return xmlResult.toString()
可能在 xmlResult.append()
.
之前执行
我假设您试图避免阻塞 main/UI 线程。但是注意你的downloadURL()
还是设计成需要等待资源下载完才能成功return。并且由于此方法是从 UI 线程调用的,因此您无法避免阻塞此线程。
要解决这个问题,需要将整个资源加载过程移到异步操作中。这意味着:downloadXML()
、downloadURL()
甚至为适配器提供数据。只需删除您当前的 GlobalScope.launch()
,然后在 onCreate()
中按照以下行做一些事情:
GlobalScope.launch(Dispatchers.IO) {
val rssFeed = async { downloadXML(feedURL) }
parseApplication.parse(rssFeed.await())
withContext(Dispatchers.Main){
val feedAdapter = FeedAdapter(this@MainActivity, R.layout.list_record, parseApplication.application)
binding.listView.adapter = feedAdapter
}
}
我想这可能不是一个完整的示例,因为您可能需要在馈送适配器时跳回到 UI 线程。另外,我认为不推荐使用 GlobalScope
,但我对 Android 不是很了解,所以我不会提出更好的解决方案。
在这个应用程序中,我使用 HttpURLConnection 下载 Feed RSS“From XML link”,然后解析它并查看列表视图,但是在我 运行 应用程序之后,我得到了空列表视图
代码
private fun downloadXML(urlPath: String? ): String {
val xmlResult = StringBuilder()
GlobalScope.launch(Dispatchers.IO) {
kotlin.runCatching {
try {
Log.d(TAG,"Dispatchers.IO Called")
val url = URL(urlPath)
val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
val responseCode = connection.responseCode
Log.d(TAG, "DownloadXML: the response code is $responseCode")
connection.inputStream.buffered().reader().use { xmlResult.append(it.readText()) }
Log.e(TAG,"xmlResult: is ${xmlResult}")
}catch (ex:Exception){
when(ex){
is MalformedURLException -> ex.message.toString()
is IOException -> ex.message.toString()
is SecurityException -> ex.message.toString()
else -> ex.message.toString()
}
}
}
}
Log.e(TAG,xmlResult.toString()) //====> THIS CODEDOES NOT EXECUTED
return xmlResult.toString() // AND THIS
}
并像这样使用它
private var feedURL:String = "http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/topfreeapplications/limit=10/xml"
private var feedLimit = 10
private var feedCashedURL = "INVALIDAED"
private val STATE_URL = "feedURL"
private val STATE_LIMIT = "feedLimit"
private val parseApplication = ParseApplication()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
downloadURL(feedURL.format(feedLimit))
val feedAdapter = FeedAdapter(this,R.layout.list_record,parseApplication.application)
binding.listView.adapter = feedAdapter
Log.d(TAG,"onCreate called")
}
private fun downloadURL(feedURL: String) {
if(feedURL != feedCashedURL){
Log.d(TAG, "download URL starting AsyncTask")
val rssFeed = downloadXML(feedURL)
parseApplication.parse(rssFeed)
feedCashedURL = feedURL
Log.d(TAG, "downloadURL done")
}else{
Log.d(TAG, "downloadURL URL not changed")
}
}
看起来这两行没有执行,因为即使我收到响应代码 200 并且打印了 xmlResult 但在 GlobalScope.launch
块,那么如何从协程块中得到这个 return 结果?
Log.e(TAG,xmlResult.toString())
return xmlResult.toString()
这两行正在执行,但不是你想的顺序。 GlobalScope.launch()
开始异步操作,它不会等待它完成,所以 return xmlResult.toString()
可能在 xmlResult.append()
.
我假设您试图避免阻塞 main/UI 线程。但是注意你的downloadURL()
还是设计成需要等待资源下载完才能成功return。并且由于此方法是从 UI 线程调用的,因此您无法避免阻塞此线程。
要解决这个问题,需要将整个资源加载过程移到异步操作中。这意味着:downloadXML()
、downloadURL()
甚至为适配器提供数据。只需删除您当前的 GlobalScope.launch()
,然后在 onCreate()
中按照以下行做一些事情:
GlobalScope.launch(Dispatchers.IO) {
val rssFeed = async { downloadXML(feedURL) }
parseApplication.parse(rssFeed.await())
withContext(Dispatchers.Main){
val feedAdapter = FeedAdapter(this@MainActivity, R.layout.list_record, parseApplication.application)
binding.listView.adapter = feedAdapter
}
}
我想这可能不是一个完整的示例,因为您可能需要在馈送适配器时跳回到 UI 线程。另外,我认为不推荐使用 GlobalScope
,但我对 Android 不是很了解,所以我不会提出更好的解决方案。