我有一個 MainActivity 使用 DrawerLayout 和帶有 2 個片段的選項卡。
我的第一個片段包含 RecyclerView 中的元素串列,我可以單擊每個元素以“選擇”它(它呼叫 SDK 函式以登錄到硬體設備)。選中后,這會觸發 Fragment 的 ViewModel 的更改:
// Selected device changes when an item is clicked
private val _devices = MutableLiveData<List<DeviceListItemViewModel>>()
private val _selectedDevice = MutableLiveData<ConnectedDevice>()
val devices: LiveData<List<DeviceListItemViewModel>> by this::_devices
val selectedDevice: LiveData<ConnectedDevice> by this::_selectedDevice
然后我在兩個片段之間有一個共享的 ViewModel,它也有一個currentDevice像這樣的變數:
private val _currentDevice = MutableLiveData<ConnectedDevice>()
val currentDevice: LiveData<ConnectedDevice> by this::_currentDevice
所以在包含串列的 Fragment 中,我有以下代碼來更新共享的 ViewModel 變數:
private val mViewModel: DeviceManagementViewModel by viewModels()
private val mSharedViewModel: MainActivityViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Log.d(classTag, "Fragment view created")
val binding = ActivityMainDevicesManagementFragmentBinding.bind(view)
binding.apply {
viewModel = mViewModel
lifecycleOwner = viewLifecycleOwner
}
// Observe fragment ViewModel
// If any device is clicked on the list, do the login on the shared ViewModel
mViewModel.selectedDevice.observe(this, {
mSharedViewModel.viewModelScope.launch {
if (it != null) {
mSharedViewModel.setCurrentDevice(videoDevice = it)
} else mSharedViewModel.unsetCurrentDevice()
}
})
}
問題是,如果設定了共享 ViewModel 的currentDevice變數,則每當我嘗試打開 Dialog 或啟動新活動時都會遇到例外。如果我修改共享 ViewModel 中的 setCurrentDevice 函式,則它可以正常作業(或者如果我不選擇任何設備)。
啟動新活動時,我看到的例外情況是:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.placeholder.easyview/com.example.myapp.activities.settings.SettingsActivity}: java.lang.IllegalArgumentException: display must not be null
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3430)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3594)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2067)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7698)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)
Caused by: java.lang.IllegalArgumentException: display must not be null
at android.app.ContextImpl.createDisplayContext(ContextImpl.java:2386)
at android.content.ContextWrapper.createDisplayContext(ContextWrapper.java:977)
at com.android.internal.policy.DecorContext.<init>(DecorContext.java:50)
at com.android.internal.policy.PhoneWindow.generateDecor(PhoneWindow.java:2348)
at com.android.internal.policy.PhoneWindow.installDecor(PhoneWindow.java:2683)
at com.android.internal.policy.PhoneWindow.getDecorView(PhoneWindow.java:2116)
at androidx.appcompat.app.AppCompatActivity.initViewTreeOwners(AppCompatActivity.java:219)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:194)
at com.example.myapp.activities.settings.SettingsActivity.onCreate(SettingsActivity.kt:34)
at android.app.Activity.performCreate(Activity.java:8000)
at android.app.Activity.performCreate(Activity.java:7984)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1310)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3403)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3594)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2067)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7698)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)
如果我嘗試打開對話框,則會出現這種情況:
java.lang.ArrayIndexOutOfBoundsException: length=16; index=2448
at android.view.InsetsState.peekSource(InsetsState.java:374)
at android.view.InsetsSourceConsumer.updateSource(InsetsSourceConsumer.java:291)
at android.view.InsetsController.updateState(InsetsController.java:654)
at android.view.InsetsController.onStateChanged(InsetsController.java:621)
at android.view.ViewRootImpl.setView(ViewRootImpl.java:1058)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:409)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:110)
at android.app.Dialog.show(Dialog.java:340)
at android.app.AlertDialog$Builder.show(AlertDialog.java:1131)
at com.example.myapp.activities.main.fragments.DeviceManagementFragment.showAddDeviceMethodDialog(DeviceManagementFragment.kt:151)
at com.example.myapp.activities.main.fragments.DeviceManagementFragment.access$showAddDeviceMethodDialog(DeviceManagementFragment.kt:33)
at com.example.myapp.activities.main.fragments.DeviceManagementFragment$onViewCreated$$inlined$apply$lambda$1.onClick(DeviceManagementFragment.kt:52)
at android.view.View.performClick(View.java:7448)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28309)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7698)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)
編輯:看起來問題實際上出在另一個片段中,在那里我有以下代碼(在onViewCreated方法中):
// If the shared view model device changes, this must change too
mSharedViewModel.currentDevice.observe(this, {
if (it != null) {
mViewModel.setCurrentDevice(it)
} else mViewModel.unsetCurrentDevice()
})
mViewModel.currentDevice.observe(this, {
if (it != null) {
mViewModel.fetchChannels()
}
})
如果我注釋掉第二部分(fetchChannels發生的地方),它運行良好。即使我只注釋掉fetchChannels呼叫,它也能作業。
這是fetchChannels函式的代碼:
fun fetchChannels() = viewModelScope.launch {
Log.d(classTag, "Getting channels for device ${currentDevice.value}")
currentDevice.value?.let {
val fetchedChannels = deviceLibManager.getChannels(it.videoDevice)
_currentDevice.value?.channels?.clear()
_currentDevice.value?.channels?.addAll(fetchedChannels)
if (fetchedChannels.isNotEmpty()) {
_currentDevice.value?.currentChannel = fetchedChannels[0]
}
}
}
下面這行給我帶來了麻煩:
val fetchedChannels = deviceLibManager.getChannels(it.videoDevice)
該功能就是這樣:
suspend fun getChannels(videoDevice: VideoDevice): List<VideoChannel> {
try {
Log.i(classTag, "Getting channels from device ${videoDevice}")
val channels = videoDevice.getChannelsAsync()
return channels
} catch (exception: Exception) {
when (exception) {
is UnknownVendorException -> {
Log.w(classTag, "Device ${videoDevice} cannot get channels because the vendor is unknown")
}
is NetworkException -> {
Log.w(classTag, "Device ${videoDevice} cannot get channels because it is unreachable")
}
else -> {
Log.w(classTag, "Device ${videoDevice} cannot get channels, reason: ${exception.message}")
}
}
return emptyList()
}
}
SDK 中的實作是這樣的:
override suspend fun getChannelsAsync(): List<VideoChannel> = withContext(Dispatchers.IO) {
Log.i(classTag, "Trying to get channels for device: $logName")
val channels = ArrayList<VideoChannel>()
getZeroChannel()?.let {
channels.add(it)
}
channels.addAll(getAllChannels())
if (channels.isNotEmpty()) {
Log.i(classTag, "Successfully retrieved ${channels.size} channels for device: $logName")
return@withContext channels
} else {
Log.w(classTag, "Error retrieving channels for device $logName or no channels exist")
throw Exception()
}
}
其他函式只是進行網路呼叫并檢索一些資料,它根本不應該與 UI 混淆。
我正在使用 Android 10 使用小米 Mi A3 進行測驗。
有人能幫我嗎?謝謝你。
uj5u.com熱心網友回復:
所以我真的不知道為什么,但我找到了答案。
在 SDK 中,函式getZeroChannel和getAllChannels不是掛起函式,盡管它們進行網路呼叫。所以我所做的是:
將
withContext(Dispatchers.IO)部分移至這兩個函式(實際進行網路呼叫的函式),并使它們掛起函式。withContext(Dispatchers.IO)從getChannelsAsync功能中洗掉該部件。將其保留為掛起功能。
在這些更改之后,一切都按預期作業。我仍然不知道為什么,所以如果有人能解釋一下,那將是非常感激的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/324314.html
標籤:安卓 android-fragments 虚拟机 android-alertdialog android-数据绑定
上一篇:如何從對話框片段內的按鈕在onClick事件中請求權限
下一篇:Android:工具列中的泄漏
