簡體   English   中英

如何防止Mortar示波器在屏幕上持久存在?

[英]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示例項目中的默認ScreenScoperPathContextFactory類,則將看到要創建的新作用域的名稱就是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM