kotlin 单一导致主线程上的网络或来自错误线程的视图根异常

qnakjoqk  于 2023-05-18  发布在  Kotlin
关注(0)|答案(2)|浏览(154)

使用一个简单的RxKotlin Single,我会收到一个android.view.ViewRootImpl$CalledFromWrongThreadException异常,或者通过添加.observeOn(AndroidSchedulers.mainThread()),我会收到一个NetworkOnMainThread异常。

fun loadStaffCalendar() {
        var calendarParser = CalendarParser()
        calendarParser.getSingleBearCal()
            .subscribeOn(Schedulers.io())
            .subscribeBy(
                onError ={error("Error loading calendar\n${it.message}")},
                onSuccess = { responseBody ->
                        println("ResponseBody retrieved")
                        var staffList = calendarParser.parseStringIntoSchedule(responseBody.string())
                        view.loadToAdapter(staffList)
                         println(staffList)

                }

            )

我可以让staffList在控制台中打印,但是当我试图将它加载到View的适配器中时,它崩溃并出现CalledFromWrongThread异常。
这是当我添加.observeOn(AndroidSchedulers.mainThread()):时的崩溃

io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | android.os.NetworkOnMainThreadException
        at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
        at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:126)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: android.os.NetworkOnMainThreadException
        at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1513)
        at com.android.org.conscrypt.Platform.blockGuardOnNetwork(Platform.java:415)
        at com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read(ConscryptFileDescriptorSocket.java:527)
        at okio.InputStreamSource.read(Okio.kt:102)

在任何地方都不会进行额外的网络呼叫。以下是剩下的:

class CalendarParser : AnkoLogger {
    fun getSingleBearCal(): Single<ResponseBody> {
        val retrofit: Retrofit = Retrofit.Builder()
            .baseUrl("https://www.brownbearsw.com/")
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
        val bearApi: BearApi = retrofit.create(BearApi::class.java)

        return bearApi.file
    }

    fun parseStringIntoSchedule(wholeSchedule: String): ArrayList<StaffModel> {
        var dateMap: HashMap<LocalDate, String> = HashMap()
        var endDelim = "END:VEVENT"
        var events: List<String> = wholeSchedule.split(endDelim)
        var parsedStaffCal: ArrayList<StaffModel> = ArrayList()
        var today = LocalDate.now()
        // :: Pull event date from event data, pull staff list from "SUMMARY" line :: //
        events.forEach {
            var tempString = (it.substringAfterLast("DATE:", "FAIL").take(8))
            var dateTime: LocalDate = eightIntoDateTime(tempString)

            var summary: String = it.substringAfter("SUMMARY:", "FAIL")
                .let { it.substringBefore("UID").replace("\\n", "\n") }
            dateMap.put(dateTime, summary)
        }

        // ::Filter out all days before today:: //
        dateMap.forEach {
            if (!it.key.isBefore(today)) {
                val staffModel = StaffModel(it.key, it.value)
                parsedStaffCal.add(staffModel)
            }
        }
        //:: Sort chronologically :://
        parsedStaffCal.sortBy { it.localDate }

        return parsedStaffCal
    }

    fun eightIntoDateTime(s: String): LocalDate {
        return if (s.length == 8 && s.isDigitsOnly()) { // <-=-=-=-=-=- avoid potential formatting exceptions
            val dateString = ("${s.subSequence(0, 4)}-${s.subSequence(4, 6)}-${s.subSequence(6, 8)}")
            LocalDate.parse(dateString)
        } else LocalDate.parse("1999-12-31")
    }

改装API:

package com.offbroadwaystl.archdemo.schedule;

import io.reactivex.Single;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.Streaming;

public interface BearApi {
    @Streaming
    @GET("url.goes.here.ics")
    Single<ResponseBody> getFile();
}
wfveoks0

wfveoks01#

subscribeOn告诉可观察对象在哪里执行工作,那么observeOn就是这个工作的结果将返回到的地方。在您的情况下,您需要:

calendarParser.getSingleBearCal()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()). 
            ......
c3frrgcw

c3frrgcw2#

我认为RxJava Gradle依赖与RxKotlin依赖冲突。删除它解决了问题。我还从onSuccess中提取了一些工作,并添加了一个操作符,这可能是更好的实践:

fun loadStaffCalendar() {
        var calendarParser = CalendarParser()
        calendarParser.getSingleBearCal()
            .subscribeOn(Schedulers.io())
            .map { calendarParser.parseStringIntoSchedule(it.string())  }
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeBy(
                onError = {error(it.localizedMessage.toString())},
                onSuccess = {view.loadToAdapter(it)})
    }

Gradle看起来像:

implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'

相关问题