Что я хотел бы получить в качестве ответа: идеально понятный ответ или совет, как решить проблему, с которой я столкнулся, но также буду благодарен за любые полезные ссылки на статьи, книги, видеоуроки - чтобы понять, как это решить.
Входные данные: в начале у нас было монолитное приложение на основе руководства по чистой архитектуре 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."
}
Код: Выделить всё
dependencies {
implementation project(":core:presentation")
implementation project(":core:domain")
implementation project(":core:data")
}
оттуда функцию/код в отдельные модули, чтобы сделать его как можно более тонким. Но это в будущем. В настоящее время время сборки этой
части занимает 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
Мобильная версия