Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ ext {
version = [
major: 1,
minor: 4,
patch : 0
patch : 1
]
libGroup = 'com.shipdream'
libVersion = "${version.major}.${version.minor}.${version.patch}"
Expand Down
7 changes: 6 additions & 1 deletion extension/service-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.shipdream.lib.android.mvc.service.internal;


import android.content.Context;

import com.shipdream.lib.android.mvc.service.PreferenceService;
Expand Down
7 changes: 6 additions & 1 deletion extension/service-mediastore/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@

apply plugin: 'java'

configurations {
provided
compile.extendsFrom provided
}

task sourceJar(type: Jar) {
from sourceSets.main.java.srcDirs
classifier = 'sources'
Expand Down Expand Up @@ -45,5 +50,5 @@ sourceSets {
}

dependencies {
compile rootProject.ext.lib.androidMinSdk
provided rootProject.lib.androidMinSdk
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -76,6 +79,8 @@
* <p/>
*/
public class MvcGraph {
List<Provider> cachedInstancesBeforeNavigation = new ArrayList<>();
private Logger logger = LoggerFactory.getLogger(getClass());
ScopeCache singletonScopeCache;
DefaultProviderFinder defaultProviderFinder;
List<StateManaged> stateManagedObjects = new ArrayList<>();
Expand Down Expand Up @@ -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());
}
}
}

Expand Down Expand Up @@ -481,6 +491,7 @@ public <T> Provider<T> findProvider(Class<T> type, Annotation qualifier) throws
}

private static class MvcProvider<T> extends ProviderByClassType<T> {
private final Logger logger = LoggerFactory.getLogger(MvcGraph.class);
private List<StateManaged> stateManagedObjects;

public MvcProvider(List<StateManaged> stateManagedObjects, Class<T> type, Class<? extends T> implementationClass) {
Expand All @@ -500,6 +511,9 @@ public void onInjected(Object object) {
BaseControllerImpl controller = (BaseControllerImpl) object;
controller.onConstruct();
unregisterOnInjectedListener(this);

logger.trace("++Controller injected - '{}'.",
object.getClass().getSimpleName());
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<ScopeCache.CachedItem> 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<ScopeCache.CachedItem> getAllCachedInstances(MvcGraph mvcGraph) {
return mvcGraph.singletonScopeCache.getCachedItems();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -30,6 +32,20 @@ public interface NavigationController extends BaseController<NavigationControlle
* Navigation only takes effect when the given locationId is different from the current location
* and raises {@link EventC2V.OnLocationForward}
*
* <p>
* 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. <br>
*
* 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.
* </p>
*
* @param sender Who wants to navigate
* @param locationId The id of the location navigate to
*/
Expand All @@ -43,6 +59,20 @@ public interface NavigationController extends BaseController<NavigationControlle
* will be cleared. Navigation only takes effect when the given locationId is different from the
* current location and raises {@link EventC2V.OnLocationForward}
*
* <p>
* 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. <br>
*
* 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.
* </p>
*
* @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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading