简体   繁体   中英

How to use AsyncLayoutInflater with Kotlin coroutines without blocking UI

I've written this extension function which suits my needs:

suspend fun AsyncLayoutInflater.inflateSuspended(@LayoutRes resid: Int, parent: ViewGroup?): View {
    return suspendCoroutine { continuation ->
        inflate(resid, parent) { view, _, parent ->
            continuation.resume(view)
        }
    }
}

But I'm not sure how I should use it without blocking UI. I tried Dispatchers.IO but I got RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

How should I use this function?

Update: I discovered that inflate doesn't freeze the UI but addView does. calendarHolder is inside a ScrollView . Scrolling freezes right before calendarView is shown in the screen.

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}")

Even if log shows this:

outside: 2 inflate: 2105 addView: 5

Like I mentioned in my comment, AsyncLayoutInflater is by definition asynchronous, and is mandatory to create the instance in the main thread, that's why you get an error if you change the Dispatcher . Nevertheless, it is possible to convert the callback-style to a coroutine style.

Example: updated to show the composition of coroutines

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 }
    }
}

More information

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM