diff --git a/ChangeLog.md b/ChangeLog.md
index 909deb6..38f7f60 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,7 @@
+Version: 1.4.1
+* Able to initialize the controller state referenced by next navigating fragment and retain the state until the navigation is fully performed when the navigation is requested directly via NavigationController in MvcGraph.use method.
+* Cached instances will be dereferenced and disposed when the instance is referenced by the fields with same variable name in both super and current class.
+
Version: 1.4.0
* Refactor of class AndroidMvc so that controllers can access the MvcGraph
* EventBusV2V is injectable same as EventBusC2V and EventBusC2C
diff --git a/build.gradle b/build.gradle
index 18c29d9..f265f70 100644
--- a/build.gradle
+++ b/build.gradle
@@ -74,7 +74,7 @@ ext {
version = [
major: 1,
minor: 4,
- patch : 0
+ patch : 1
]
libGroup = 'com.shipdream'
libVersion = "${version.major}.${version.minor}.${version.patch}"
diff --git a/extension/service-core/build.gradle b/extension/service-core/build.gradle
index 2ea8079..afda7c8 100644
--- a/extension/service-core/build.gradle
+++ b/extension/service-core/build.gradle
@@ -21,6 +21,11 @@ plugins {
apply plugin: 'java'
apply plugin: 'maven'
+configurations {
+ provided
+ compile.extendsFrom provided
+}
+
task sourceJar(type: Jar) {
from sourceSets.main.java.srcDirs
classifier = 'sources'
@@ -50,7 +55,7 @@ sourceSets {
}
dependencies {
- compile rootProject.ext.lib.androidMinSdk
+ provided rootProject.ext.lib.androidMinSdk
testCompile rootProject.ext.lib.junit
testCompile rootProject.ext.lib.mokito
diff --git a/extension/service-core/src/test/java/com/shipdream/lib/android/mvc/service/internal/TestPreferencesServiceImpl.java b/extension/service-core/src/test/java/com/shipdream/lib/android/mvc/service/internal/TestPreferencesServiceImpl.java
index 4c80c9b..cad7d01 100644
--- a/extension/service-core/src/test/java/com/shipdream/lib/android/mvc/service/internal/TestPreferencesServiceImpl.java
+++ b/extension/service-core/src/test/java/com/shipdream/lib/android/mvc/service/internal/TestPreferencesServiceImpl.java
@@ -16,7 +16,6 @@
package com.shipdream.lib.android.mvc.service.internal;
-
import android.content.Context;
import com.shipdream.lib.android.mvc.service.PreferenceService;
diff --git a/extension/service-mediastore/build.gradle b/extension/service-mediastore/build.gradle
index d656af8..69033e0 100644
--- a/extension/service-mediastore/build.gradle
+++ b/extension/service-mediastore/build.gradle
@@ -16,6 +16,11 @@
apply plugin: 'java'
+configurations {
+ provided
+ compile.extendsFrom provided
+}
+
task sourceJar(type: Jar) {
from sourceSets.main.java.srcDirs
classifier = 'sources'
@@ -45,5 +50,5 @@ sourceSets {
}
dependencies {
- compile rootProject.ext.lib.androidMinSdk
+ provided rootProject.lib.androidMinSdk
}
\ No newline at end of file
diff --git a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/Injector.java b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/Injector.java
index 615d592..9caa5cb 100644
--- a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/Injector.java
+++ b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/Injector.java
@@ -3,7 +3,7 @@
import com.shipdream.lib.poke.exception.PokeException;
public class Injector {
- private static MvcGraph mvcGraph;
+ static MvcGraph mvcGraph;
/**
* Config the dependencies of MvcGraph. Be careful to use this method because it will dump the
@@ -23,6 +23,9 @@ public static void configGraph(MvcGraph.BaseDependencies dependencies) {
* @return
*/
public static MvcGraph getGraph() {
+ if (mvcGraph == null) {
+ throw new RuntimeException("In unit testing, the graph needs to be mocked before running tests. See how the graph is prepared by TestControllerBase#prepareGraph() in https://github.com/kejunxia/AndroidMvc/blob/master/samples/note/core/src/test/java/com/shipdream/lib/android/mvc/samples/note/controller/internal/TestControllerBase.java");
+ }
return mvcGraph;
}
}
diff --git a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/MvcGraph.java b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/MvcGraph.java
index 7e6557d..379dca9 100644
--- a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/MvcGraph.java
+++ b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/MvcGraph.java
@@ -41,6 +41,9 @@
import com.shipdream.lib.poke.exception.ProviderConflictException;
import com.shipdream.lib.poke.exception.ProviderMissingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
@@ -76,6 +79,8 @@
*
*/
public class MvcGraph {
+ List cachedInstancesBeforeNavigation = new ArrayList<>();
+ private Logger logger = LoggerFactory.getLogger(getClass());
ScopeCache singletonScopeCache;
DefaultProviderFinder defaultProviderFinder;
List stateManagedObjects = new ArrayList<>();
@@ -105,6 +110,11 @@ public void onFreed(Provider provider) {
if (obj instanceof Disposable) {
((Disposable) obj).onDisposed();
}
+
+ if (obj instanceof BaseControllerImpl) {
+ logger.trace("--Controller freed - '{}'.",
+ obj.getClass().getSimpleName());
+ }
}
}
@@ -481,6 +491,7 @@ public Provider findProvider(Class type, Annotation qualifier) throws
}
private static class MvcProvider extends ProviderByClassType {
+ private final Logger logger = LoggerFactory.getLogger(MvcGraph.class);
private List stateManagedObjects;
public MvcProvider(List stateManagedObjects, Class type, Class extends T> implementationClass) {
@@ -500,6 +511,9 @@ public void onInjected(Object object) {
BaseControllerImpl controller = (BaseControllerImpl) object;
controller.onConstruct();
unregisterOnInjectedListener(this);
+
+ logger.trace("++Controller injected - '{}'.",
+ object.getClass().getSimpleName());
}
});
}
diff --git a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/MvcGraphException.java b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/MvcGraphException.java
index 37e1289..32ccf82 100644
--- a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/MvcGraphException.java
+++ b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/MvcGraphException.java
@@ -1,11 +1,7 @@
package com.shipdream.lib.android.mvc;
public class MvcGraphException extends RuntimeException {
- public MvcGraphException(String message) {
- super(message);
- }
-
- public MvcGraphException(String message, Throwable cause) {
+ MvcGraphException(String message, Throwable cause) {
super(message, cause);
}
}
\ No newline at end of file
diff --git a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/__MvcGraphHelper.java b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/__MvcGraphHelper.java
new file mode 100644
index 0000000..30016be
--- /dev/null
+++ b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/__MvcGraphHelper.java
@@ -0,0 +1,49 @@
+package com.shipdream.lib.android.mvc;
+
+import com.shipdream.lib.poke.Provider;
+import com.shipdream.lib.poke.ScopeCache;
+
+import java.util.Collection;
+
+/**
+ * Helper class to work with MvcGraph. Internal use only. Don't use it in your app.
+ */
+public class __MvcGraphHelper {
+ /**
+ * Internal use. Don't call it in app directly.
+ */
+ public static void retainCachedObjectsBeforeNavigation(MvcGraph mvcGraph) {
+ mvcGraph.cachedInstancesBeforeNavigation.clear();
+ //Retain all cached items before navigation.
+ Collection cachedItems = mvcGraph.singletonScopeCache.getCachedItems();
+ for (ScopeCache.CachedItem cachedItem : cachedItems) {
+ Provider provider = cachedItem.getProvider();
+ if (provider != null) {
+ mvcGraph.cachedInstancesBeforeNavigation.add(provider);
+ provider.retain();
+ }
+ }
+ }
+
+ /**
+ * Internal use. Don't call it in app directly.
+ */
+ public static void releaseCachedItemsAfterNavigation(MvcGraph mvcGraph) {
+ //Release all cached items after the fragment navigated to is ready to show.
+ for (Provider provider : mvcGraph.cachedInstancesBeforeNavigation) {
+ if (provider != null) {
+ provider.release();
+ }
+ }
+ mvcGraph.cachedInstancesBeforeNavigation.clear();
+ }
+
+ /**
+ * Internal use. Gets all cached items this cache still manages
+ * @return The collection of cached times
+ */
+ public static Collection getAllCachedInstances(MvcGraph mvcGraph) {
+ return mvcGraph.singletonScopeCache.getCachedItems();
+ }
+
+}
diff --git a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/NavigationController.java b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/NavigationController.java
index 15b7412..6caa270 100644
--- a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/NavigationController.java
+++ b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/NavigationController.java
@@ -16,9 +16,11 @@
package com.shipdream.lib.android.mvc.controller;
+import com.shipdream.lib.android.mvc.MvcGraph;
import com.shipdream.lib.android.mvc.NavLocation;
import com.shipdream.lib.android.mvc.event.BaseEventC2C;
import com.shipdream.lib.android.mvc.event.ValueChangeEventC2V;
+import com.shipdream.lib.poke.Consumer;
/**
* Controller to navigate among different fragments in the SAME activity.
@@ -30,6 +32,20 @@ public interface NavigationController extends BaseController
+ * Forward navigating will automatically manage continuity of state before and after the
+ * navigation is performed. This is useful when the next navigation location needs be configured
+ * with some initial state.
+ *
+ * For example, when navigate to a fragment called TimerFragment which counts down from an
+ * initial time value. We can create a TimerController[TimerModel] with the initial time
+ * value and inject it into TimerFragment. Before we navigate to TimerFragment, we can set the
+ * initial time value in TimerController[TimerModel] either by an injected field in current
+ * object who is calling this method or use {@link MvcGraph#use(Class, Consumer)} to inject and
+ * set the value on the fly. This value will be carried on to TimerFragment when it's created
+ * and ready to show.
+ *
+ *
* @param sender Who wants to navigate
* @param locationId The id of the location navigate to
*/
@@ -43,6 +59,20 @@ public interface NavigationController extends BaseController
+ * Forward navigating will automatically manage continuity of state before and after the
+ * navigation is performed. This is useful when the next navigation location needs be configured
+ * with some initial state.
+ *
+ * For example, when navigate to a fragment called TimerFragment which counts down from an
+ * initial time value. We can create a TimerController[TimerModel] with the initial time
+ * value and inject it into TimerFragment. Before we navigate to TimerFragment, we can set the
+ * initial time value in TimerController[TimerModel] either by an injected field in current
+ * object who is calling this method or use {@link MvcGraph#use(Class, Consumer)} to inject and
+ * set the value on the fly. This value will be carried on to TimerFragment when it's created
+ * and ready to show.
+ *
+ *
* @param sender Who wants to navigate
* @param locationId The id of the location navigate to
* @param clearTopToLocationId Null if all history locations want to be cleared otherwise, the
diff --git a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/internal/NavigationControllerImpl.java b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/internal/NavigationControllerImpl.java
index 1928e4e..3c25c2c 100644
--- a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/internal/NavigationControllerImpl.java
+++ b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/internal/NavigationControllerImpl.java
@@ -16,6 +16,8 @@
package com.shipdream.lib.android.mvc.controller.internal;
+import com.shipdream.lib.android.mvc.Injector;
+import com.shipdream.lib.android.mvc.__MvcGraphHelper;
import com.shipdream.lib.android.mvc.NavLocation;
import com.shipdream.lib.android.mvc.controller.NavigationController;
@@ -94,10 +96,25 @@ private void doNavigateTo(Object sender, String locationId, boolean clearTop,
getModel().setCurrentLocation(currentLoc);
String lastLocId = lastLoc == null ? null : lastLoc.getLocationId();
+
+ /**
+ * Retain all cached state managed objects. They will be retained until the fragment is
+ * created and ready to show. They will be released by the view ready call back by the
+ * fragment navigating to.
+ * fragment.registerOnViewReadyListener(new Runnable() {
+ @Override
+ public void run() {
+ Injector.getGraph().releaseCachedItems();
+ fragment.unregisterOnViewReadyListener(this);
+ }
+ });
+ */
+ __MvcGraphHelper.retainCachedObjectsBeforeNavigation(Injector.getGraph());
+
postC2VEvent(new EventC2V.OnLocationForward(sender, lastLoc, currentLoc, clearTop,
clearedTopToLocation));
- logger.debug("Nav Controller: Forward: {} -> {}", lastLocId, currentLoc.getLocationId());
+ logger.trace("Nav Controller: Forward: {} -> {}", lastLocId, currentLoc.getLocationId());
}
dumpHistory();
@@ -115,7 +132,7 @@ public void navigateBack(Object sender) {
getModel().setCurrentLocation(previousLoc);
postC2VEvent(new EventC2V.OnLocationBack(sender, currentLoc, previousLoc, false));
- logger.debug("Nav Controller: Backward: {} -> {}", currentLoc.getLocationId(),
+ logger.trace("Nav Controller: Backward: {} -> {}", currentLoc.getLocationId(),
previousLoc == null ? "null" : previousLoc.getLocationId());
checkAppExit(sender);
@@ -158,7 +175,7 @@ public void navigateBack(Object sender, String toLocationId) {
if(success) {
getModel().setCurrentLocation(currentLoc);
postC2VEvent(new EventC2V.OnLocationBack(sender, previousLoc, currentLoc, true));
- logger.debug("Nav Controller: Backward: {} -> {}", currentLoc.getLocationId(),
+ logger.trace("Nav Controller: Backward: {} -> {}", currentLoc.getLocationId(),
previousLoc.getLocationId());
checkAppExit(sender);
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestInjector.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestInjector.java
new file mode 100644
index 0000000..3477571
--- /dev/null
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestInjector.java
@@ -0,0 +1,55 @@
+package com.shipdream.lib.android.mvc;
+
+import com.shipdream.lib.android.mvc.event.bus.EventBus;
+import com.shipdream.lib.android.mvc.event.bus.annotation.EventBusC2C;
+import com.shipdream.lib.android.mvc.event.bus.internal.EventBusImpl;
+import com.shipdream.lib.poke.Component;
+import com.shipdream.lib.poke.Provides;
+
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.concurrent.ExecutorService;
+
+import javax.inject.Singleton;
+
+import static org.mockito.Mockito.mock;
+
+public class TestInjector {
+ @After
+ public void tearDown() throws Exception {
+ Injector.mvcGraph = null;
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void should_raise_exception_when_getting_mvc_graph_before_configuring_it() {
+ Injector.getGraph();
+ }
+
+ static class Comp extends Component {
+ @Provides
+ @EventBusC2C
+ @Singleton
+ public EventBus providesIEventBusC2C() {
+ return new EventBusImpl();
+ }
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void should_raise_runtime_exception_when_exception_occurring_when_configuring_mvc_graph_by_injector() {
+ MvcGraph.BaseDependencies baseDependencies = new MvcGraph.BaseDependencies() {
+ @Override
+ protected ExecutorService createExecutorService() {
+ return mock(ExecutorService.class);
+ }
+ };
+
+ Injector.configGraph(baseDependencies);
+
+ //Register component providing duplicate instances
+ Injector.getGraph().register(new Comp());
+
+ //Exception should be raised here
+ Injector.configGraph(baseDependencies);
+ }
+}
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraphHelper.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraphHelper.java
new file mode 100644
index 0000000..36f9deb
--- /dev/null
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraphHelper.java
@@ -0,0 +1,50 @@
+package com.shipdream.lib.android.mvc;
+
+import com.shipdream.lib.android.mvc.controller.NavigationController;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.concurrent.ExecutorService;
+
+import javax.inject.Inject;
+
+import static org.mockito.Mockito.mock;
+
+public class TestMvcGraphHelper {
+ @After
+ public void tearDown() throws Exception {
+ Injector.mvcGraph = null;
+ }
+
+ class View {
+ @Inject
+ private NavigationController navigationController;
+ }
+
+ @Test
+ public void should_retain_and_release_cached_instance_by_mvcgraph_helper() {
+ //Prepare graph
+ MvcGraph.BaseDependencies baseDependencies = new MvcGraph.BaseDependencies() {
+ @Override
+ protected ExecutorService createExecutorService() {
+ return mock(ExecutorService.class);
+ }
+ };
+
+ Injector.configGraph(baseDependencies);
+
+ View view = new View();
+ Injector.getGraph().inject(view);
+
+ int initSize = __MvcGraphHelper.getAllCachedInstances(Injector.getGraph()).size();
+
+ __MvcGraphHelper.retainCachedObjectsBeforeNavigation(Injector.getGraph());
+ Assert.assertNotEquals(initSize, __MvcGraphHelper.getAllCachedInstances(Injector.getGraph()));
+
+ __MvcGraphHelper.releaseCachedItemsAfterNavigation(Injector.getGraph());
+ Assert.assertEquals(initSize, __MvcGraphHelper.getAllCachedInstances(Injector.getGraph()).size());
+ }
+
+}
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/BaseControllerTest.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/BaseControllerTest.java
index c5e9599..02aa5b2 100644
--- a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/BaseControllerTest.java
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/BaseControllerTest.java
@@ -16,9 +16,10 @@
package com.shipdream.lib.android.mvc.controller;
-import com.shipdream.lib.android.mvc.event.bus.internal.EventBusImpl;
-import com.shipdream.lib.android.mvc.event.bus.EventBus;
+import com.shipdream.lib.android.mvc.Injector;
import com.shipdream.lib.android.mvc.MvcGraph;
+import com.shipdream.lib.android.mvc.event.bus.EventBus;
+import com.shipdream.lib.android.mvc.event.bus.internal.EventBusImpl;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
@@ -26,21 +27,47 @@
import org.apache.log4j.PatternLayout;
import org.junit.Before;
import org.junit.BeforeClass;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.concurrent.ExecutorService;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
public class BaseControllerTest {
- protected MvcGraph graph;
protected EventBus eventBusC2C;
protected EventBus eventBusC2V;
protected ExecutorService executorService;
+ private void prepareGraph() {
+ eventBusC2C = new EventBusImpl();
+ eventBusC2V = new EventBusImpl();
+ executorService = mock(ExecutorService.class);
+
+ Injector.configGraph(new MvcGraph.BaseDependencies() {
+ @Override
+ public EventBus createEventBusC2C() {
+ return eventBusC2C;
+ }
+
+ @Override
+ public EventBus createEventBusC2V() {
+ return eventBusC2V;
+ }
+
+ @Override
+ public ExecutorService createExecutorService() {
+ return executorService;
+ }
+ });
+ }
+
+ protected MvcGraph graph;
+
+ @Before
+ public void setUp() throws Exception {
+ prepareGraph();
+ graph = Injector.getGraph();
+ }
+
@BeforeClass
public static void beforeClass() {
ConsoleAppender console = new ConsoleAppender(); //create appender
@@ -53,23 +80,6 @@ public static void beforeClass() {
Logger.getRootLogger().addAppender(console);
}
- @Before
- public void setUp() throws Exception {
- eventBusC2C = new EventBusImpl();
- eventBusC2V = new EventBusImpl();
- executorService = mock(ExecutorService.class);
- doAnswer(new Answer() {
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- Runnable runnable = (Runnable) invocation.getArguments()[0];
- runnable.run();
- return null;
- }
- }).when(executorService).submit(any(Runnable.class));
-
- graph = new MvcGraph(new ControllerDependencies(this));
- }
-
static class ControllerDependencies extends MvcGraph.BaseDependencies {
private BaseControllerTest baseControllerTest;
diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationFromController.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationFromController.java
new file mode 100644
index 0000000..4d1dde0
--- /dev/null
+++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/nav/TestCaseNavigationFromController.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 Kejun Xia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.shipdream.lib.android.mvc.view.nav;
+
+import com.shipdream.lib.android.mvc.Injector;
+import com.shipdream.lib.android.mvc.controller.NavigationController;
+import com.shipdream.lib.android.mvc.view.AndroidMvc;
+import com.shipdream.lib.android.mvc.view.BaseTestCase;
+import com.shipdream.lib.poke.Component;
+import com.shipdream.lib.poke.Consumer;
+import com.shipdream.lib.poke.Provides;
+import com.shipdream.lib.poke.ScopeCache;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class TestCaseNavigationFromController extends BaseTestCase {
+ @Inject
+ private NavigationController navigationController;
+
+ private Comp comp;
+ private DisposeCheckerE disposeCheckerEMock;
+
+ public TestCaseNavigationFromController() {
+ super(MvcTestActivityNavigation.class);
+ }
+
+ @Override
+ protected void waitTest() throws InterruptedException {
+ waitTest(200);
+ }
+
+ public static class Comp extends Component{
+ TestCaseNavigationFromController testCaseNavigation;
+
+ Comp(ScopeCache scopeCache) {
+ super(scopeCache);
+ }
+
+ @Singleton
+ @Provides
+ public DisposeCheckerE providesDisposeCheckerE() {
+ return testCaseNavigation.disposeCheckerEMock;
+ }
+ }
+
+ @Override
+ protected void injectDependencies(ScopeCache mvcSingletonCache) {
+ super.injectDependencies(mvcSingletonCache);
+
+ disposeCheckerEMock = mock(DisposeCheckerE.class);
+ comp = new Comp(mvcSingletonCache);
+ comp.testCaseNavigation = this;
+ AndroidMvc.graph().register(comp);
+ }
+
+ @Override
+ protected void cleanDependencies() {
+ super.cleanDependencies();
+ AndroidMvc.graph().unregister(comp);
+ }
+
+ @Test
+ public void testShouldReleaseInjectionsAfterFragmentsArePoppedOut() throws Throwable {
+ onView(withText(NavFragmentA.class.getSimpleName())).check(matches(isDisplayed()));
+
+ final String val = "Value = " + new Random().nextInt();
+
+ Injector.getGraph().use(ControllerE.class, new Consumer() {
+ @Override
+ public void consume(ControllerE instance) {
+ instance.setValue(val);
+ navigationController.navigateTo(this, MvcTestActivityNavigation.Loc.E);
+ }
+ });
+
+ //The value set to controller e in Injector.getGraph().use should be retained during the
+ //navigation
+ onView(withText(val)).check(matches(isDisplayed()));
+
+ //The controller should not be disposed yet
+ verify(disposeCheckerEMock, times(0)).onDisposed();
+
+ navigationController.navigateBack(this);
+
+ //Controller should be disposed after navigated away from fragment E
+ waitTest();
+ verify(disposeCheckerEMock, times(1)).onDisposed();
+ }
+
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/ControllerE.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/ControllerE.java
new file mode 100644
index 0000000..4df87b5
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/ControllerE.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 Kejun Xia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.shipdream.lib.android.mvc.view.nav;
+
+import com.shipdream.lib.android.mvc.controller.BaseController;
+
+public interface ControllerE extends BaseController {
+ class Model {
+ public String value;
+ }
+
+ void setValue(String value);
+ String getValue();
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/DisposeCheckerE.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/DisposeCheckerE.java
new file mode 100644
index 0000000..00a3701
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/DisposeCheckerE.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 Kejun Xia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.shipdream.lib.android.mvc.view.nav;
+
+import com.shipdream.lib.android.mvc.Disposable;
+
+public interface DisposeCheckerE extends Disposable {
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/MvcTestActivityNavigation.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/MvcTestActivityNavigation.java
index 3eead6b..8a605e1 100644
--- a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/MvcTestActivityNavigation.java
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/MvcTestActivityNavigation.java
@@ -31,6 +31,7 @@ public static class Loc {
public static final String B = "TestNavigationFragmentB";
public static final String C = "TestNavigationFragmentC";
public static final String D = "TestNavigationFragmentD";
+ public static final String E = "TestNavigationFragmentE";
}
@Override
@@ -44,6 +45,8 @@ protected Class extends MvcFragment> mapNavigationFragment(String locationId)
return NavFragmentC.class;
case Loc.D :
return NavFragmentD.class;
+ case Loc.E :
+ return NavFragmentE.class;
default:
return null;
}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/NavFragmentE.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/NavFragmentE.java
new file mode 100644
index 0000000..d64a476
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/NavFragmentE.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Kejun Xia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.shipdream.lib.android.mvc.view.nav;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+
+import com.shipdream.lib.android.mvc.view.MvcFragment;
+import com.shipdream.lib.android.mvc.view.test.R;
+
+import javax.inject.Inject;
+
+public class NavFragmentE extends MvcFragment {
+ @Inject
+ private ControllerE controllerE;
+
+ private TextView textView;
+
+ @Override
+ protected int getLayoutResId() {
+ return R.layout.fragment_mvc_test_nav_e;
+ }
+
+ @Override
+ public void onViewReady(View view, Bundle savedInstanceState, Reason reason) {
+ super.onViewReady(view, savedInstanceState, reason);
+ textView = (TextView) view.findViewById(R.id.nav_frag_e_text);
+
+ textView.setText(controllerE.getValue());
+ }
+
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/internal/ControllerEImpl.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/internal/ControllerEImpl.java
new file mode 100644
index 0000000..597700a
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/internal/ControllerEImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 Kejun Xia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.shipdream.lib.android.mvc.view.nav.internal;
+
+import android.util.Log;
+
+import com.shipdream.lib.android.mvc.controller.internal.BaseControllerImpl;
+import com.shipdream.lib.android.mvc.view.nav.ControllerE;
+import com.shipdream.lib.android.mvc.view.nav.DisposeCheckerE;
+
+import javax.inject.Inject;
+
+public class ControllerEImpl extends BaseControllerImpl implements ControllerE {
+ private int value = 0;
+
+ @Inject
+ private DisposeCheckerE disposeCheckerE;
+
+ @Override
+ public void onDisposed() {
+ Log.i("DisposeCheck", "Checker E disposed");
+ disposeCheckerE.onDisposed();
+ }
+
+ @Override
+ protected Class getModelClassType() {
+ return Model.class;
+ }
+
+ @Override
+ public void setValue(String value) {
+ getModel().value = value;
+ }
+
+ @Override
+ public String getValue() {
+ return getModel().value;
+ }
+
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/internal/DisposeCheckerEImpl.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/internal/DisposeCheckerEImpl.java
new file mode 100644
index 0000000..2416d4e
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/nav/internal/DisposeCheckerEImpl.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 Kejun Xia
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.shipdream.lib.android.mvc.view.nav.internal;
+
+import android.util.Log;
+
+import com.shipdream.lib.android.mvc.view.nav.DisposeCheckerE;
+
+public class DisposeCheckerEImpl implements DisposeCheckerE {
+ @Override
+ public void onDisposed() {
+ Log.i("DisposeCheck", "Checker E disposed");
+ }
+}
diff --git a/library/android-mvc-test/src/main/res/layout/fragment_mvc_test_nav_e.xml b/library/android-mvc-test/src/main/res/layout/fragment_mvc_test_nav_e.xml
new file mode 100644
index 0000000..488cea6
--- /dev/null
+++ b/library/android-mvc-test/src/main/res/layout/fragment_mvc_test_nav_e.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/EventRegister.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/EventRegister.java
index 3407c8d..a51a24d 100644
--- a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/EventRegister.java
+++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/EventRegister.java
@@ -50,10 +50,10 @@ void registerEventBuses() {
eventBusC2V.register(androidComponent);
eventBusV2V.register(androidComponent);
eventsRegistered = true;
- logger.debug("+Event bus registered for view - '{}'.",
+ logger.trace("+Event bus registered for view - '{}'.",
androidComponent.getClass().getSimpleName());
} else {
- logger.debug("!Event bus already registered for view - '{}' and its controllers.",
+ logger.trace("!Event bus already registered for view - '{}' and its controllers.",
androidComponent.getClass().getSimpleName());
}
}
@@ -66,10 +66,10 @@ void unregisterEventBuses() {
eventBusC2V.unregister(androidComponent);
eventBusV2V.unregister(androidComponent);
eventsRegistered = false;
- logger.debug("-Event bus unregistered for view - '{}' and its controllers.",
+ logger.trace("-Event bus unregistered for view - '{}' and its controllers.",
androidComponent.getClass().getSimpleName());
} else {
- logger.debug("!Event bus already unregistered for view - '{}'.",
+ logger.trace("!Event bus already unregistered for view - '{}'.",
androidComponent.getClass().getSimpleName());
}
}
diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcActivity.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcActivity.java
index 6f81e08..84ffa1b 100644
--- a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcActivity.java
+++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcActivity.java
@@ -24,6 +24,8 @@
import android.support.v7.app.AppCompatActivity;
import android.view.View;
+import com.shipdream.lib.android.mvc.Injector;
+import com.shipdream.lib.android.mvc.__MvcGraphHelper;
import com.shipdream.lib.android.mvc.NavLocation;
import com.shipdream.lib.android.mvc.StateManaged;
import com.shipdream.lib.android.mvc.controller.NavigationController;
@@ -39,8 +41,10 @@
import javax.inject.Inject;
public abstract class MvcActivity extends AppCompatActivity {
+ private Logger logger = LoggerFactory.getLogger(getClass());
private static final String FRAGMENT_TAG_PREFIX = "__--AndroidMvc:Fragment:";
private DelegateFragment delegateFragment;
+ boolean toPrintAppExitMessage = false;
String getDelegateFragmentTag() {
return FRAGMENT_TAG_PREFIX + getDelegateFragmentClass().getName();
@@ -69,6 +73,17 @@ protected void onCreate(Bundle savedInstanceState) {
}
}
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ if (toPrintAppExitMessage && logger.isTraceEnabled()) {
+ logger.trace("App Exits(UI): {} injected objects are still cached.",
+ __MvcGraphHelper.getAllCachedInstances(Injector.getGraph()).size());
+ toPrintAppExitMessage = false;
+ }
+ }
+
void performSuperBackKeyPressed () {
super.onBackPressed();
}
@@ -396,7 +411,7 @@ private void performForwardNav(NavigationController.EventC2V.OnLocationForward e
FragmentTransaction transaction = fm.beginTransaction();
String fragmentTag = getFragmentTag(event.getCurrentValue().getLocationId());
- MvcFragment fragment;
+ final MvcFragment fragment;
try {
fragment = (MvcFragment) new ReflectUtils.newObjectByType(fragmentClass).newInstance();
fragment.injectDependencies();
@@ -404,15 +419,14 @@ private void performForwardNav(NavigationController.EventC2V.OnLocationForward e
throw new RuntimeException("Failed to instantiate fragment: " + fragmentClass.getName(), e);
}
+ MvcFragment lastFrag = null;
if (!event.isClearHistory()) {
- MvcFragment lastFrag = null;
if (event.getLastValue() != null && event.getLastValue().getLocationId() != null) {
lastFrag = (MvcFragment) fm.findFragmentByTag(
getFragmentTag(event.getLastValue().getLocationId()));
}
if (lastFrag != null) {
lastFrag.onPushingToBackStack();
- lastFrag.releaseDependencies();
}
} else {
NavLocation clearTopToLocation = event.getLocationWhereHistoryClearedUpTo();
@@ -431,6 +445,21 @@ private void performForwardNav(NavigationController.EventC2V.OnLocationForward e
logger.trace("Cleared fragment back stack up to {}", tagPopTo);
}
+ final MvcFragment finalLastFrag = lastFrag;
+ fragment.registerOnViewReadyListener(new Runnable() {
+ @Override
+ public void run() {
+ //Release reference count to pair the retaining by NavigationControllerImpl
+ // with Injector.getGraph().retainCachedObjectsBeforeNavigation();
+ __MvcGraphHelper.releaseCachedItemsAfterNavigation(Injector.getGraph());
+
+ if (finalLastFrag != null) {
+ finalLastFrag.releaseDependencies();
+ }
+
+ fragment.unregisterOnViewReadyListener(this);
+ }
+ });
transaction.replace(getContentLayoutResId(), fragment, fragmentTag);
transaction.addToBackStack(fragmentTag);
transaction.commit();
@@ -457,8 +486,10 @@ public void run() {
private void performBackNav(NavigationController.EventC2V.OnLocationBack event) {
NavLocation currentLoc = event.getCurrentValue();
if (currentLoc == null) {
+ MvcActivity mvcActivity = ((MvcActivity) getActivity());
//Back to null which should finish the current activity
- ((MvcActivity)getActivity()).performSuperBackKeyPressed();
+ mvcActivity.performSuperBackKeyPressed();
+ mvcActivity.toPrintAppExitMessage = true;
} else {
//FIXME: ChildFragmentManager hack - use getChildFragmentManager when bug is fixed
FragmentManager fm = childFragmentManager();
diff --git a/library/poke/src/main/java/com/shipdream/lib/poke/Graph.java b/library/poke/src/main/java/com/shipdream/lib/poke/Graph.java
index 5ddfeb5..d1e62a7 100644
--- a/library/poke/src/main/java/com/shipdream/lib/poke/Graph.java
+++ b/library/poke/src/main/java/com/shipdream/lib/poke/Graph.java
@@ -46,7 +46,7 @@ public abstract class Graph {
private List monitors;
private String revisitedNode = null;
private Set visitedInjectNodes = new LinkedHashSet<>();
- private Map