我在下面创建了一个非常简化的版本。
严格模式通过以下策略设置:
Private Sub OK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ok.Click
Try
userss = user.Text
Query = "Select * FROM user WHERE users = '" & userss & "' AND password = '" & pass.Text & "'"
daData = New OleDbDataAdapter(Query, conn)
dsData = New DataSet
daData.Fill(dsData) '**LAST EXECUTE CODE**
If dsData.Tables(0).Rows.Count = 0 Then '**VB DONT EXECUTE**
If mCount < 3 Then
MsgBox("Wrong Username or Password", MsgBoxStyle.Critical + MsgBoxStyle.OkCancel, "Message")
user.Focus()
mCount = mCount + 1
Me.pass.Text = ""
Else
MsgBox("Login Failed", MsgBoxStyle.Critical + MsgBoxStyle.OkCancel, "Message")
End
End If
Else
If (dsData.Tables(0).Rows(0).Item(4) = 1) Then
dsData.Dispose()
dsData.Reset()
daData.Dispose()
Me.Hide()
Main.Show()
Else
MsgBox("Login Failed", MsgBoxStyle.Critical + MsgBoxStyle.OkCancel, "Message")
End If
End If
Catch ex As Exception
MsgBox("Database Connection Failed!", MsgBoxStyle.Critical + MsgBoxStyle.OkCancel, "Message") '**AND DIRECTLY EXECUTE HERE**
End Try
End Sub
视图模型只有一个函数,该函数在调用时会使应用程序崩溃。该函数不执行任何操作(它的主体为空)
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.penaltyDeath()
.build()
)
该活动将调用class MyViewModel : ViewModel() {
fun foo() {
viewModelScope.launch(Dispatchers.IO){ }
}
}
中的viewModel.foo()
,并通过以下跟踪信息使应用程序崩溃。
onCreate
根据堆栈跟踪,有磁盘读取冲突,但是该代码中的任何内容都不应访问磁盘。
感兴趣的行是:
--------- beginning of crash
2019-04-08 22:07:49.579 1471-1471/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 1471
java.lang.RuntimeException: StrictMode ThreadPolicy violation
at android.os.StrictMode$AndroidBlockGuardPolicy.onThreadPolicyViolation(StrictMode.java:1705)
at android.os.StrictMode$AndroidBlockGuardPolicy.lambda$handleViolationWithTimingAttempt$0(StrictMode.java:1619)
at android.os.-$$Lambda$StrictMode$AndroidBlockGuardPolicy$9nBulCQKaMajrWr41SB7f7YRT1I.run(Unknown Source:6)
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.strictmode.DiskReadViolation
at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1504)
at java.io.UnixFileSystem.getBooleanAttributes(UnixFileSystem.java:241)
at java.io.File.isDirectory(File.java:845)
at dalvik.system.DexPathList$Element.maybeInit(DexPathList.java:696)
at dalvik.system.DexPathList$Element.findResource(DexPathList.java:729)
at dalvik.system.DexPathList.findResources(DexPathList.java:526)
at dalvik.system.BaseDexClassLoader.findResources(BaseDexClassLoader.java:174)
at java.lang.ClassLoader.getResources(ClassLoader.java:839)
at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:349)
at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:402)
at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:488)
at kotlin.collections.CollectionsKt___CollectionsKt.toCollection(_Collections.kt:1145)
at kotlin.collections.CollectionsKt___CollectionsKt.toMutableList(_Collections.kt:1178)
at kotlin.collections.CollectionsKt___CollectionsKt.toList(_Collections.kt:1169)
at kotlinx.coroutines.internal.MainDispatcherLoader.loadMainDispatcher(MainDispatchers.kt:15)
at kotlinx.coroutines.internal.MainDispatcherLoader.<clinit>(MainDispatchers.kt:10)
at kotlinx.coroutines.Dispatchers.getMain(Dispatchers.kt:55)
at androidx.lifecycle.ViewModelKt.getViewModelScope(ViewModel.kt:41)
at com.example.myapplication.MyViewModel.foo(MainActivity.kt:35)
at com.example.myapplication.MainActivity.onCreate(MainActivity.kt:28)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
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)
第35行: at com.example.myapplication.MyViewModel.foo(MainActivity.kt:35)
at com.example.myapplication.MainActivity.onCreate(MainActivity.kt:28)
第28行:viewModelScope.launch(Dispatchers.IO){ }
如果我删除了viewModel.foo()
,该应用程序也不会崩溃。
所以我的问题是
如何使用上面的严格模式配置防止崩溃?
协程本身或严格模式本身是否有问题?
更新: 这似乎是协程的一个已知问题。仍未解决-查看对话here
答案 0 :(得分:1)
您的stacktrace使得您的代码正在访问磁盘是显而易见的,因为它是第一次运行并且正在触发某些类加载。这将转到DexClassLoader
并触摸磁盘。
尝试使用所有代码路径后启用严格模式。
答案 1 :(得分:1)
解决方案是使用您自己初始化的调度程序,而不在主线程上执行I / O。
实施起来有点棘手,因为要避免由于Handler
默认启用了vsync(可能会延迟多达16ms的代码,根本不需要vsync)而导致应用速度降低,您必须使用API 28+构造函数,并将反射用于较旧的Android版本。之后,您可以将asCoroutineDispatcher()
的扩展功能Handler
使用,并使用生成的调度程序。
为了使它对我和其他人更简单,我制作了一个(小型)库,该库提供了Dispatchers.MainAndroid
扩展名,该扩展名是延迟初始化的,没有任何I / O,可以代替Dispatchers.Main
使用。它还将Lifecycle
与协同程序范围集成在一起。
在此链接中,您可以查看如何获取依赖关系(在jcenter上可用)以及如何实现依赖关系:https://github.com/LouisCAD/Splitties/tree/master/modules/lifecycle-coroutines
答案 2 :(得分:0)
我将移除.penaltyDeath()
以防止其崩溃-并忽略该性能损失-因为它基本上是“责任之外”,除非是由自己造成的。
答案 3 :(得分:0)
问题在于,初始化Kotlin Coroutines的Dispatchers.Main使用大量的磁盘时间来读取和校验JAR。这不应该发生。
This issue in Kotlin Coroutines通过更快速的ServiceLoader解决。您应该使用a newer version的Kotlin协程,它提供了一种不对磁盘上的JAR进行校验和的解决方法ServiceLoader。
致力于R8优化器的Google Android团队也正在创建一个更好的解决方案,如果您已通过足够新的R8完全启用了ProGuard优化,则将在ProGuard步骤中完全优化ServiceLoader读取。
答案 4 :(得分:0)
不能真正解决问题,但是可以采用一种解决方法,忽略某个块的StrictMode,以便您可以继续在应用的其余部分中启用StrictMode:
fun <T> permitDiskReads(func: () -> T): T {
return if (BuildConfig.DEBUG) {
val oldThreadPolicy = StrictMode.getThreadPolicy()
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder(oldThreadPolicy).permitDiskReads().build())
val value = func()
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder(oldThreadPolicy).build())
value
} else {
func()
}
}
所以你可以做
class MyViewModel : ViewModel() {
fun foo() {
permitDiskReads { viewModelScope.launch(Dispatchers.IO) { } }
}
}