单一导致主线程上的网络或错误线程异常的查看根
Single causes either Network on Main Thread or View Root from Wrong Thread exceptions
使用简单的 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 在控制台中打印,但是一旦我尝试将它加载到视图的适配器中,它就会崩溃并出现 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();
}
subscribeOn
告诉 Observable 在哪里执行工作,然后 observeOn
是这项工作的结果将返回到的地方。在您的情况下,您需要:
calendarParser.getSingleBearCal()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).
......
我认为有一个 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'
使用简单的 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 在控制台中打印,但是一旦我尝试将它加载到视图的适配器中,它就会崩溃并出现 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();
}
subscribeOn
告诉 Observable 在哪里执行工作,然后 observeOn
是这项工作的结果将返回到的地方。在您的情况下,您需要:
calendarParser.getSingleBearCal()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).
......
我认为有一个 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'