4

使用简单的 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()):

Process: com.offbroadwaystl.archdemo, PID: 21809
    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();
}
4

2 回答 2

2

subscribeOn告诉可观察者在哪里执行工作,然后observeOn是这项工作的结果将返回到哪里。在您的情况下,您需要:

calendarParser.getSingleBearCal()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()). 
            ......
于 2019-08-04T07:44:07.027 回答
0

我认为有一个 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)})
    }

摇篮看起来像:

implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'
于 2019-08-04T17:53:04.743 回答