首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Android:延迟加载DataBinding视图引发异常

Android:延迟加载DataBinding视图引发异常
EN

Stack Overflow用户
提问于 2021-12-05 07:12:15
回答 4查看 589关注 0票数 2

有一个大的数据绑定视图,可能需要几秒钟才能膨胀.我想给用户显示一个启动屏幕,并膨胀主视图,延迟操作。Android抛出一个异常“未能调用观察者方法”。

MainActivity:

代码语言:javascript
复制
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.screen_splash)
    Handler(Looper.getMainLooper()).postDelayed({
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
            this,
            R.layout.activity_main
        )
        binding.lifecycleOwner = this // this line throws exception
    }, 1000)
}

activity_main.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto">

<data>
    <variable
        name="vm"
        type="com.example.ViewModel"/>
</data>

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
            android:id="@+id/map_list"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity" />
</RelativeLayout>

例外:

代码语言:javascript
复制
2021-12-05 13:42:56.638 23701-23701/com.example E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example, PID: 23701
java.lang.RuntimeException: Failed to call observer method
    at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:226)
    at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194)
    at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185)
    at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:37)
    at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
    at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:196)
    at androidx.databinding.ViewDataBinding.setLifecycleOwner(ViewDataBinding.java:434)
    at com.example.databinding.ActivityMainBindingImpl.setLifecycleOwner(ActivityMainBindingImpl.java:166)
    at com.example.MainActivity.onCreate$lambda-3(MainActivity.kt:106)
    at com.example.MainActivity.$r8$lambda$lffeScwTEbHi2B1isKEoQYU2po4(Unknown Source:0)
    at com.example.MainActivity$$ExternalSyntheticLambda5.run(Unknown Source:2)
    at android.os.Handler.handleCallback(Handler.java:888)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:213)
    at android.app.ActivityThread.main(ActivityThread.java:8178)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)
 Caused by: java.lang.NumberFormatException: s == null
    at java.lang.Integer.parseInt(Integer.java:577)
    at java.lang.Integer.valueOf(Integer.java:801)
    at com.example.databinding.ControlPanelBindingImpl.executeBindings(ControlPanelBindingImpl.java:800)...
EN

回答 4

Stack Overflow用户

发布于 2021-12-07 08:24:24

我不确定你的申请结构。在我们的例子中,我们有一个类似的需求,我们希望在绑定初始fragment之前向加载程序显示。所以我们在viewStub中创建了一个activity。然后,当该片段被附加时,我们将共享视图模型中的liveData设置为SHOW,这将通知活动来膨胀viewStub。这样,我们就可以膨胀视图存根,它隐藏了显示飞溅图像的整个屏幕。然后,一旦创建了片段中的视图,并且在onViewCreated中,我们再次将共享视图模型中的liveData设置为HIDE,其中隐藏了viewStub,并显示了片段。

票数 0
EN

Stack Overflow用户

发布于 2021-12-07 16:36:07

在您的主要活动中使用fragmentContainerView。显示要在此容器中显示的视图。在容器前面创建一个视图。在此视图中显示splash消息。在加载主视图时,使splash视图的可见性消失。因此,启动屏幕将使用活动生命周期,主视图将使用片段生命周期。这可能是你的解决方案。

票数 0
EN

Stack Overflow用户

发布于 2021-12-09 22:42:34

免责声明:答案中的更新2部分解决了该问题;如果其他部分能够帮助未来的访问者解决其他潜在问题,则将留下其他部分。

乍一看,您认为Caused by: java.lang.NumberFormatException: s == null与这个问题有关;尽管您在评论中告诉我们,它是同步工作的。

通过在代码中跟踪错误,异常java.lang.RuntimeException: Failed to call observer method将无助于了解错误。

但是,您的代码在简单布局中成功地与我一起工作;这个问题可能与您在访问binding.lifecycleOwner时试图同步加载大量布局有关;我想后一个片段在访问lifecycleOwner之前需要一段时间。所以,你可以提前发布一些延迟。

为此,我将使用协同机制,而不是发布延迟;因为代码将更具线性和可读性:

