From 7c5f5e10644b6c786670ce411c9ede317ba8f1a7 Mon Sep 17 00:00:00 2001
From: Kejun Xia
Date: Sun, 15 Nov 2015 13:13:27 +1100
Subject: [PATCH 1/9] Retain controller state until navigation is fully done
---
ChangeLog.md | 3 ++
build.gradle | 2 +-
.../shipdream/lib/android/mvc/MvcGraph.java | 29 ++++++++++++++++++
.../mvc/controller/NavigationController.java | 30 +++++++++++++++++++
.../internal/NavigationControllerImpl.java | 22 ++++++++++++--
.../lib/android/mvc/view/EventRegister.java | 8 ++---
.../lib/android/mvc/view/MvcActivity.java | 10 ++++++-
.../com/shipdream/lib/poke/ScopeCache.java | 19 ++++++++++++
8 files changed, 114 insertions(+), 9 deletions(-)
diff --git a/ChangeLog.md b/ChangeLog.md
index 909deb6..6b05c17 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,6 @@
+Version: 1.4.1
+* Navigation will retain the state of all injected state managed objects including controllers until the next fragment is ready and shown.
+
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/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..417b692 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,7 @@
*
*/
public class MvcGraph {
+ private Logger logger = LoggerFactory.getLogger(getClass());
ScopeCache singletonScopeCache;
DefaultProviderFinder defaultProviderFinder;
List stateManagedObjects = new ArrayList<>();
@@ -105,6 +109,11 @@ public void onFreed(Provider provider) {
if (obj instanceof Disposable) {
((Disposable) obj).onDisposed();
}
+
+ if (obj instanceof BaseControllerImpl) {
+ logger.trace("--Controller freed - '{}'.",
+ obj.getClass().getSimpleName());
+ }
}
}
@@ -378,6 +387,22 @@ public void restoreAllStates(StateKeeper stateKeeper) {
}
}
+ /**
+ * Internal use. Don't call it in app directly.
+ */
+ public void retainCachedObjects() {
+ //Retain all cached items before navigation.
+ singletonScopeCache.retainAllCachedItems();
+ }
+
+ /**
+ * Internal use. Don't call it in app directly.
+ */
+ public void releaseCachedItems() {
+ //Release all cached items after the fragment navigated to is ready to show.
+ singletonScopeCache.releaseAllCachedItems();
+ }
+
/**
* Dependencies for all controllers
*/
@@ -481,6 +506,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 +526,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/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..982b713 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,7 @@
package com.shipdream.lib.android.mvc.controller.internal;
+import com.shipdream.lib.android.mvc.Injector;
import com.shipdream.lib.android.mvc.NavLocation;
import com.shipdream.lib.android.mvc.controller.NavigationController;
@@ -94,10 +95,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);
+ }
+ });
+ */
+ Injector.getGraph().retainCachedObjects();
+
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 +131,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 +174,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/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..ab4954c 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,7 @@
import android.support.v7.app.AppCompatActivity;
import android.view.View;
+import com.shipdream.lib.android.mvc.Injector;
import com.shipdream.lib.android.mvc.NavLocation;
import com.shipdream.lib.android.mvc.StateManaged;
import com.shipdream.lib.android.mvc.controller.NavigationController;
@@ -396,7 +397,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();
@@ -431,6 +432,13 @@ private void performForwardNav(NavigationController.EventC2V.OnLocationForward e
logger.trace("Cleared fragment back stack up to {}", tagPopTo);
}
+ fragment.registerOnViewReadyListener(new Runnable() {
+ @Override
+ public void run() {
+ Injector.getGraph().releaseCachedItems();
+ fragment.unregisterOnViewReadyListener(this);
+ }
+ });
transaction.replace(getContentLayoutResId(), fragment, fragmentTag);
transaction.addToBackStack(fragmentTag);
transaction.commit();
diff --git a/library/poke/src/main/java/com/shipdream/lib/poke/ScopeCache.java b/library/poke/src/main/java/com/shipdream/lib/poke/ScopeCache.java
index cb4cb33..a80ac43 100644
--- a/library/poke/src/main/java/com/shipdream/lib/poke/ScopeCache.java
+++ b/library/poke/src/main/java/com/shipdream/lib/poke/ScopeCache.java
@@ -31,6 +31,7 @@ public static class CachedItem {
Class type;
T instance;
Annotation qualifier;
+ Provider provider;
}
protected Map cache = new HashMap<>();
@@ -43,6 +44,7 @@ T get(Provider provider) throws ProvideException {
item = new CachedItem<>();
item.type = provider.type();
item.instance = provider.createInstance();
+ item.provider = provider;
if(item.instance == null) {
String qualifierName = (provider.getQualifier() == null) ? "null" : provider.getQualifier().getClass().getName();
throw new ProvideException(String.format("Provider (type: %s, qualifier: " +
@@ -72,4 +74,21 @@ public void removeCache(Class type, Annotation qualifier) {
cache.remove(PokeHelper.makeProviderKey(type, qualifier));
}
+ /**
+ * Retain references of all cached items
+ */
+ public void retainAllCachedItems() {
+ for (CachedItem cachedItem : cache.values()) {
+ cachedItem.provider.retain();
+ }
+ }
+
+ /**
+ * Release references of all cached items
+ */
+ public void releaseAllCachedItems() {
+ for (CachedItem cachedItem : cache.values()) {
+ cachedItem.provider.release();
+ }
+ }
}
From 8ea1a6d03fac14c33da102f8ecece7b11004bbb7 Mon Sep 17 00:00:00 2001
From: Kejun Xia
Date: Sun, 15 Nov 2015 16:49:32 +1100
Subject: [PATCH 2/9] Link sample code to remind how to setup unit test
---
extension/service-core/build.gradle | 6 +++-
extension/service-mediastore/build.gradle | 6 +++-
.../shipdream/lib/android/mvc/Injector.java | 3 ++
.../shipdream/lib/android/mvc/MvcGraph.java | 9 ++++++
.../com/shipdream/lib/poke/ScopeCache.java | 29 +++++++++++++++++--
.../internal/TestControllerBase.java | 22 +++++++++-----
6 files changed, 63 insertions(+), 12 deletions(-)
diff --git a/extension/service-core/build.gradle b/extension/service-core/build.gradle
index 2ea8079..57556bb 100644
--- a/extension/service-core/build.gradle
+++ b/extension/service-core/build.gradle
@@ -21,6 +21,10 @@ plugins {
apply plugin: 'java'
apply plugin: 'maven'
+configurations {
+ provided
+}
+
task sourceJar(type: Jar) {
from sourceSets.main.java.srcDirs
classifier = 'sources'
@@ -50,7 +54,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-mediastore/build.gradle b/extension/service-mediastore/build.gradle
index d656af8..3537458 100644
--- a/extension/service-mediastore/build.gradle
+++ b/extension/service-mediastore/build.gradle
@@ -16,6 +16,10 @@
apply plugin: 'java'
+configurations {
+ provided
+}
+
task sourceJar(type: Jar) {
from sourceSets.main.java.srcDirs
classifier = 'sources'
@@ -45,5 +49,5 @@ sourceSets {
}
dependencies {
- compile rootProject.ext.lib.androidMinSdk
+ provided rootProject.ext.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..4660fd8 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
@@ -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 417b692..ad7d04f 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
@@ -46,6 +46,7 @@
import java.lang.annotation.Annotation;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -403,6 +404,14 @@ public void releaseCachedItems() {
singletonScopeCache.releaseAllCachedItems();
}
+ /**
+ * Gets all cached items this cache still manages
+ * @return The collection of cached times
+ */
+ public Collection getAllCachedInstances() {
+ return singletonScopeCache.getCachedItems();
+ }
+
/**
* Dependencies for all controllers
*/
diff --git a/library/poke/src/main/java/com/shipdream/lib/poke/ScopeCache.java b/library/poke/src/main/java/com/shipdream/lib/poke/ScopeCache.java
index a80ac43..45ab90c 100644
--- a/library/poke/src/main/java/com/shipdream/lib/poke/ScopeCache.java
+++ b/library/poke/src/main/java/com/shipdream/lib/poke/ScopeCache.java
@@ -19,6 +19,7 @@
import com.shipdream.lib.poke.exception.ProvideException;
import java.lang.annotation.Annotation;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -32,6 +33,22 @@ public static class CachedItem {
T instance;
Annotation qualifier;
Provider provider;
+
+ public Class getType() {
+ return type;
+ }
+
+ public T getInstance() {
+ return instance;
+ }
+
+ public Annotation getQualifier() {
+ return qualifier;
+ }
+
+ public Provider getProvider() {
+ return provider;
+ }
}
protected Map cache = new HashMap<>();
@@ -75,7 +92,7 @@ public void removeCache(Class type, Annotation qualifier) {
}
/**
- * Retain references of all cached items
+ * Retains references of all cached items
*/
public void retainAllCachedItems() {
for (CachedItem cachedItem : cache.values()) {
@@ -84,11 +101,19 @@ public void retainAllCachedItems() {
}
/**
- * Release references of all cached items
+ * Releases references of all cached items
*/
public void releaseAllCachedItems() {
for (CachedItem cachedItem : cache.values()) {
cachedItem.provider.release();
}
}
+
+ /**
+ * Gets all cached items this cache still manages
+ * @return The collection of cached times
+ */
+ public Collection getCachedItems() {
+ return cache.values();
+ }
}
diff --git a/samples/note/core/src/test/java/com/shipdream/lib/android/mvc/samples/note/controller/internal/TestControllerBase.java b/samples/note/core/src/test/java/com/shipdream/lib/android/mvc/samples/note/controller/internal/TestControllerBase.java
index f4d7ad8..f4bd1d0 100644
--- a/samples/note/core/src/test/java/com/shipdream/lib/android/mvc/samples/note/controller/internal/TestControllerBase.java
+++ b/samples/note/core/src/test/java/com/shipdream/lib/android/mvc/samples/note/controller/internal/TestControllerBase.java
@@ -16,6 +16,7 @@
package com.shipdream.lib.android.mvc.samples.note.controller.internal;
+import com.shipdream.lib.android.mvc.Injector;
import com.shipdream.lib.android.mvc.MvcGraph;
import com.shipdream.lib.android.mvc.controller.BaseController;
import com.shipdream.lib.android.mvc.controller.internal.BaseControllerImpl;
@@ -33,14 +34,14 @@ public abstract class TestControllerBase ext
protected EventBus eventBusC2C;
protected EventBus eventBusC2V;
protected ExecutorService executorService;
- private MvcGraph mvcGraph;
protected Controller controllerToTest;
- @Before
- public void setUp() throws Exception {
+ private void prepareGraph() {
eventBusC2C = new EventBusImpl();
eventBusC2V = new EventBusImpl();
- mvcGraph = new MvcGraph(new MvcGraph.BaseDependencies() {
+ executorService = mock(ExecutorService.class);
+
+ Injector.configGraph(new MvcGraph.BaseDependencies() {
@Override
public EventBus createEventBusC2C() {
return eventBusC2C;
@@ -56,16 +57,21 @@ public ExecutorService createExecutorService() {
return executorService;
}
});
- executorService = mock(ExecutorService.class);
- registerDependencies(mvcGraph);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ prepareGraph();
+
+ registerDependencies(Injector.getGraph());
controllerToTest = createTestingController();
- mvcGraph.inject(controllerToTest);
+ Injector.getGraph().inject(controllerToTest);
((BaseControllerImpl)controllerToTest).onConstruct();
}
@After
public void tearDown() throws Exception {
- mvcGraph.release(controllerToTest);
+ Injector.getGraph().release(controllerToTest);
}
protected void registerDependencies(MvcGraph mvcGraph) {
From 2c2fa5696be792b80ccb8c5a31ac21b7947965cd Mon Sep 17 00:00:00 2001
From: Kejun Xia
Date: Sun, 15 Nov 2015 17:07:01 +1100
Subject: [PATCH 3/9] Fix unit tests
---
extension/service-core/build.gradle | 1 +
.../internal/TestPreferencesServiceImpl.java | 1 -
extension/service-mediastore/build.gradle | 3 +-
.../mvc/controller/BaseControllerTest.java | 58 +++++++++++--------
.../internal/TestCounterController.java | 28 +++++----
5 files changed, 54 insertions(+), 37 deletions(-)
diff --git a/extension/service-core/build.gradle b/extension/service-core/build.gradle
index 57556bb..afda7c8 100644
--- a/extension/service-core/build.gradle
+++ b/extension/service-core/build.gradle
@@ -23,6 +23,7 @@ apply plugin: 'maven'
configurations {
provided
+ compile.extendsFrom provided
}
task sourceJar(type: Jar) {
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 3537458..69033e0 100644
--- a/extension/service-mediastore/build.gradle
+++ b/extension/service-mediastore/build.gradle
@@ -18,6 +18,7 @@ apply plugin: 'java'
configurations {
provided
+ compile.extendsFrom provided
}
task sourceJar(type: Jar) {
@@ -49,5 +50,5 @@ sourceSets {
}
dependencies {
- provided rootProject.ext.lib.androidMinSdk
+ provided rootProject.lib.androidMinSdk
}
\ No newline at end of file
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/samples/simple/src/test/java/com/shipdream/lib/android/mvc/samples/simple/controller/internal/TestCounterController.java b/samples/simple/src/test/java/com/shipdream/lib/android/mvc/samples/simple/controller/internal/TestCounterController.java
index f6a89cf..39c0fb4 100644
--- a/samples/simple/src/test/java/com/shipdream/lib/android/mvc/samples/simple/controller/internal/TestCounterController.java
+++ b/samples/simple/src/test/java/com/shipdream/lib/android/mvc/samples/simple/controller/internal/TestCounterController.java
@@ -16,6 +16,7 @@
package com.shipdream.lib.android.mvc.samples.simple.controller.internal;
+import com.shipdream.lib.android.mvc.Injector;
import com.shipdream.lib.android.mvc.MvcGraph;
import com.shipdream.lib.android.mvc.controller.NavigationController;
import com.shipdream.lib.android.mvc.event.bus.EventBus;
@@ -54,24 +55,21 @@ public static void beforeClass() {
Logger.getRootLogger().addAppender(console);
}
- //Dependencies of base controllers
- protected EventBus eventBusC2C;
- protected EventBus eventBusC2V;
- protected ExecutorService executorService;
//The graph used to inject
private MvcGraph mvcGraph;
private CounterControllerImpl counterController;
- @Before
- public void setUp() throws Exception {
- //create instance of CounterController
- counterController = new CounterControllerImpl();
+ protected EventBus eventBusC2C;
+ protected EventBus eventBusC2V;
+ protected ExecutorService executorService;
- //Prepare dependencies for injection
+ private void prepareGraph() {
eventBusC2C = new EventBusImpl();
eventBusC2V = new EventBusImpl();
- mvcGraph = new MvcGraph(new MvcGraph.BaseDependencies() {
+ executorService = mock(ExecutorService.class);
+
+ Injector.configGraph(new MvcGraph.BaseDependencies() {
@Override
public EventBus createEventBusC2C() {
return eventBusC2C;
@@ -87,7 +85,15 @@ public ExecutorService createExecutorService() {
return executorService;
}
});
- executorService = mock(ExecutorService.class);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ prepareGraph();
+ mvcGraph = Injector.getGraph();
+
+ //create instance of CounterController
+ counterController = new CounterControllerImpl();
//inject dependencies into controller
mvcGraph.inject(counterController);
From 105a228737ed77f2d2c42f1241ee5e7ce5096ba3 Mon Sep 17 00:00:00 2001
From: Kejun Xia
Date: Mon, 16 Nov 2015 20:13:51 +1100
Subject: [PATCH 4/9] Fields with same name in both super and current class
will be released correctly
---
.../shipdream/lib/android/mvc/MvcGraph.java | 23 +++++-
.../internal/NavigationControllerImpl.java | 2 +-
.../lib/android/mvc/view/MvcActivity.java | 13 +++-
.../java/com/shipdream/lib/poke/Graph.java | 78 +++++++++++++------
.../java/com/shipdream/lib/poke/Provider.java | 24 +++---
.../com/shipdream/lib/poke/ScopeCache.java | 18 -----
.../lib/poke/TestCircularDependencies.java | 75 +++++++-----------
.../lib/poke/TestInjectionReferenceCount.java | 55 ++++++++++---
8 files changed, 168 insertions(+), 120 deletions(-)
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 ad7d04f..7b1d2c1 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
@@ -388,20 +388,35 @@ public void restoreAllStates(StateKeeper stateKeeper) {
}
}
+ private List cachedInstancesBeforeNavigation = new ArrayList<>();
+
/**
* Internal use. Don't call it in app directly.
*/
- public void retainCachedObjects() {
+ public void retainCachedObjectsBeforeNavigation() {
+ cachedInstancesBeforeNavigation.clear();
//Retain all cached items before navigation.
- singletonScopeCache.retainAllCachedItems();
+ Collection cachedItems = singletonScopeCache.getCachedItems();
+ for (ScopeCache.CachedItem cachedItem : cachedItems) {
+ Provider provider = cachedItem.getProvider();
+ if (provider != null) {
+ cachedInstancesBeforeNavigation.add(provider);
+ provider.retain();
+ }
+ }
}
/**
* Internal use. Don't call it in app directly.
*/
- public void releaseCachedItems() {
+ public void releaseCachedItemsAfterNavigation() {
//Release all cached items after the fragment navigated to is ready to show.
- singletonScopeCache.releaseAllCachedItems();
+ for (Provider provider : cachedInstancesBeforeNavigation) {
+ if (provider != null) {
+ provider.release();
+ }
+ }
+ cachedInstancesBeforeNavigation.clear();
}
/**
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 982b713..7311bd5 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
@@ -108,7 +108,7 @@ public void run() {
}
});
*/
- Injector.getGraph().retainCachedObjects();
+ Injector.getGraph().retainCachedObjectsBeforeNavigation();
postC2VEvent(new EventC2V.OnLocationForward(sender, lastLoc, currentLoc, clearTop,
clearedTopToLocation));
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 ab4954c..0758a0a 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
@@ -405,15 +405,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();
@@ -432,10 +431,18 @@ 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() {
- Injector.getGraph().releaseCachedItems();
+ //Release reference count to pair the retaining by NavigationControllerImpl
+ // with Injector.getGraph().retainCachedObjectsBeforeNavigation();
+ Injector.getGraph().releaseCachedItemsAfterNavigation();
+
+ if (finalLastFrag != null) {
+ finalLastFrag.releaseDependencies();
+ }
+
fragment.unregisterOnViewReadyListener(this);
}
});
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