Сторонняя библиотека модуля динамических функций не может получить доступ к ресурсам

У меня есть приложение с динамическим функциональным модулем. В модуле динамических функций есть форма с изображениями, полями ввода, а также кнопка для доступа к другой сторонней библиотеке.

В сторонней библиотеке есть активность и фрагмент. При открытии фрагмента внутри активности я получаю сообщение об ошибке ниже, хотя в макете активности есть контейнер:

Не найдено представление для идентификатора 0x7f080053 (com.app.sample:id/container) для фрагмента SampleFragment{eed53f7 (5e4c0693-09a2-4725-a6de-1df49dd818f0) id=0x7f080053}

При доступе к чертежам в этой сторонней библиотеке возникает ошибка ниже:

java.lang.NoSuchFieldError: нет статического поля ic_back типа I в классе Lcom.third.library/R$drawable; или его суперклассы (объявление com.third.library.R$drawable находится в /data/app/com.app.sample-QtC8XuamC1fHEVU4FUpWaA==/split_thirdparty.apk)

Это нормально, когда я использую эту библиотеку в приложении без модуля динамических функций.

3 ответа

Обычно, когда SplitCompat.installActivity(this) не вызывается Activity2, это не сработает. Не имея исходного кода, вам придется извлечь пакет и правильно его повторно упаковать, потому чтоActivity2 (или даже весь пакет библиотеки), скорее всего, несовместим с DFM.

После того, как вы включите SplitCompat для своего базового приложения, вам необходимо включить SplitCompat для каждого действия, которое ваше приложение загружает в модуле динамических функций.

Вот еще один мой ответ, демонстрирующий доступ через отражение.

Динамическая доставка - относительно новая функция, поэтому она имеет множество ограничений. Одно из этих ограничений заключается в том, что вы не можете получить доступ к коду и ресурсам динамического модуля обычным способом, поэтому он не может быть зависимостью для других модулей. В настоящее время вы можете получить доступ к динамическому модулю через отражение и иметь динамические функции, определенные через общедоступные интерфейсы в модуле общей библиотеки, и загружать их фактические реализации (расположенные в модулях динамических функций) во время выполнения с помощью ServiceLoader. У него есть свои недостатки в производительности. Их можно минимизировать с помощью R8, используя ServiceLoaderRewriter но не полностью удален.

Хотя использование отражения очень подвержено ошибкам, мы можем минимизировать их либо с помощью @AutoService - AutoService это процессор аннотаций, который будет сканировать проект на предмет классов, помеченных @AutoService, для любого найденного класса он автоматически сгенерирует для него файл определения службы.

Вот небольшой пример того, как это делается

// All feature definitions extend this interface, T is the dependencies that the feature requires
interface Feature<T> {
    fun getMainScreen(): Fragment
    fun getLaunchIntent(context: Context): Intent
    fun inject(dependencies: T)
}

interface VideoFeature : Feature<VideoFeature.Dependencies> {
    interface Dependencies {
        val okHttpClient: OkHttpClient
        val context: Context
        val handler: Handler
        val backgroundDispatcher: CoroutineDispatcher
    }
}

internal var videoComponent: VideoComponent? = null
    private set

@AutoService(VideoFeature::class)
class VideoFeatureImpl : VideoFeature {
    override fun getLaunchIntent(context: Context): Intent = Intent(context, VideoActivity::class.java)

    override fun getMainScreen(): Fragment = createVideoFragment()

    override fun inject(dependencies: VideoFeature.Dependencies) {
        if (videoComponent != null) {
            return
        }

        videoComponent = DaggerVideoComponent.factory()
                .create(dependencies, this)
    }
}

И для фактического доступа к коду динамической функции используйте


inline fun <reified T : Feature<D>, D> FeatureManager.getFeature(
        dependencies: D
): T? {
    return if (isFeatureInstalled<T>()) {
        val serviceIterator = ServiceLoader.load(
                T::class.java,
                T::class.java.classLoader
        ).iterator()

        if (serviceIterator.hasNext()) {
            val feature = serviceIterator.next()
            feature.apply { inject(dependencies) }
        } else {
            null
        }
    } else {
        null
    }
}

Взято отсюда. Также там намного больше информации, поэтому я бы порекомендовал вам ее проверить.

Как правило, я бы просто не рекомендовал использовать динамическую функцию в качестве зависимости и соответствующим образом планировать архитектуру приложения.

Надеюсь, это поможет.

Для ресурсов эта часть кода может использоваться

R.id.settings было бы:

getResources().getIdentifier("settings", "id", "com.library.package");
Другие вопросы по тегам