通过动态功能中的 ServiceLoader 启动片段 (Android)

发布于 2025-01-16 23:34:03 字数 2031 浏览 0 评论 0原文

我尝试使用 ServiceLoader 在动态功能模块中启动片段,如下所示,但第一次尝试启动片段时发生崩溃。初次崩溃后,如果我尝试再次启动该片段,它会正常加载。

我在尝试启动片段之前检查模块是否已安装。为什么我仍然会遇到这种第一次崩溃?除非我让应用程序再空闲一秒钟,否则就好像 serviceLoader 没有完全加载一样。


splitInstallManager = SplitInstallManagerFactory.create(this)

val request = SplitInstallRequest
    .newBuilder()
    .addModule("dynamicFragment")
    .build()

splitInstallManager.startInstall(request)
    .addOnSuccessListener { sessionId ->

    if (!splitInstallManager.installedModules.contains("dynamicFragment"))
        return@addOnSuccessListener

    findViewById<TextView>(R.id.installStatus).text = "Successfully installed dynamic module, sessionId=$sessionId"

    val launch = findViewById<Button>(R.id.launchFragmentBtn)
    launch.visibility = View.VISIBLE
    launch.setOnClickListener {

        val serviceLoader = ServiceLoader.load(
            DynamicFragmentContract.Provider::class.java,
                 DynamicFragmentContract.Provider::class.java.classLoader)

        val c: DynamicFragmentContract = serviceLoader.iterator().next().get()
        val fragment = c as Fragment                        
   
        supportFragmentManager
            .beginTransaction().add(R.id.fragmentContainer, f).commit()
2022-03-25 21:06:31.960 18125-18125/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.myapp.dynamicfeaturemodulesapp, PID: 18125
    java.util.NoSuchElementException
        at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:366)
        at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:416)
        at java.util.ServiceLoader$1.next(ServiceLoader.java:494)
        at com.myapp.mylibrary.LibraryActivity.onCreate$lambda-3$lambda-1$lambda-0(LibraryActivity.kt:61)
        at com.myapp.mylibrary.LibraryActivity.$r8$lambda$_Qbmh4qCZoH4E5ov5s1Js7ZPauo(Unknown Source:0)
        at com.myapp.mylibrary.LibraryActivity$$ExternalSyntheticLambda1.onClick(Unknown Source:2)

I'm trying to launch a fragment in my a dynamic feature module using a ServiceLoader as follows, but I get a crash the first time I try to launch my fragment. After the initial crash, if I try to launch the fragment again, it loads fine.

I am checking if the module is installed before trying to launch the fragment. Why am I still getting this first-time crash? It's almost as if the serviceLoader isn't fully loaded unless I leave the app idle for another second.


splitInstallManager = SplitInstallManagerFactory.create(this)

val request = SplitInstallRequest
    .newBuilder()
    .addModule("dynamicFragment")
    .build()

splitInstallManager.startInstall(request)
    .addOnSuccessListener { sessionId ->

    if (!splitInstallManager.installedModules.contains("dynamicFragment"))
        return@addOnSuccessListener

    findViewById<TextView>(R.id.installStatus).text = "Successfully installed dynamic module, sessionId=$sessionId"

    val launch = findViewById<Button>(R.id.launchFragmentBtn)
    launch.visibility = View.VISIBLE
    launch.setOnClickListener {

        val serviceLoader = ServiceLoader.load(
            DynamicFragmentContract.Provider::class.java,
                 DynamicFragmentContract.Provider::class.java.classLoader)

        val c: DynamicFragmentContract = serviceLoader.iterator().next().get()
        val fragment = c as Fragment                        
   
        supportFragmentManager
            .beginTransaction().add(R.id.fragmentContainer, f).commit()
2022-03-25 21:06:31.960 18125-18125/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.myapp.dynamicfeaturemodulesapp, PID: 18125
    java.util.NoSuchElementException
        at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:366)
        at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:416)
        at java.util.ServiceLoader$1.next(ServiceLoader.java:494)
        at com.myapp.mylibrary.LibraryActivity.onCreate$lambda-3$lambda-1$lambda-0(LibraryActivity.kt:61)
        at com.myapp.mylibrary.LibraryActivity.$r8$lambda$_Qbmh4qCZoH4E5ov5s1Js7ZPauo(Unknown Source:0)
        at com.myapp.mylibrary.LibraryActivity$ExternalSyntheticLambda1.onClick(Unknown Source:2)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

弃爱 2025-01-23 23:34:03

结果 splitInstallManager.addOnSuccessListener 无法可靠地反映下载/安装状态。在我将其更改为使用 SplitInstallStateUpdatedListener 后,一切都按预期工作。

private val splitInstallStateUpdatedListener = SplitInstallStateUpdatedListener { state ->
        val names = state.moduleNames().joinToString(" - ")
        when (state.status()) {
            SplitInstallSessionStatus.DOWNLOADING -> {
                //  In order to see this, the application has to be uploaded to the Play Store.
                findViewById<TextView>(R.id.installStatus).text = "Downloading $names..."
            }
            SplitInstallSessionStatus.INSTALLED -> {
                findViewById<TextView>(R.id.installStatus).text =
                    "Successfully installed $names dynamic feature module"

                onSuccessfullyInstalled()
            }

            SplitInstallSessionStatus.INSTALLING -> {
                findViewById<TextView>(R.id.installStatus).text = "Installing $names..."
            }
            SplitInstallSessionStatus.FAILED -> {
                findViewById<TextView>(R.id.installStatus).text =
                    "Error: ${state.errorCode()} for module ${state.moduleNames()}"
            }
        }
    }

Turns out splitInstallManager.addOnSuccessListener wasn't reliably reflecting the download/install status. After I changed it to use the SplitInstallStateUpdatedListener, things are working as expected.

private val splitInstallStateUpdatedListener = SplitInstallStateUpdatedListener { state ->
        val names = state.moduleNames().joinToString(" - ")
        when (state.status()) {
            SplitInstallSessionStatus.DOWNLOADING -> {
                //  In order to see this, the application has to be uploaded to the Play Store.
                findViewById<TextView>(R.id.installStatus).text = "Downloading $names..."
            }
            SplitInstallSessionStatus.INSTALLED -> {
                findViewById<TextView>(R.id.installStatus).text =
                    "Successfully installed $names dynamic feature module"

                onSuccessfullyInstalled()
            }

            SplitInstallSessionStatus.INSTALLING -> {
                findViewById<TextView>(R.id.installStatus).text = "Installing $names..."
            }
            SplitInstallSessionStatus.FAILED -> {
                findViewById<TextView>(R.id.installStatus).text =
                    "Error: ${state.errorCode()} for module ${state.moduleNames()}"
            }
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文