代码语言:javascript
复制
CoroutineScope(Main).launch {

    delay(1000) // Original delay of yours
    val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
        this@MainActivity,
        R.layout.activity_main
    )

    delay(1000) // try and error to manipulate this delay 
    binding.lifecycleOwner = this@MainActivity 

}

如果尚未使用,则coroutine依赖项为

代码语言:javascript
复制
def coroutine_version = "1.5.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"

更新

当主活动加载时,代码中发布的延迟无助于显示延迟期间的启动/启动屏幕;

处理程序(Looper.getMainLooper()).postDelayed({ val binding = DataBindingUtil.setContentView( this,R.layout.activity_main ) //除非1000秒超过binding.lifecycleOwner = This },1000)

您的代码所做的:

  1. 显示了一个飞溅的屏幕。
  2. 延迟张贴(仍然主布局没有加载在这里)
  3. 延迟结束时显示主布局。

因此,发布的延迟只是累积到加载主布局的时间,这甚至使其更加滞后。此外,这并不是使用screen的推荐方法(这个中型员额会在这方面有所帮助)

相反,,我认为您打算做的是:

  1. 显示飞溅的屏幕
  2. 负荷主布局
  3. 发布延迟,以便主布局在延迟期间需要时间加载。
  4. 在延迟结束时显示主布局。

但是,问题是需要加载的东西是UI,它需要在主线程中,而不是在后台线程中这样做。因此,我们不使用两次不同的布局并调用setContentView()两次;您可以为主布局创建一个单一布局,并添加一些表示启动屏幕的视图,该视图将完全模糊主布局(位于其前面),直到加载布局(即延迟结束);然后删除这个启动视图:

演示:

splash_screen.xml (您想要的任何布局都必须与父级匹配,以使其模糊):

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/splash_screen"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:gravity="center">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher" />
</LinearLayout>

主要活动:

代码语言:javascript
复制
class MainActivity : AppCompatActivity() {

    companion object {
        const val TAG = "LOG_TAG"
    }

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Log.d(TAG, "Start Inflating layout")
        binding = DataBindingUtil.setContentView(
            this@MainActivity,
            R.layout.activity_main
        )

        // Show only the first time app launches, not in configuration changes
        if (savedInstanceState == null) {
            CoroutineScope(IO).launch {
                Log.d(TAG, "Start of delay")
                delay(1000)
                Log.d(TAG, "End of delay")
                withContext(Main) {
                    hideSplash()
                }
            }
            showSplash()
        }

        binding.lifecycleOwner = this@MainActivity
        Log.d(TAG, "End Inflating layout")

    }

    private fun showSplash() {
        supportActionBar?.hide()

        // Inflate splash screen layout
        val splashLayout =
            layoutInflater.inflate(
                R.layout.splash_screen,
                binding.rootLayout,
                false
            ) as LinearLayout

        binding.rootLayout.addView(
            splashLayout
        )

    }

    private fun hideSplash() {
        supportActionBar?.show()
        binding.rootLayout.removeView(
            findViewById(R.id.splash_screen)
        )
    }

}

日志

代码语言:javascript
复制
2021-12-11 21:59:18.349 20681-20681/  D/LOG_TAG: Start Inflating layout
2021-12-11 21:59:18.452 20681-20707/  D/LOG_TAG: Start of delay
2021-12-11 21:59:18.476 20681-20681/  D/LOG_TAG: End Inflating layout
2021-12-11 21:59:20.457 20681-20707/  D/LOG_TAG: End of delay

现在,延迟随着布局的膨胀而运行;加载时显示的启动屏幕;延迟结束时结束。

更新2

这绝对行不通:数据库=.行需要2.5秒才能完成,无法在"databinding.root“准备好之前添加视图。它在显示的代码中工作,因为您的主视图很小。

现在尝试将布局从setContentView()中的dataBinding中分离出来;但两者都需要位于主线程中。

代码语言:javascript
复制
setContentView(R.layout.screen_splash)

CoroutineScope(Main).launch {
    // Inflate main screen layout asynchronously 
    binding = ActivityMainBinding.inflate(layoutInflater) 

    delay(2500) // 2.5 sec delay of loading the mainLayout before setContentView

    setContentView(binding.root)
    binding.lifecycleOwner = this@MainActivity
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70232199

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档