如何防止迫击炮范围在屏幕上持续存在?

时间:2015-10-15 18:37:06

标签: android flow dagger-2 mortar

我使用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
        }
    }
}

2 个答案:

答案 0 :(得分:2)

如果您使用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));
}

在其他地方,如果推出新范围或范围被销毁,我正在递增/递减计数器。

答案 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;
    }
}