android - 在 Kotlin 中使用协程 IO 线程时使用的主线程
问题描述
我正在尝试使用 Volley 和 HTTPURLConnection 从服务器获取数据。使用协程 IO 线程来防止阻塞主线程,但是在执行工作时屏幕停止(这意味着主线程被阻塞)。
奇怪的是,它在使用 Jsoup 和 HTTPURLConnection 做其他工作时效果很好。我能找到的唯一区别是它不使用 lambda。
可能是什么问题呢?提前致谢。
*Edit1:我使用了suspend和withContext,但我发现删除它们并不能解决问题。
ConDetailActivity.kt 代码
val scope = CoroutineScope(Dispatchers.IO)
private fun initialize(context: Context,idx:String) = scope.launch{
try{
ConCrawler.getConData(context,idx) {data ->
CoroutineScope(Dispatchers.Main).launch { adapter.addData(data.imgDataList) }
progressbar.visibility = View.INVISIBLE
}
}catch(e:Exception){
e.printStackTrace()
}
}
对象 ConCrawler 的代码
suspend fun getConData(context: Context, idx: String, returnConData: (condata: ConDataforReturn) -> Unit) = withContext(Dispatchers.Default) {
val params = HashMap<String, String>()
val cd = arrayListOf<ConImgData?>()
var title = ""
params.put("package_idx", idx)
VolleyRequest(context, params, "https://dccon.dcinside.com/index/package_detail") { response ->
val answer = JSONObject(response)
var json = answer.getJSONArray("detail")
title = answer.getJSONObject("info").getString("title")
for (i in 0..(json.length() - 1)) {
val v = json.getJSONObject(i)
cd.add(ConImgData(v.getString("title"), v.getString("ext"), runBlocking { getBitmapFromURL("https://dcimg5.dcinside.com/dccon.php?no=" + v.getString("path")) }, v.getString("path")))
}
returnConData(ConDataforReturn(title, cd))
}
}
fun VolleyRequest(context: Context, params: HashMap<String, String>, link: String, success: (response: String) -> Unit) {
val queue = Volley.newRequestQueue(context)
val stringRequest = object : StringRequest(Method.POST, link,
Response.Listener<String> { response ->
Log.e("response", response.toString())
success(response)
},
Response.ErrorListener { error ->
error.printStackTrace()
}) {
override fun getHeaders(): MutableMap<String, String> {
val headers = mutableMapOf<String, String>()
/*unused headers
headers.put("user-agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36")
headers.put("origin","https://dccon.dcinside.com")
headers.put("referer","https://dccon.dcinside.com")
headers.put("content-type","multipart/form-data")
*/
headers.put("x-requested-with", "XMLHttpRequest")
return headers
}
override fun getParams(): MutableMap<String, String> {
return params
}
}
stringRequest.setShouldCache(false)
queue.add(stringRequest)
}
suspend fun getBitmapFromURL(src: String): Bitmap? = withContext(Dispatchers.Default) {
try {
val url = URL(src)
val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
connection.setRequestProperty("Referer", "https://dccon.dcinside.com")
connection.setRequestProperty("Sec-Fetch-Mode", "no-cors")
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36")
connection.setDoInput(true)
connection.connect()
val input: InputStream = connection.getInputStream()
BitmapFactory.decodeStream(input)
} catch (e: Exception) { // Log exception
e.printStackTrace()
null
}
}
解决方案
找到了解决方案。关键是两个案例之间的区别。
问题是 lambda。使用 lambda 时,会创建新块,并且该块与外部块分开。这意味着 lambda 块在主线程中再次运行。
因此,在 lambda 块内再次创建协程可以解决问题。希望这对其他新手有所帮助。
fun getConData(context: Context, idx: String, returnConData: (condata: ConDataforReturn) -> Unit) {
val params = HashMap<String, String>()
val cd = arrayListOf<ConImgData?>()
var title = ""
params.put("package_idx", idx)
VolleyRequest(context, params, "https://dccon.dcinside.com/index/package_detail") { response ->
CoroutineScope(Dispatchers.IO).launch {
val answer = JSONObject(response)
var json = answer.getJSONArray("detail")
title = answer.getJSONObject("info").getString("title")
for (i in 0..(json.length() - 1)) {
val v = json.getJSONObject(i)
var a = getBitmapFromURL("https://dcimg5.dcinside.com/dccon.php?no=" + v.getString("path"))
cd.add(ConImgData(v.getString("title"), v.getString("ext"), a, v.getString("path")))
}
returnConData(ConDataforReturn(title, cd))
}
}
}
推荐阅读
- android - 如何在 Jetpack Compose 中组合多个 Modifier 对象?
- vue.js - FilePond - 暂停上传直到方法执行完成
- protractor - 量角器:失败:过时的元素引用:元素未附加到地图中点的页面文档
- c++ - 如何通过文本框在两个值之间留一个空格来放置第二个值?
- reactjs - 如何在 axios get 方法中设置其他组件的状态?
- mongodb - 将整个数据集导入到一个 MongoDB 文档中
- iot - 调制解调器 AT 命令,无法进入数据模式 (PPP)
- sql-server - 具有用户分配的托管标识的 Azure SQL 数据库连接池
- ios - 获取设备界面方向 Swift
- batch-file - 如何通过 x.bat 文件打开名称中有空格的 ahk 脚本