Проблема циклической зависимости нескольких модулей AndroidAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Проблема циклической зависимости нескольких модулей Android

Сообщение Anonymous »

Хочу спросить вашего совета, как решить циклическую зависимость в многомодульном проекте Android?
Что я хотел бы получить в качестве ответа: идеально понятный ответ или совет, как решить проблему, с которой я столкнулся, но также буду благодарен за любые полезные ссылки на статьи, книги, видеоуроки - чтобы понять, как это решить.
Входные данные: в начале у нас было монолитное приложение на основе руководства по чистой архитектуре Android от Google с модулями: data, домен,
презентация.
Уровень представления: презентатор представления модели (не MVVM)
Вот пример со всеми его источниками данных, репозиториями и т. д.: https://github.com/android10/Android-CleanArchitecture
По мере роста проекта мы решили перейти к многомодульной архитектуре, чтобы добиться: отдельная функция - отдельный модуль,
потому что раньше у нас все это было объединено в такие пакеты как: презентеры, активности, фрагменты, модели и т.д..
На данный момент приложение состоит из таких модулей:

Код: Выделить всё

app -- just app
core:data -- datastores, requests to server, db etc.
core:domain -- business logic (interactors)
core:presentation -- view + presenters + all strings localizations + all graphics resources and layouts

features:feature_module_1 -- some feature with fragments, it contains data, domain, presentation layers and has dependencies for core module, because there are a lot of useful classes.
....
features:feature_module_N
Вот текущая ситуация с модулями в моем проекте, над которым я работаю:
Зависимости модулей приложения:

Код: Выделить всё

dependencies {
implementation project(':features:feature_module_1')
implementation project(':core:presentation')
implementation project(':core:data')
implementation project(':core:domain')
}
данные:

Код: Выделить всё

implementation project(':core:domain')
презентация:

Код: Выделить всё

dependencies {
implementation project(':core:domain')
implementation project(':core:data')
implementation project(':features:feature_module_1') -- when I add this - it writes "circular dependency between presentation and feature_module_1."
}
feature_module_1

Код: Выделить всё

dependencies {
implementation project(":core:presentation")
implementation project(":core:domain")
implementation project(":core:data")
}
Все ресурсы приложения, а также весь служебный и базовый код находятся в модуле core:presentation. В идеальной ситуации мы собираемся вынести
оттуда функцию/код в отдельные модули, чтобы сделать его как можно более тонким. Но это в будущем. В настоящее время время сборки этой
части занимает 2 минуты, если мы внесем туда небольшие изменения. Функциональные модули создаются быстро.
И вот описание проблемы: Как вы можете видеть из вышеупомянутой информации, Feature_module_1 зависит от ядра: модуль представления.
Хмл-файлы макета, связанные с Feature_module_1, я помещаю в Feature_module_1, потому что я хочу, чтобы все ресурсы, связанные с Feature
, находились в одной папке. Но есть несколько классов, которые мы создали уже давно, чтобы ускорить работу с задачами шаблона RecyclerAdapter. У нас есть перечисление ViewTypes для более чем 500 строк кода, которое оно содержит в себе ссылки на (viewHolder + файл item xml).
И в Feature_module_1 я хочу использовать этот универсальный RecyclerView. В моем Feature_module_1 я создал файлы MyCustomViewModel + MyCustomViewHolder + my_custom_layout.xml, надеясь, что все будет работать нормально, но чтобы все работало, мне нужно добавить запись в
enum ViewType с перечислением всех viewHolder в модуле core:presentation. А модуль core:presentation ничего не знает о
feature_module_1. Если я добавлю Feature_module_1 в качестве зависимости к модулю core:presentation - я получаю ошибку циклической зависимости и проект не собирается.
В качестве решения я могу взять все эти классы MyCustomViewModel + MyCustomViewHolder + my_custom_layout.xml и переместить их из
feature_module_1 -> core:presentation - тогда все будет работать, но мне бы не хотелось распространять функцию между разными модули.
Другое решение, которое я вижу:
Это переместить все классы, связанные с этим универсальным RecyclerAdapter + ViewType + все файлы, принадлежащие этому отдельному модулю, назовем его recycler_helper_module. И эти два модуля будут иметь это как зависимость: core:presentation & Feature_module_1
Но проблема просто перешла в другое место, потому что recycler_helper_module должен знать о файлах ресурсов макета (визуальных элементах представления recycler), и если этот новый модуль будет зависеть от модуля core:presentation - мы снова не получим циклическую зависимость.
Мы также можем переместить все файлы *_item_layout.xml адаптера в этот модуль. recycler_helper_module - тогда все должно работать, но нужно помнить, что такие файлы нужно добавлять каждый раз, когда мы что-то еще делаем в разных модулях.
Я тоже слышал об идее вынести все ресурсы в отдельный модуль, но не уловил, как это правильно сделать и как правильно управлять этим модулем и где размещать ресурсы для других модулей.
Напишите мне в комментариях, если вам нужно больше данных. Заранее спасибо!

Код: Выделить всё

Circular dependency between the following tasks:
:core:presentation:compileDebugAidl
\--- :features:myFeature1:compileDebugAidl
\--- :core:presentation:compileDebugAidl (*)
А вот что у меня есть в классах уровня представления:

Код: Выделить всё

public class RecyclerAdapter extends RecyclerView.Adapter{}

public interface RecyclerItem {

@LayoutRes
int getType();

/**
* Maximum number of ViewHolders to hold in the pool before discarding.
* @return max number
*/
default int getMaxSizeInPool() {
return DEFAULT_MAX_SCRAP;
}

default long getItemId() {
return getType();
}

default void onBindViewHolder(@NonNull T holder, int position, RecyclerItemListener listener) {
}

default void onBindViewHolder(@NonNull T holder, int position) {
}

default void onBindViewHolder(@NonNull T holder, int position, RecyclerAdapter adapter) {
}

default boolean areContentsTheSame(RecyclerItem obj) {
return (this == obj);
}

default boolean isPeriodicUpdateNeeded() {
return false;
}
}

enum class ViewType(private val viewType: Int) {
CUSTOM_LIST_ITEM(R.layout.custom_list_item) {
override fun getViewHolder(viewGroup: ViewGroup): BaseViewHolder {
return MyCustomModel.MyCustomHolder(
MyCustomListItemBinding.inflate(
LayoutInflater.from(viewGroup.context),
viewGroup,
false
)
)
}
};

abstract fun getViewHolder(viewGroup: ViewGroup): BaseViewHolder
fun createViewHolder(parent: ViewGroup): BaseViewHolder {
return getViewHolder(parent)
}

fun getInflatedView(parent: ViewGroup): View {
return LayoutInflater.from(parent.context).inflate(viewType, parent, false)
}

companion object {
private val map: MutableMap = HashMap()

@JvmStatic
fun valueOf(value: Int): ViewType {
return map[value]
?: throw IllegalArgumentException("view type for value $value not found in enum")
}

init {
for (type in values()) {
map[type.viewType] = type
}
}
}
}
Спасибо!

Подробнее здесь: https://stackoverflow.com/questions/706 ... ency-issue
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»