我已经编写了适合我需要的扩展功能:
suspend fun AsyncLayoutInflater.inflateSuspended(@LayoutRes resid: Int, parent: ViewGroup?): View {
return suspendCoroutine { continuation ->
inflate(resid, parent) { view, _, parent ->
continuation.resume(view)
}
}
}
但是我不确定如何在不阻止UI的情况下使用它。我尝试了Dispatchers.IO,但出现了 RuntimeException:无法在未调用Looper.prepare()
的线程内创建处理程序我应该如何使用此功能?
更新:我发现inflate
不会冻结UI,而addView会冻结。 calendarHolder
在ScrollView
内部。在屏幕上显示calendarView之前,滚动定格。
val inflater = AsyncLayoutInflater(this)
val startTime = System.currentTimeMillis()
coroutineScope.launch {
val startTimeInside = System.currentTimeMillis()
repeat(100) {
calendarView = inflater.inflateSuspended(R.layout.layout_calendar, calendarHolder)
as MaterialCalendarView
}
val addViewStart = System.currentTimeMillis()
calendarHolder.addView(calendarView)
val endTimeInside = System.currentTimeMillis()
Timber.i("inflate: ${endTimeInside - startTimeInside}")
Timber.i("addView: ${endTimeInside - addViewStart}")
setupCalendar()
}
val endTime = System.currentTimeMillis()
Timber.i("outside: ${endTime - startTime}")
即使日志显示以下内容:
外部:2 充气:2105 addView:5
答案 0 :(得分:1)
就像我在评论中提到的那样,AsyncLayoutInflater
根据定义是异步的,并且是在主线程中创建实例所必需的,这就是为什么更改Dispatcher
时会出错的原因。不过,可以将回调样式转换为协程样式。
示例:已更新以显示协程的组成
class MainActivity : AppCompatActivity(), CoroutineScope {
private val activityJob = Job()
private lateinit var requestQueue: RequestQueue
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + activityJob
suspend fun AsyncLayoutInflater.inflate(@LayoutRes resid: Int, parent: ViewGroup?): View =
suspendCoroutine { continuation -> inflate(resid, parent) { view, _, _ -> continuation.resume(view) } }
suspend fun getTodo(id: Int): String = suspendCoroutine { continuation ->
val request = StringRequest(Request.Method.GET, "https://jsonplaceholder.typicode.com/todos/$id",
Response.Listener { continuation.resume(it) },
Response.ErrorListener { continuation.resumeWithException(it) }
)
requestQueue.add(request)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestQueue = Volley.newRequestQueue(this)
val parent = findViewById<ViewGroup>(R.id.frameLayout)
val asyncLayoutInflater = AsyncLayoutInflater(this)
launch {
val view = asyncLayoutInflater.inflate(R.layout.async_layout, parent) as TextView
parent.addView(view)
delay(1000)
val todo = getTodo(1)
view.text = todo
}
}
override fun onDestroy() {
super.onDestroy()
activityJob.cancel()
requestQueue.cancelAll { true }
}
}