我使用Mortar / Flow和Dagger 2设置了一个应用程序。除了在同一个类的两个视图之间切换时,它似乎有效。新视图最终以前一个视图的演示者结束。
例如,我有一个ConversationScreen,它将conversationId作为构造函数参数。我第一次创建ConversationScreen并将其添加到Flow时会创建ConversationView,它会为自己注入一个Presenter,该Presenter是使用传递给屏幕的conversationId创建的。如果我然后使用不同的conversationId创建一个新的ConversationScreen,当ConversationView请求Presenter时,Dagger将返回旧的Presenter,因为范围尚未在之前的ConversationScreen上关闭。
在设置新屏幕之前,我有办法手动关闭上一屏幕的范围吗?或者我刚开始设置范围错误?
ConversationView
public class ConversationView extends RelativeLayout {
@Inject
ConversationScreen.Presenter presenter;
public ConversationView(Context context, AttributeSet attrs) {
super(context, attrs);
DaggerService.<ConversationScreen.Component>getDaggerComponent(context).inject(this);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
presenter.takeView(this);
}
@Override
protected void onDetachedFromWindow() {
presenter.dropView(this);
super.onDetachedFromWindow();
}
}
ConversationScreen
@Layout(R.layout.screen_conversation)
public class ConversationScreen extends Paths.ConversationPath implements ScreenComponentFactory<SomeComponent> {
public ConversationScreen(String conversationId) {
super(conversationId);
}
@Override
public String getTitle() {
title = Conversation.get(conversationId).getTitle();
}
@Override
public Object createComponent(SomeComponent parent) {
return DaggerConversationScreen_Component.builder()
.someComponent(parent)
.conversationModule(new ConversationModule())
.build();
}
@dagger.Component(
dependencies = SomeComponent.class,
modules = ConversationModule.class
)
@DaggerScope(Component.class)
public interface Component {
void inject(ConversationView conversationView);
}
@DaggerScope(Component.class)
@dagger.Module
public class ConversationModule {
@Provides
@DaggerScope(Component.class)
Presenter providePresenter() {
return new Presenter(conversationId);
}
}
@DaggerScope(Component.class)
static public class Presenter extends BasePresenter<ConversationView> {
private String conversationId;
@Inject
Presenter(String conversationId) {
this.conversationId = conversationId;
}
@Override
protected void onLoad(Bundle savedInstanceState) {
super.onLoad(savedInstanceState);
bindData();
}
void bindData() {
// Show the messages in the conversation
}
}
}
答案 0 :(得分:2)
如果您使用Mortar / Flow示例项目中的默认ScreenScoper
和PathContextFactory
类,您将看到要创建的新范围的名称是Screen类的名称。
由于您希望从ConversationScreen
的一个实例导航到另一个ConversationScreen
实例,因此新范围的名称将等于先前范围的名称。因此,您不会创建新的Mortar范围,而只是重复使用前一个范围,这意味着重用相同的演示者。
您需要更改新范围的命名政策。而不是仅使用新屏幕类的名称,添加其他内容
最简单的修复方法是使用实例标识符:myScreen.toString()
。
另一个更好的解决方法是跟踪屏幕/范围名称。 以下示例摘自https://github.com/lukaspili/Mortar-architect
class EntryCounter {
private final SimpleArrayMap<Class, Integer> ids = new SimpleArrayMap<>();
int get(History.Entry entry) {
Class cls = entry.path.getClass();
return ids.containsKey(cls) ? ids.get(cls) : 0;
}
void increment(History.Entry entry) {
update(entry, true);
}
void decrement(History.Entry entry) {
update(entry, false);
}
private void update(History.Entry entry, boolean increment) {
Class cls = entry.path.getClass();
int id = ids.containsKey(cls) ? ids.get(cls) : 0;
ids.put(cls, id + (increment ? 1 : -1));
}
}
然后在创建新范围时使用此计数器:
private ScopedEntry buildScopedEntry(History.Entry entry) {
String scopeName = String.format("ARCHITECT_SCOPE_%s_%d", entry.path.getClass().getName(), entryCounter.get(entry));
return new ScopedEntry(entry, MortarFactory.createScope(navigator.getScope(), entry.path, scopeName));
}
在其他地方,如果推出新范围或范围被销毁,我正在递增/递减计数器。
答案 1 :(得分:2)
ScreenScoper
中的范围基于一个字符串,如果您创建相同的路径,它将使用与它基于路径的类名称相同的名称。
我通过从ScreenScoper中删除一些噪音来解决这个问题,因为我在Dagger2驱动的项目中并没有使用@ModuleFactory
。
public abstract class BasePath
extends Path {
public abstract int getLayout();
public abstract Object createComponent();
public abstract String getScopeName();
}
public class ScreenScoper {
public MortarScope getScreenScope(Context context, String name, Object screen) {
MortarScope parentScope = MortarScope.getScope(context);
return getScreenScope(parentScope, name, screen);
}
/**
* Finds or creates the scope for the given screen.
*/
public MortarScope getScreenScope(MortarScope parentScope, final String name, final Object screen) {
MortarScope childScope = parentScope.findChild(name);
if (childScope == null) {
BasePath basePath = (BasePath) screen;
childScope = parentScope.buildChild()
.withService(DaggerService.TAG, basePath.createComponent())
.build(name);
}
return childScope;
}
}