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

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

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

Не найдено представление для идентификатора 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)

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


person captaindroid    schedule 23.01.2020    source источник
comment
Является ли сторонняя библиотека открытой библиотекой (например, доступна на github), чтобы мы могли ее воспроизвести? И используется ли он только в dynamic-feature-module1 или также в App1 модуле или в других модулях на уровне dynamic-feature-module1?   -  person Vall0n    schedule 31.01.2020
comment
Возможно, стоит прочитать Как создать минимальный воспроизводимый пример, поскольку сообщения об ошибках без минимального количества кода довольно абстрактны.   -  person Martin Zeitler    schedule 31.01.2020


Ответы (3)


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

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

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

person Martin Zeitler    schedule 31.01.2020

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

Хотя использование отражения очень подвержено ошибкам, мы можем минимизировать их с помощью @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
    }
}

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

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

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

person Pavlo Ostasha    schedule 31.01.2020

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

R.id.settings будет:

getResources().getIdentifier("settings", "id", "com.library.package");
person mrtcnkryln    schedule 31.01.2020