Как использовать AsyncLayoutInflater с сопрограммами Kotlin без блокировки пользовательского интерфейса - PullRequest
0 голосов
/ 20 декабря 2018

Я написал эту функцию расширения, которая удовлетворяет моим потребностям:

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

Но я не уверен, как использовать ее без блокировки пользовательского интерфейса.Я попробовал Dispatchers.IO, но получил RuntimeException: не могу создать обработчик внутри потока, который не вызвал Looper.prepare ()

Как мне использовать эту функцию?

Обновление: Я обнаружил, что inflate не останавливает интерфейс, но добавляет ViewView.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

1 Ответ

0 голосов
/ 20 декабря 2018

Как я уже упоминал в своем комментарии, 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 }
    }
}

Подробнееинформация

...