[英]How do I prevent Mortar scopes from persisting across screens?
我有一個使用Mortar / Flow和Dagger 2設置的應用程序。除了在同一個類的兩個視圖之間切換時,它似乎可以正常工作。 新視圖以先前視圖的演示者結束。
例如,我有一個ConversationScreen,它接受一個sessionId作為構造函數參數。 第一次創建ConversationScreen並將其添加到Flow時,它會創建ConversationView,並使用一個Presenter注入自身,該Presenter是使用傳遞到屏幕的sessionId創建的。 如果隨后我使用不同的sessionId創建一個新的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
}
}
}
如果您使用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));
}
在其他地方,如果要推送新作用域或破壞作用域,我將增加/減少計數器。
ScreenScoper
的范圍是基於字符串的,如果您創建相同的路徑,則它將使用相同的名稱,因為它基於路徑的類名稱。
考慮到我始終不在Dagger2驅動的項目中使用@ModuleFactory
,因此我通過消除了ScreenScoper中的一些噪聲來解決了這一問題。
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;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.