diff --git a/.travis.yml b/.travis.yml
index 35b99b4..63385af 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,10 +7,10 @@ android:
- tools
# The BuildTools version used by your project
- - build-tools-22.0.1
+ - build-tools-23.0.1
# The SDK version used to compile your project
- - android-22
+ - android-23
# Additional components
- extra-google-m2repository
@@ -18,11 +18,11 @@ android:
# Specify at least one system image,
# if you need to run emulator(s) during your tests
- # - sys-img-armeabi-v7a-android-22
+ # - sys-img-armeabi-v7a-android-23
# Emulator Management: Create, Start and Wait
#before_script:
- #- echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a
+ #- echo no | android create avd --force -n test -t android-23 --abi armeabi-v7a
#- emulator -avd test -no-skin -no-audio -no-window &
#- android-wait-for-emulator
#- adb shell input keyevent 82 &
diff --git a/ChangeLog.md b/ChangeLog.md
index 3f79557..909deb6 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,10 @@
+Version: 1.4.0
+* Refactor of class AndroidMvc so that controllers can access the MvcGraph
+* EventBusV2V is injectable same as EventBusC2V and EventBusC2C
+* Add method "use" in MvcGraph to consume an injectable classes which doesn't require a class field marked by @Inject
+* Remove BaseControllerImpl.onInitialized and replaced by onConstruct since onInitialized is also called when the controller is restored
+* Add call back BaseControllerImpl.onRestored
+
Version: 1.3.0
* Refactor the MvcFragment.Reason object.
* Remove android-mvc-controller-retrofit
@@ -24,7 +31,7 @@ Once root fragment is restored, it should notify all nested fragments they are n
Fix issue that nested fragments of popping out fragment won't call onReturnFromBackground incorrectly
Version: 1.1.6
-Fix issue that onViewReady is called with incorrect reason - RESTORE which should be FIRST_TIME when the fragment is a page and recreated by FragmentStatePagerAdapter
+Fix issue that onViewReady is called with incorrect reason - RESTORE which should be FIRST_TIME when the fragment is a page and recreated by FragmentPagerAdapter
Version: 1.1.5
Allow remove preference key from SharedPreferenceService
diff --git a/README.md b/README.md
index 4dea962..27a442f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
# AndroidMvc Framework
[](https://travis-ci.org/kejunxia/AndroidMvc)
[](https://coveralls.io/r/kejunxia/AndroidMvc)
+[](https://bintray.com/kejunxia/maven/android-mvc/_latestVersion)
[](https://maven-badges.herokuapp.com/maven-central/com.shipdream/android-mvc)
# Website
@@ -26,20 +27,22 @@
See [**Source code** here](https://github.com/kejunxia/AndroidMvc/tree/master/samples/note) and download [**Sample APK** here](https://docs.google.com/uc?authuser=0&id=0BwcZml9gnwoZOHcxZFI3Z0ZGUUk&export=download)
## Download
-The library is currently release to jCenter and MavenCentral
+The library is currently released to both
+* jCenter [](https://bintray.com/kejunxia/maven/android-mvc/_latestVersion)
+* Maven Central [](https://maven-badges.herokuapp.com/maven-central/com.shipdream/android-mvc)
**Maven:**
```xml
com.shipdreamandroid-mvc
- 1.3.0
+ [LatestVersion]
```
**Gradle:**
```groovy
-compile "com.shipdream:android-mvc:1.3.0"
+compile "com.shipdream:android-mvc:[LatestVersion]"
```
## Dependency injection with reference count
diff --git a/build.gradle b/build.gradle
index 9d51bad..18c29d9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,10 +29,9 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.1.+'
+ classpath 'com.android.tools.build:gradle:1.3.1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
- classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
- classpath 'com.github.dcendents:android-maven-plugin:1.2'
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.0.1'
}
@@ -74,7 +73,7 @@ ext {
gitUrl = 'https://github.com/kejunxia/AndroidMvc.git' // Git repository URL
version = [
major: 1,
- minor: 3,
+ minor: 4,
patch : 0
]
libGroup = 'com.shipdream'
@@ -82,13 +81,13 @@ ext {
shouldPublish = false
androidMinSdkVersion = 14
- androidCompileSdkVersion = 22
- supportLibVersion = "22.2.1"
+ androidCompileSdkVersion = 23
+ supportLibVersion = "23.1.0"
androidTargetSdkVersion = androidCompileSdkVersion
lib = [
androidMinSdk: 'com.google.android:android:4.0.1.2',
androidSupportLib: "com.android.support:appcompat-v7:$supportLibVersion",
- androidBuildToolVersion: "22.0.1",
+ androidBuildToolVersion: "23.0.1",
junit: 'junit:junit:4.12',
mokito: 'org.mockito:mockito-core:1.9.5',
slf4jApi: "org.slf4j:slf4j-api:$log4jVersion",
diff --git a/documents/sites/Site-MarkDown.md b/documents/sites/Site-MarkDown.md
index 581cff3..c00d186 100644
--- a/documents/sites/Site-MarkDown.md
+++ b/documents/sites/Site-MarkDown.md
@@ -33,20 +33,22 @@ First of all, let's look at some problems of the Android development below:
- Well tested by jUnit and instrument test with Espresso.
## Download
-The library is currently release to jCenter and MavenCentral
+The library is currently released to both
+* jCenter [](https://bintray.com/kejunxia/maven/android-mvc/_latestVersion)
+* Maven Central [](https://maven-badges.herokuapp.com/maven-central/com.shipdream/android-mvc)
**Maven:**
```xml
com.shipdreamandroid-mvc
- 1.3.0
+ [LatestVersion]
```
**Gradle:**
```groovy
-compile "com.shipdream:android-mvc:1.3.0"
+compile "com.shipdream:android-mvc:[LatestVersion]"
```
## Samples
diff --git a/extension/service-core/build.gradle b/extension/service-core/build.gradle
index 44c59ee..2ea8079 100644
--- a/extension/service-core/build.gradle
+++ b/extension/service-core/build.gradle
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+plugins {
+ id "com.jfrog.bintray" version "1.4"
+}
+
apply plugin: 'java'
apply plugin: 'maven'
-apply plugin: 'com.jfrog.bintray'
task sourceJar(type: Jar) {
from sourceSets.main.java.srcDirs
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4bd6796..83e4ce0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -19,4 +19,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip
+- add org.gradle.daemon=true
\ No newline at end of file
diff --git a/library/android-mvc-controller/build.gradle b/library/android-mvc-controller/build.gradle
index c8fe718..00c5867 100644
--- a/library/android-mvc-controller/build.gradle
+++ b/library/android-mvc-controller/build.gradle
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+plugins {
+ id "com.jfrog.bintray" version "1.4"
+}
+
apply plugin: 'java'
apply plugin: 'maven'
-apply plugin: 'com.jfrog.bintray'
task sourceJar(type: Jar) {
from sourceSets.main.java.srcDirs
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
new file mode 100644
index 0000000..615d592
--- /dev/null
+++ b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/Injector.java
@@ -0,0 +1,28 @@
+package com.shipdream.lib.android.mvc;
+
+import com.shipdream.lib.poke.exception.PokeException;
+
+public class Injector {
+ private static MvcGraph mvcGraph;
+
+ /**
+ * Config the dependencies of MvcGraph. Be careful to use this method because it will dump the
+ * existing graph and all injectable instances managed by it
+ * @param dependencies the dependencies.
+ */
+ public static void configGraph(MvcGraph.BaseDependencies dependencies) {
+ try {
+ mvcGraph = new MvcGraph(dependencies);
+ } catch (PokeException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Get the graph managing injectable objects.
+ * @return
+ */
+ public static MvcGraph getGraph() {
+ 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 114b8cf..7e6557d 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
@@ -16,7 +16,6 @@
package com.shipdream.lib.android.mvc;
-import com.shipdream.lib.android.mvc.controller.BaseController;
import com.shipdream.lib.android.mvc.controller.internal.AsyncTask;
import com.shipdream.lib.android.mvc.controller.internal.BaseControllerImpl;
import com.shipdream.lib.android.mvc.event.bus.EventBus;
@@ -24,21 +23,23 @@
import com.shipdream.lib.android.mvc.event.bus.annotation.EventBusC2V;
import com.shipdream.lib.android.mvc.event.bus.internal.EventBusImpl;
import com.shipdream.lib.poke.Component;
+import com.shipdream.lib.poke.Consumer;
import com.shipdream.lib.poke.Graph;
import com.shipdream.lib.poke.ImplClassLocator;
import com.shipdream.lib.poke.ImplClassLocatorByPattern;
import com.shipdream.lib.poke.ImplClassNotFoundException;
import com.shipdream.lib.poke.Provider;
+import com.shipdream.lib.poke.Provider.OnFreedListener;
import com.shipdream.lib.poke.ProviderByClassType;
import com.shipdream.lib.poke.ProviderFinderByRegistry;
import com.shipdream.lib.poke.Provides;
import com.shipdream.lib.poke.ScopeCache;
import com.shipdream.lib.poke.SimpleGraph;
import com.shipdream.lib.poke.exception.CircularDependenciesException;
+import com.shipdream.lib.poke.exception.PokeException;
import com.shipdream.lib.poke.exception.ProvideException;
import com.shipdream.lib.poke.exception.ProviderConflictException;
import com.shipdream.lib.poke.exception.ProviderMissingException;
-import com.shipdream.lib.poke.Provider.OnFreedListener;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
@@ -172,24 +173,124 @@ public void clearOnProviderFreedListeners() {
}
/**
- * Get an instance matching the type and qualifier. If there is an instance cached, the cached
- * instance will be returned otherwise a new instance will be created.
- *
- *
Note that, not like {@link #inject(Object)} (Object)} this method will NOT increment
- * reference count for the injectable object with the same type and qualifier.
- * @param type the type of the object
- * @param qualifier the qualifier of the injected object. Null is allowed if no qualifier is specified
- * @return The cached object or a new instance matching the type and qualifier
- * @throws MvcGraphException throw if exception occurs during getting the instance
+ * Same as {@link #use(Class, Annotation, Consumer)} except using un-qualified injectable type.
+ * @param type The type of the injectable instance
+ * @param consumer Consume to use the instance
*/
- public T get(Class type, Annotation qualifier) {
+ public void use(Class type, Consumer consumer) {
try {
- return graph.get(type, qualifier, Inject.class);
- } catch (ProviderMissingException e) {
- throw new MvcGraphException(e.getMessage(), e);
- } catch (ProvideException e) {
+ graph.use(type, Inject.class, consumer);
+ } catch (PokeException e) {
throw new MvcGraphException(e.getMessage(), e);
- } catch (CircularDependenciesException e) {
+ }
+ }
+
+ /**
+ * Use an injectable instance in the scope of {@link Consumer#consume(Object)} without injecting
+ * it as a field of an object. This method will automatically retain the instance before
+ * {@link Consumer#consume(Object)} is called and released after it's returned. As a result,
+ * it doesn't hold the instance like the field marked by {@link Inject} that will retain the
+ * reference of the instance until {@link #release(Object)} is called. However, in the
+ * scope of {@link Consumer#consume(Object)} the instance will be held.
+ *
For example,
+ *
+ interface Os {
+ }
+
+ static class DeviceComponent extends Component {
+ @Provides
+ @Singleton
+ public Os provide() {
+ return new Os(){
+ };
+ }
+ }
+
+ class Device {
+ @Inject
+ private Os os;
+ }
+
+ mvcGraph.register(new DeviceComponent());
+
+ //OsReferenceCount = 0
+ mvcGraph.use(Os.class, null, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //First time to create the instance.
+ //OsReferenceCount = 1
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+
+ final Device device = new Device();
+ mvcGraph.inject(device); //OsReferenceCount = 1
+ //New instance created and cached
+
+ mvcGraph.use(Os.class, null, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //Since reference count is greater than 0, cached instance will be reused
+ //OsReferenceCount = 2
+ Assert.assertTrue(device.os == instance);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 1
+
+ mvcGraph.release(device); //OsReferenceCount = 0
+ //Last instance released, so next time a new instance will be created
+
+ mvcGraph.use(Os.class, null, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.os != instance);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+
+ mvcGraph.use(Os.class, null, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.os != instance);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+ //Cached instance cleared again
+
+ mvcGraph.use(Os.class, null, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ mvcGraph.inject(device);
+ //Injection will reuse the cached instance and increment the reference count
+ //OsReferenceCount = 2
+
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.os == instance);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 1
+
+ mvcGraph.release(device); //OsReferenceCount = 0
+ *
+ * @param type The type of the injectable instance
+ * @param qualifier Qualifier for the injectable instance
+ * @param consumer Consume to use the instance
+ * @throws MvcGraphException throw when there are exceptions during the consumption of the instance
+ */
+ public void use(Class type, Annotation qualifier, Consumer consumer) {
+ try {
+ graph.use(type, qualifier, Inject.class, consumer);
+ } catch (PokeException e) {
throw new MvcGraphException(e.getMessage(), e);
}
}
@@ -392,14 +493,12 @@ public MvcProvider(List stateManagedObjects, Class type, Class<
public T createInstance() throws ProvideException {
final T newInstance = (T) super.createInstance();
- if (newInstance instanceof BaseController) {
+ if (newInstance instanceof BaseControllerImpl) {
registerOnInjectedListener(new OnInjectedListener() {
@Override
public void onInjected(Object object) {
- if (object instanceof BaseController) {
- BaseController controller = (BaseController) object;
- controller.init();
- }
+ BaseControllerImpl controller = (BaseControllerImpl) object;
+ controller.onConstruct();
unregisterOnInjectedListener(this);
}
});
diff --git a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/BaseController.java b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/BaseController.java
index eeb4362..f4b88ac 100644
--- a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/BaseController.java
+++ b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/BaseController.java
@@ -34,12 +34,6 @@ public interface BaseController {
*/
MODEL getModel();
- /**
- * Initialized the controller and create an empty model. If a specific model needs to be bound
- * to this controller use {@link #bindModel(Object, Object)}
- */
- void init();
-
/**
* Bind a prepared non-null model to the controller
* @param sender Who wants to bind it
diff --git a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/internal/BaseControllerImpl.java b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/internal/BaseControllerImpl.java
index 9579baf..f029107 100644
--- a/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/internal/BaseControllerImpl.java
+++ b/library/android-mvc-controller/src/main/java/com/shipdream/lib/android/mvc/controller/internal/BaseControllerImpl.java
@@ -60,11 +60,17 @@ interface AndroidPoster {
private MODEL model;
- @Override
- public void init() {
+ /**
+ * Called when the controller is constructed. Note that it could be called either when the
+ * controller is instantiated for the first time or restored by views.
+ *
+ *
The model of the controller will be instantiated by model's default no-argument constructor.
+ * However, if the controller needs to be restored, a new instance of model restored by
+ * {@link #restoreState(Object)} will replace the model created here.
+ */
+ public void onConstruct() {
model = createModelInstance();
eventBusC2C.register(this);
- onInitialized();
}
private MODEL createModelInstance() {
@@ -80,14 +86,8 @@ private MODEL createModelInstance() {
}
/**
- * Called when the controller is newly initialized.
- */
- protected void onInitialized() {
- }
-
- /**
- * Called when the controller is disposed. When the controller is not referenced any more this
- * should be called.
+ * Called when the controller is disposed. This occurs when the controller is de-referenced and
+ * not retained by any objects.
*/
@Override
public void onDisposed() {
@@ -143,10 +143,8 @@ final public Class getStateType() {
* Method of {@link StateManaged} that allows {@link StateKeeper} to save and get the state of
* which is also the model the controller.
*
- * Note that if the controller doesn't need to get its state saved and restored
- * automatically. e.g. The controller always loads resource from remote services so that
- * its state can be thought persisted by the remote services when {@link #getModelClassType()}
- * returns null, the method will have no effect.
+ * Note that if the controller doesn't need its state saved and restored automatically return
+ * null in {@link #getModelClassType()} and then this method will have no effect.
*
*
* @param restoredState The restored state by {@link StateKeeper} that will be bound to the
@@ -157,6 +155,13 @@ final public void restoreState(MODEL restoredState) {
if (getModelClassType() != null) {
bindModel(this, restoredState);
}
+ onRestored();
+ }
+
+ /**
+ * Called when the controller is restored after {@link #restoreState(Object)} is called.
+ */
+ public void onRestored() {
}
@Override
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraph.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraph.java
index 7da131a..77c4b5f 100644
--- a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraph.java
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/TestMvcGraph.java
@@ -17,11 +17,13 @@
package com.shipdream.lib.android.mvc;
import com.shipdream.lib.poke.Component;
+import com.shipdream.lib.poke.Consumer;
import com.shipdream.lib.poke.Graph;
+import com.shipdream.lib.poke.Provider.OnFreedListener;
+import com.shipdream.lib.poke.Provides;
import com.shipdream.lib.poke.ScopeCache;
import com.shipdream.lib.poke.exception.ProvideException;
import com.shipdream.lib.poke.exception.ProviderConflictException;
-import com.shipdream.lib.poke.Provider.OnFreedListener;
import org.junit.Assert;
import org.junit.Before;
@@ -29,10 +31,18 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
@@ -66,6 +76,225 @@ protected ExecutorService createExecutorService() {
});
}
+ interface Os {
+ }
+
+ @Qualifier
+ @Documented
+ @Retention(RUNTIME)
+ @interface Apple {
+ }
+
+ @Qualifier
+ @Documented
+ @Retention(RUNTIME)
+ @interface Google {
+ }
+
+ static class iOS implements Os {
+
+ }
+
+ static class Android implements Os {
+
+ }
+
+ static class DeviceComponent extends Component {
+ @Provides
+ @Singleton
+ public Os provide() {
+ return new Android();
+ }
+
+ @Provides
+ @Singleton
+ @Apple
+ public Os provideIos() {
+ return new iOS();
+ }
+
+ @Provides
+ @Singleton
+ @Google
+ public Os provideAndroid() {
+ return new Android();
+ }
+ }
+
+ class Device {
+ @Inject
+ private Os android;
+
+ @Inject
+ @Apple
+ private Os os;
+ }
+
+ @Test
+ public void use_method_should_retain_and_release_instance_without_qualifier_correctly() {
+ mvcGraph.register(new DeviceComponent());
+
+ //OsReferenceCount = 0
+ mvcGraph.use(Os.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //First time to create the instance.
+ //OsReferenceCount = 1
+ Assert.assertTrue(instance instanceof Android);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+
+ final Device device = new Device();
+ mvcGraph.inject(device); //OsReferenceCount = 1
+ //New instance created and cached
+
+ mvcGraph.use(Os.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //Since reference count is greater than 0, cached instance will be reused
+ //OsReferenceCount = 2
+ Assert.assertTrue(device.android == instance);
+ Assert.assertTrue(instance instanceof Android);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 1
+
+ mvcGraph.release(device); //OsReferenceCount = 0
+ //Last instance released, so next time a new instance will be created
+
+ mvcGraph.use(Os.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.android != instance);
+ Assert.assertTrue(instance instanceof Android);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+
+ mvcGraph.use(Os.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.android != instance);
+ Assert.assertTrue(instance instanceof Android);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+ //Cached instance cleared again
+
+ mvcGraph.use(Os.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ mvcGraph.inject(device);
+ //Injection will reuse the cached instance and increment the reference count
+ //OsReferenceCount = 2
+
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.android == instance);
+ Assert.assertTrue(instance instanceof Android);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 1
+
+ mvcGraph.release(device); //OsReferenceCount = 0
+ }
+
+ @Test
+ public void use_method_should_retain_and_release_instance_correctly() {
+ mvcGraph.register(new DeviceComponent());
+
+ @Apple
+ class NeedIoS {
+
+ }
+
+ Annotation iosQualifier = NeedIoS.class.getAnnotation(Apple.class);
+
+ //OsReferenceCount = 0
+ mvcGraph.use(Os.class, iosQualifier, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //First time to create the instance.
+ //OsReferenceCount = 1
+ Assert.assertTrue(instance instanceof iOS);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+
+ final Device device = new Device();
+ mvcGraph.inject(device); //OsReferenceCount = 1
+ //New instance created and cached
+
+ mvcGraph.use(Os.class, iosQualifier, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //Since reference count is greater than 0, cached instance will be reused
+ //OsReferenceCount = 2
+ Assert.assertTrue(device.os == instance);
+ Assert.assertTrue(instance instanceof iOS);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 1
+
+ mvcGraph.release(device); //OsReferenceCount = 0
+ //Last instance released, so next time a new instance will be created
+
+ mvcGraph.use(Os.class, iosQualifier, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.os != instance);
+ Assert.assertTrue(instance instanceof iOS);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+
+ mvcGraph.use(Os.class, iosQualifier, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.os != instance);
+ Assert.assertTrue(instance instanceof iOS);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+ //Cached instance cleared again
+
+ mvcGraph.use(Os.class, iosQualifier, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ mvcGraph.inject(device);
+ //Injection will reuse the cached instance and increment the reference count
+ //OsReferenceCount = 2
+
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.os == instance);
+ Assert.assertTrue(instance instanceof iOS);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 1
+
+ mvcGraph.release(device); //OsReferenceCount = 0
+ }
+
@Test
public void should_delegate_mvc_graph_properly() throws ProvideException, ProviderConflictException {
// Arrange
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/BaseNavigationControllerTest.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/BaseNavigationControllerTest.java
index a57ab11..fe9daba 100644
--- a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/BaseNavigationControllerTest.java
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/BaseNavigationControllerTest.java
@@ -21,13 +21,13 @@
import org.junit.Before;
public class BaseNavigationControllerTest extends BaseControllerTest{
- protected NavigationController navigationController;
+ protected NavigationControllerImpl navigationController;
@Before
public void setUp() throws Exception {
super.setUp();
navigationController = new NavigationControllerImpl();
graph.inject(navigationController);
- navigationController.init();
+ navigationController.onConstruct();
}
}
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java
index f411dcf..7f00e18 100644
--- a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/TestRunAsyncTask.java
@@ -53,7 +53,7 @@ public void setUp() throws Exception {
controller = new MyControllerImpl();
graph.inject(controller);
- controller.init();
+ controller.onConstruct();
eventMonitor = mock(EventMonitor.class);
eventBusC2V.register(eventMonitor);
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/internal/TestBaseControllerImpl.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/internal/TestBaseControllerImpl.java
index 7ef5c22..15b996b 100644
--- a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/internal/TestBaseControllerImpl.java
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/controller/internal/TestBaseControllerImpl.java
@@ -118,10 +118,10 @@ public void should_be_able_to_send_and_receive_c2c_events() throws Exception {
Controller1 c1 = new Controller1();
graph.inject(c1);
- c1.init();
+ c1.onConstruct();
Controller2 c2 = new Controller2();
graph.inject(c2);
- c2.init();
+ c2.onConstruct();
c2.proxy = proxy;
Event myEvent = new Event(this);
@@ -151,7 +151,7 @@ public void should_log_exception_when_posting_c2c_events_to_null_eventBus() thro
TestController controller = new TestController();
graph.inject(controller);
- controller.init();
+ controller.onConstruct();
controller.setLogger(loggerMock);
controller.eventBusC2C = null;
@@ -171,7 +171,7 @@ public void should_log_exception_when_posting_c2v_events_to_null_eventBus() thro
TestController controller = new TestController();
graph.inject(controller);
- controller.init();
+ controller.onConstruct();
controller.setLogger(loggerMock);
controller.mEventBusC2V = null;
@@ -199,6 +199,6 @@ protected Class getModelClassType() {
public void should_throw_out_runtime_exception_when_unable_to_create_model_instance() throws Exception {
BadController controller = new BadController();
graph.inject(controller);
- controller.init();
+ controller.onConstruct();
}
}
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/TestControllerSimpleInject.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/TestControllerSimpleInject.java
index d9d17ff..90bd1f5 100644
--- a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/TestControllerSimpleInject.java
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/TestControllerSimpleInject.java
@@ -123,9 +123,9 @@ public void testOnDisposeShouldBeCalledWhenControllerReleased() throws Exception
graph.register(new LifeCycleTestControllerComponent(lifeCycleProxy));
TestLifCycleView testView = new TestLifCycleView();
- verify(lifeCycleProxy, times(0)).initCalled();
+ verify(lifeCycleProxy, times(0)).onConstructCalled();
graph.inject(testView);
- verify(lifeCycleProxy, times(1)).initCalled();
+ verify(lifeCycleProxy, times(1)).onConstructCalled();
verify(lifeCycleProxy, times(0)).disposeCalled();
graph.release(testView);
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/LifeCycleTestController.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/LifeCycleTestController.java
index db1a782..dd20ceb 100644
--- a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/LifeCycleTestController.java
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/LifeCycleTestController.java
@@ -20,7 +20,7 @@
public interface LifeCycleTestController extends BaseController {
interface Proxy {
- void initCalled();
+ void onConstructCalled();
void disposeCalled();
}
}
diff --git a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/internal/LifeCycleTestControllerImpl.java b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/internal/LifeCycleTestControllerImpl.java
index b1ed954..3c2f94a 100644
--- a/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/internal/LifeCycleTestControllerImpl.java
+++ b/library/android-mvc-controller/src/test/java/com/shipdream/lib/android/mvc/inject/testNameMapping/controller/internal/LifeCycleTestControllerImpl.java
@@ -31,9 +31,9 @@ public Class getModelClassType() {
}
@Override
- public void onInitialized() {
- super.onInitialized();
- proxy.initCalled();
+ public void onConstruct() {
+ super.onConstruct();
+ proxy.onConstructCalled();
}
@Override
diff --git a/library/android-mvc-test/build.gradle b/library/android-mvc-test/build.gradle
index f7215b1..fdabf7a 100644
--- a/library/android-mvc-test/build.gradle
+++ b/library/android-mvc-test/build.gradle
@@ -23,9 +23,9 @@ dependencies {
compile rootProject.lib.logbackAndroidClassic
// Testing-only dependencies
- androidTestCompile 'com.android.support.test:runner:0.3'
- androidTestCompile 'com.android.support.test:rules:0.3'
- androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
+ androidTestCompile 'com.android.support.test:runner:0.4'
+ androidTestCompile 'com.android.support.test:rules:0.4'
+ androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
androidTestCompile "com.android.support:support-annotations:$rootProject.supportLibVersion"
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
}
diff --git a/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/eventv2v/TestV2VEvents.java b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/eventv2v/TestV2VEvents.java
new file mode 100644
index 0000000..43d81fd
--- /dev/null
+++ b/library/android-mvc-test/src/androidTest/java/com/shipdream/lib/android/mvc/view/eventv2v/TestV2VEvents.java
@@ -0,0 +1,73 @@
+/*
+ * 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.eventv2v;
+
+import com.shipdream.lib.android.mvc.view.BaseTestCase;
+import com.shipdream.lib.android.mvc.view.eventv2v.controller.V2VTestController;
+import com.shipdream.lib.android.mvc.view.test.R;
+
+import org.junit.Test;
+
+import javax.inject.Inject;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+public class TestV2VEvents extends BaseTestCase {
+ @Inject
+ private V2VTestController v2VTestController;
+
+ public TestV2VEvents() {
+ super(EventBusV2VActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ waitTest();
+ }
+
+ @Test
+ public void should_be_able_to_send_and_receive_v2v_events_among_fragments_services_and_dialogFragments() throws Throwable {
+ onView(withId(R.id.fragment_mvc_v2v_text)).check(matches(withText("Initial Text")));
+
+ onView(withId(R.id.fragment_mvc_v2v_btnService)).perform(click());
+
+ onView(withId(R.id.fragment_mvc_v2v_text)).check(matches(withText("Updated By Service")));
+
+ onView(withId(R.id.fragment_mvc_v2v_btnDialog)).perform(click());
+
+ onView(withId(R.id.fragment_mvc_v2v_dialog_text)).check(matches(withText("Initial Dialog Text")));
+
+ v2VTestController.updateDialogButton(this, "Updated By Under Fragment via V2V event");
+
+ onView(withId(R.id.fragment_mvc_v2v_dialog_text)).check(matches(withText("Updated By Under Fragment via V2V event")));
+
+ onView(withId(R.id.fragment_mvc_v2v_dialog_button)).perform(click());
+
+ onView(withId(R.id.fragment_mvc_v2v_text)).check(matches(withText("Dialog Closed")));
+ }
+
+}
diff --git a/library/android-mvc-test/src/main/AndroidManifest.xml b/library/android-mvc-test/src/main/AndroidManifest.xml
index a52de66..ae10a4d 100644
--- a/library/android-mvc-test/src/main/AndroidManifest.xml
+++ b/library/android-mvc-test/src/main/AndroidManifest.xml
@@ -45,6 +45,12 @@
+
+
+
+
+
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VActivity.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VActivity.java
new file mode 100644
index 0000000..5f75203
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VActivity.java
@@ -0,0 +1,63 @@
+/*
+ * 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.eventv2v;
+
+import android.os.Bundle;
+import android.view.View;
+
+import com.shipdream.lib.android.mvc.view.MvcActivity;
+import com.shipdream.lib.android.mvc.view.MvcFragment;
+
+public class EventBusV2VActivity extends MvcActivity {
+
+ @Override
+ protected Class extends MvcFragment> mapNavigationFragment(String locationId) {
+ return EventBusV2VFragment.class;
+ }
+
+ @Override
+ protected Class extends DelegateFragment> getDelegateFragmentClass() {
+ return HomeFragment.class;
+ }
+
+ public static class HomeFragment extends DelegateFragment {
+ private boolean onViewStateRestoredCalled = false;
+
+ @Override
+ protected void onStartUp() {
+ getNavigationController().navigateTo(this, "TestFragment", null);
+ }
+
+ @Override
+ public void onViewReady(View view, Bundle savedInstanceState, Reason reason) {
+ super.onViewReady(view, savedInstanceState, reason);
+
+ if (reason.isRestored()) {
+ if (!onViewStateRestoredCalled) {
+ throw new IllegalStateException("When activity is restoring, onViewReady must be called after onViewStateRestored to guarantee all state of this fragment is ready to use.");
+ }
+ }
+ }
+
+ @Override
+ public void onViewStateRestored(Bundle savedInstanceState) {
+ onViewStateRestoredCalled = true;
+ super.onViewStateRestored(savedInstanceState);
+ }
+ }
+
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VDialogFragment.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VDialogFragment.java
new file mode 100644
index 0000000..ffe20e9
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VDialogFragment.java
@@ -0,0 +1,50 @@
+/*
+ * 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.eventv2v;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.shipdream.lib.android.mvc.view.MvcDialogFragment;
+import com.shipdream.lib.android.mvc.view.test.R;
+
+public class EventBusV2VDialogFragment extends MvcDialogFragment {
+ private TextView textView;
+ private View button;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_mvc_v2v_dialog, container);
+ textView = (TextView) view.findViewById(R.id.fragment_mvc_v2v_dialog_text);
+ button = view.findViewById(R.id.fragment_mvc_v2v_dialog_button);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ postEventV2V(new Events.OnFragmentTextChanged(v, "Dialog Closed"));
+ dismiss();
+ }
+ });
+ return view;
+ }
+
+ private void onEvent(Events.OnDialogButtonChanged onButtonUpdated) {
+ textView.setText(onButtonUpdated.getText());
+ }
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VFragment.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VFragment.java
new file mode 100644
index 0000000..10a6bad
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VFragment.java
@@ -0,0 +1,71 @@
+/*
+ * 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.eventv2v;
+
+import android.content.Intent;
+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.eventv2v.controller.V2VTestController;
+import com.shipdream.lib.android.mvc.view.test.R;
+
+public class EventBusV2VFragment extends MvcFragment {
+ private TextView textView;
+ private View buttonDialog;
+ private View buttonService;
+
+ @Override
+ protected int getLayoutResId() {
+ return R.layout.fragment_mvc_v2v;
+ }
+
+ @Override
+ public void onViewReady(View view, Bundle savedInstanceState, Reason reason) {
+ super.onViewReady(view, savedInstanceState, reason);
+
+ textView = (TextView) view.findViewById(R.id.fragment_mvc_v2v_text);
+
+ buttonDialog = view.findViewById(R.id.fragment_mvc_v2v_btnDialog);
+ buttonDialog.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final EventBusV2VDialogFragment cityDialog = new EventBusV2VDialogFragment();
+ cityDialog.show(getFragmentManager(), "EventBusV2VDialogFragment");
+ }
+ });
+
+ buttonService = view.findViewById(R.id.fragment_mvc_v2v_btnService);
+ buttonService.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(getActivity(), EventBusV2VService.class);
+ getActivity().startService(intent);
+ }
+ });
+ }
+
+ private void onEvent(Events.OnFragmentTextChanged event) {
+ textView.setText(event.getText());
+ }
+
+ private void onEvent(V2VTestController.EventC2V.OnButtonUpdated onButtonUpdated) {
+ postEventV2V(new Events.OnDialogButtonChanged(onButtonUpdated.getSender(), onButtonUpdated.getText()));
+ }
+
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VService.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VService.java
new file mode 100644
index 0000000..e8606fd
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/EventBusV2VService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.eventv2v;
+
+import android.content.Intent;
+import android.os.IBinder;
+
+import com.shipdream.lib.android.mvc.view.MvcService;
+
+public class EventBusV2VService extends MvcService {
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ super.onStartCommand(intent, flags, startId);
+ postEventV2V(new Events.OnFragmentTextChanged(this, "Updated By Service"));
+ return START_STICKY;
+ }
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/Events.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/Events.java
new file mode 100644
index 0000000..7bee1de
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/Events.java
@@ -0,0 +1,30 @@
+package com.shipdream.lib.android.mvc.view.eventv2v;
+
+import com.shipdream.lib.android.mvc.event.BaseEventV2V;
+
+public interface Events {
+ abstract class OnTextChanged extends BaseEventV2V {
+ private final String text;
+ protected OnTextChanged(Object sender, String text) {
+ super(sender);
+ this.text = text;
+ }
+
+ public String getText() {
+ return text;
+ }
+ }
+
+ class OnFragmentTextChanged extends OnTextChanged {
+ protected OnFragmentTextChanged(Object sender, String text) {
+ super(sender, text);
+ }
+ }
+
+ class OnDialogButtonChanged extends OnTextChanged {
+ protected OnDialogButtonChanged(Object sender, String text) {
+ super(sender, text);
+ }
+ }
+
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/controller/V2VTestController.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/controller/V2VTestController.java
new file mode 100644
index 0000000..bb2cdb5
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/controller/V2VTestController.java
@@ -0,0 +1,23 @@
+package com.shipdream.lib.android.mvc.view.eventv2v.controller;
+
+import com.shipdream.lib.android.mvc.controller.BaseController;
+import com.shipdream.lib.android.mvc.event.BaseEventC2V;
+
+public interface V2VTestController extends BaseController {
+ void updateDialogButton(Object sender, String text);
+
+ interface EventC2V {
+ class OnButtonUpdated extends BaseEventC2V {
+ private final String text;
+
+ public OnButtonUpdated(Object sender, String text) {
+ super(sender);
+ this.text = text;
+ }
+
+ public String getText() {
+ return text;
+ }
+ }
+ }
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/controller/internal/V2VTestControllerImpl.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/controller/internal/V2VTestControllerImpl.java
new file mode 100644
index 0000000..21d4719
--- /dev/null
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/eventv2v/controller/internal/V2VTestControllerImpl.java
@@ -0,0 +1,16 @@
+package com.shipdream.lib.android.mvc.view.eventv2v.controller.internal;
+
+import com.shipdream.lib.android.mvc.controller.internal.BaseControllerImpl;
+import com.shipdream.lib.android.mvc.view.eventv2v.controller.V2VTestController;
+
+public class V2VTestControllerImpl extends BaseControllerImpl implements V2VTestController{
+ @Override
+ protected Class getModelClassType() {
+ return null;
+ }
+
+ @Override
+ public void updateDialogButton(Object sender, String text) {
+ postC2VEvent(new EventC2V.OnButtonUpdated(sender, text));
+ }
+}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerAImpl.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerAImpl.java
index 63d2bad..1e82fdb 100644
--- a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerAImpl.java
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerAImpl.java
@@ -30,8 +30,8 @@ public Class getModelClassType() {
}
@Override
- protected void onInitialized() {
- super.onInitialized();
+ public void onConstruct() {
+ super.onConstruct();
getModel().setTags(new ArrayList());
}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerBImpl.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerBImpl.java
index 9c048c2..ecc429a 100644
--- a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerBImpl.java
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerBImpl.java
@@ -30,8 +30,8 @@ public Class getModelClassType() {
}
@Override
- protected void onInitialized() {
- super.onInitialized();
+ public void onConstruct() {
+ super.onConstruct();
getModel().setTags(new ArrayList());
}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerCImpl.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerCImpl.java
index b1ba546..9306ccd 100644
--- a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerCImpl.java
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/injection/controller/internal/ControllerCImpl.java
@@ -30,8 +30,8 @@ public Class getModelClassType() {
}
@Override
- protected void onInitialized() {
- super.onInitialized();
+ public void onConstruct() {
+ super.onConstruct();
getModel().setTags(new ArrayList());
}
diff --git a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/viewpager/ViewPagerHomeFragment.java b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/viewpager/ViewPagerHomeFragment.java
index 28d8791..89f2051 100644
--- a/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/viewpager/ViewPagerHomeFragment.java
+++ b/library/android-mvc-test/src/main/java/com/shipdream/lib/android/mvc/view/viewpager/ViewPagerHomeFragment.java
@@ -3,7 +3,7 @@
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
@@ -86,7 +86,7 @@ public void onDestroy() {
super.onDestroy();
}
- private class PagerAdapter extends FragmentStatePagerAdapter {
+ private class PagerAdapter extends FragmentPagerAdapter {
private Class extends MvcFragment>[] tabs = new Class[]{
TabFragmentA.class,
TabFragmentB.class,
diff --git a/library/android-mvc-test/src/main/res/layout/fragment_mvc_v2v.xml b/library/android-mvc-test/src/main/res/layout/fragment_mvc_v2v.xml
new file mode 100644
index 0000000..ed1dc31
--- /dev/null
+++ b/library/android-mvc-test/src/main/res/layout/fragment_mvc_v2v.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library/android-mvc-test/src/main/res/layout/fragment_mvc_v2v_dialog.xml b/library/android-mvc-test/src/main/res/layout/fragment_mvc_v2v_dialog.xml
new file mode 100644
index 0000000..ea9f274
--- /dev/null
+++ b/library/android-mvc-test/src/main/res/layout/fragment_mvc_v2v_dialog.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library/android-mvc/build.gradle b/library/android-mvc/build.gradle
index 283c593..4f5bf01 100644
--- a/library/android-mvc/build.gradle
+++ b/library/android-mvc/build.gradle
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+plugins {
+ id "com.jfrog.bintray" version "1.4"
+}
+
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
-apply plugin: 'com.jfrog.bintray'
dependencies {
compile project(':library:android-mvc-controller')
diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/AndroidMvc.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/AndroidMvc.java
index db1033e..d7d15b7 100644
--- a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/AndroidMvc.java
+++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/AndroidMvc.java
@@ -16,172 +16,32 @@
package com.shipdream.lib.android.mvc.view;
-import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonSyntaxException;
+import com.shipdream.lib.android.mvc.Injector;
import com.shipdream.lib.android.mvc.MvcGraph;
-import com.shipdream.lib.android.mvc.StateKeeper;
-import com.shipdream.lib.android.mvc.StateManaged;
-import com.shipdream.lib.android.mvc.controller.BaseController;
-import com.shipdream.lib.android.mvc.controller.NavigationController;
import com.shipdream.lib.android.mvc.controller.internal.AndroidPosterImpl;
-import com.shipdream.lib.android.mvc.event.BaseEventV2V;
import com.shipdream.lib.android.mvc.event.bus.EventBus;
-import com.shipdream.lib.android.mvc.event.bus.annotation.EventBusC2V;
import com.shipdream.lib.android.mvc.event.bus.internal.EventBusImpl;
-import com.shipdream.lib.poke.exception.PokeException;
+import com.shipdream.lib.poke.Component;
+import com.shipdream.lib.poke.Provides;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.lang.reflect.Field;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
-import javax.inject.Inject;
+import javax.inject.Singleton;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
/**
* {@link AndroidMvc} will generate a default {@link MvcGraph} for injection. To replace
- * {@link MvcGraph.BaseDependencies} use {@link #configGraph(MvcGraph.BaseDependencies)}.
+ * {@link MvcGraph.BaseDependencies} use {@link Injector#configGraph(MvcGraph.BaseDependencies)}.
* By default, the graph uses naming convention to locate the implementations of dependencies. See
* {@link MvcGraph} how it works.
*/
public class AndroidMvc {
- private static class EventBusC2VHolder {
- @Inject
- @EventBusC2V
- EventBus eventBusC2V;
- }
-
- static final String MVC_SATE_PREFIX = "__--AndroidMvc:State:";
- static final String FRAGMENT_TAG_PREFIX = "__--AndroidMvc:Fragment:";
- private static MvcGraph mvcGraph;
- private static EventBusC2VHolder eventBusC2VHolder;
- private static EventBus eventBusV2V;
- private static DefaultStateKeeper sStateManager;
-
- static {
- configGraph(new DefaultControllerDependencies());
- sStateManager = new DefaultStateKeeper();
- eventBusV2V = new EventBusImpl();
-
- AndroidPosterImpl.init();
- }
-
- private AndroidMvc() {
- }
-
- /**
- * The graph to inject dependencies for mvc components.
- * @return The {@link MvcGraph}
- */
- public static MvcGraph graph() {
- return mvcGraph;
- }
-
- /**
- * Config the {@link MvcGraph} by custom dependencies.
- *
Note that, the graph will be regenerated after config. In addition, it's cached instances
- * will be regenerated such as cached singletons.
- *
- * @param baseDependencies The dependencies of all controllers
- */
- public static void configGraph(MvcGraph.BaseDependencies baseDependencies) {
- try {
- mvcGraph = new MvcGraph(baseDependencies);
- eventBusC2VHolder = new EventBusC2VHolder();
- mvcGraph.inject(eventBusC2VHolder);
- } catch (PokeException e) {
- throw new RuntimeException(e);
- }
- }
-
- static void saveStateOfAllControllers(Bundle outState) {
- sStateManager.bundle = outState;
- mvcGraph.saveAllStates(sStateManager);
- sStateManager.bundle = null;
- }
-
- static void restoreStateOfAllControllers(Bundle savedState) {
- sStateManager.bundle = savedState;
- mvcGraph.restoreAllStates(sStateManager);
- sStateManager.bundle = null;
- }
-
- static void saveControllerStateOfTheirOwn(Bundle outState, Object object) {
- Field[] fields = object.getClass().getDeclaredFields();
- sStateManager.bundle = outState;
- for (int i = 0; i < fields.length; i++) {
- Field field = fields[i];
- if (BaseController.class.isAssignableFrom(field.getType())) {
- StateManaged stateManaged = null;
- try {
- field.setAccessible(true);
- stateManaged = (StateManaged) field.get(object);
- } catch (IllegalAccessException e) {
- //ignore
- }
- sStateManager.saveState(stateManaged.getState(), stateManaged.getStateType());
- }
- }
- }
-
- static void restoreControllerStateByTheirOwn(Bundle savedState, Object object) {
- Field[] fields = object.getClass().getDeclaredFields();
- sStateManager.bundle = savedState;
- for (int i = 0; i < fields.length; i++) {
- Field field = fields[i];
- if (BaseController.class.isAssignableFrom(field.getType())) {
- try {
- field.setAccessible(true);
- StateManaged stateManaged = (StateManaged) field.get(object);
- Object value = sStateManager.getState(stateManaged.getStateType());
- stateManaged.restoreState(value);
- } catch (IllegalAccessException e) {
- //ignore
- }
- }
- }
- }
-
- /**
- * Gets Controllers to Views event bus. Internal use by AndroidMvc library only.
- * @return The event bus
- */
- static EventBus getEventBusC2V() {
- return eventBusC2VHolder.eventBusC2V;
- }
-
- /**
- * Gets the views to views event bus. By default all {@link MvcFragment}s,
- * {@link MvcDialogFragment} and {@link MvcService} have registered to this event bus. They
- * also have short cut methods to post {@link BaseEventV2V}. Custom views which need to use the
- * event bus must register to this event respectively..
- * @return The event bus
- */
- public static EventBus getEventBusV2V() {
- return eventBusV2V;
- }
-
- /**
- * Set the custom state keeper for the objects that are wanted to be saved and restored by it.
- * Other objects will still be saved and restored by json serialization.
- * @param customStateKeeper The State keeper use {@link Parcelable} to save and restore state.
- * If any state doesn't need to be managed by this state keeper return
- * null in its {@link AndroidStateKeeper#saveState(Object, Class)} and
- * {@link AndroidStateKeeper#getState(Parcelable, Class)}
- */
- public static void setCustomStateKeeper(AndroidStateKeeper customStateKeeper) {
- sStateManager.customStateKeeper = customStateKeeper;
- }
-
private static class DefaultControllerDependencies extends MvcGraph.BaseDependencies {
private static ExecutorService sNetworkExecutorService;
private final static String BACKGROUND_THREAD_NAME = "AndroidMvcDefaultBackgroundThread";
@@ -206,117 +66,43 @@ public void run() {
}
}
- private static class DefaultStateKeeper implements StateKeeper {
- private static Gson gson;
- private Bundle bundle;
- private Logger logger = LoggerFactory.getLogger(getClass());
- private AndroidStateKeeper navigationModelKeeper = new NavigationModelKeeper();
- private AndroidStateKeeper customStateKeeper;
-
- private DefaultStateKeeper() {
- gson = new GsonBuilder().create();
- }
-
- private static String getStateKey(String stateTypeName) {
- return MVC_SATE_PREFIX + stateTypeName;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public void saveState(T state, Class type) {
- if (type != null) {
- Parcelable parcelable = null;
-
- if (NavigationController.Model.class == type) {
- //Use navigation model keeper to save state
- parcelable = navigationModelKeeper.saveState(state, type);
- } else {
- if (customStateKeeper != null) {
- //Use customs state manager to restore state
- parcelable = customStateKeeper.saveState(state, type);
- }
- }
-
- long ts = System.currentTimeMillis();
- if (parcelable != null) {
- String stateKey = getStateKey(type.getName());
- bundle.putParcelable(stateKey, parcelable);
- logger.trace("Save state by parcel state keeper - {}, {}ms used.",
- type.getName(), System.currentTimeMillis() - ts);
- } else {
- //Use Gson to restore state
- String stateKey = getStateKey(type.getName());
- String json = gson.toJson(state);
- bundle.putString(stateKey, json);
-
- logger.trace("Save state by JSON - {}, {}ms used. Content: {}",
- type.getName(), System.currentTimeMillis() - ts, json);
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public T getState(Class type) {
- T state = null;
- if (type != null) {
- long ts = System.currentTimeMillis();
- String stateKey = getStateKey(type.getName());
- Object value = bundle.get(stateKey);
- Parcelable parcelable = null;
- if (value instanceof Parcelable) {
- parcelable = (Parcelable) value;
- }
- if (NavigationController.Model.class == type) {
- //Use navigation model keeper to restore state
- state = (T) navigationModelKeeper.getState(parcelable, type);
- logger.trace("Restore state by parcel state keeper - {}, {}ms used.",
- type.getName(), System.currentTimeMillis() - ts);
- } else {
- //Use custom state keeper or gson state keeper to restore state.
- if (customStateKeeper != null) {
- if (parcelable != null) {
- //Use custom state manager to restore state
- state = (T) customStateKeeper.getState(parcelable, type);
- logger.trace("Restore state by parcel state keeper - {}, {}ms used.",
- type.getName(), System.currentTimeMillis() - ts);
- }
- }
- if (state == null) {
- //State is not restored successfully by custom state keeper nor navigation
- //model keeper. So try to use Gson to restore state
- state = deserialize(bundle, type);
- }
- }
-
- if (state == null) {
- throw new IllegalStateException("Can't find restore state for " + type.getName());
- }
- }
- return state;
+ static class ViewComponent extends Component {
+ @Provides
+ @EventBusV2V
+ @Singleton
+ public EventBus providesIEventBusC2V() {
+ return new EventBusImpl();
}
+ }
- private T deserialize(Bundle outState, Class type) {
- T state;
- long ts = System.currentTimeMillis();
+ static {
+ Injector.configGraph(new DefaultControllerDependencies());
+ Injector.getGraph().register(new ViewComponent());
- String stateKey = getStateKey(type.getName());
- String json = outState.getString(stateKey);
- try {
- //recover the model
- state = gson.fromJson(json, type);
- //rebind the model to the controller
- } catch (JsonSyntaxException exception) {
- String errorMessage = String.format(
- "Failed to restore state(%s) by json deserialization", type.getName());
- throw new RuntimeException(errorMessage, exception);
- }
+ AndroidPosterImpl.init();
+ }
- logger.trace("Restore state by JSON - {}, {}ms used.",
- type.getName(), System.currentTimeMillis() - ts);
+ private AndroidMvc() {
+ }
- return state;
- }
+ /**
+ * The graph to inject dependencies for mvc components.
+ * @return The {@link MvcGraph}
+ */
+ public static MvcGraph graph() {
+ return Injector.getGraph();
+ }
+ /**
+ * Set the custom state keeper for the objects that are wanted to be saved and restored by it.
+ * Other objects will still be saved and restored by json serialization.
+ * @param customStateKeeper The State keeper use {@link Parcelable} to save and restore state.
+ * If any state doesn't need to be managed by this state keeper return
+ * null in its {@link AndroidStateKeeper#saveState(Object, Class)} and
+ * {@link AndroidStateKeeper#getState(Parcelable, Class)}
+ */
+ public static void setCustomStateKeeper(AndroidStateKeeper customStateKeeper) {
+ DefaultStateKeeperHolder.stateKeeper.customStateKeeper = customStateKeeper;
}
+
}
diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/DefaultStateKeeper.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/DefaultStateKeeper.java
new file mode 100644
index 0000000..59a6606
--- /dev/null
+++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/DefaultStateKeeper.java
@@ -0,0 +1,129 @@
+package com.shipdream.lib.android.mvc.view;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonSyntaxException;
+import com.shipdream.lib.android.mvc.StateKeeper;
+import com.shipdream.lib.android.mvc.controller.NavigationController;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class DefaultStateKeeper implements StateKeeper {
+ static final String MVC_SATE_PREFIX = "__--AndroidMvc:State:";
+
+ private static Gson gson;
+ private Logger logger = LoggerFactory.getLogger(getClass());
+ private AndroidStateKeeper navigationModelKeeper = new NavigationModelKeeper();
+ AndroidStateKeeper customStateKeeper;
+ Bundle bundle;
+
+ DefaultStateKeeper() {
+ gson = new GsonBuilder().create();
+ }
+
+ private static String getStateKey(String stateTypeName) {
+ return MVC_SATE_PREFIX + stateTypeName;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void saveState(T state, Class type) {
+ if (type != null) {
+ Parcelable parcelable = null;
+
+ if (NavigationController.Model.class == type) {
+ //Use navigation model keeper to save state
+ parcelable = navigationModelKeeper.saveState(state, type);
+ } else {
+ if (customStateKeeper != null) {
+ //Use customs state manager to restore state
+ parcelable = customStateKeeper.saveState(state, type);
+ }
+ }
+
+ long ts = System.currentTimeMillis();
+ if (parcelable != null) {
+ String stateKey = getStateKey(type.getName());
+ bundle.putParcelable(stateKey, parcelable);
+ logger.trace("Save state by parcel state keeper - {}, {}ms used.",
+ type.getName(), System.currentTimeMillis() - ts);
+ } else {
+ //Use Gson to restore state
+ String stateKey = getStateKey(type.getName());
+ String json = gson.toJson(state);
+ bundle.putString(stateKey, json);
+
+ logger.trace("Save state by JSON - {}, {}ms used. Content: {}",
+ type.getName(), System.currentTimeMillis() - ts, json);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T getState(Class type) {
+ T state = null;
+ if (type != null) {
+ long ts = System.currentTimeMillis();
+ String stateKey = getStateKey(type.getName());
+ Object value = bundle.get(stateKey);
+ Parcelable parcelable = null;
+ if (value instanceof Parcelable) {
+ parcelable = (Parcelable) value;
+ }
+ if (NavigationController.Model.class == type) {
+ //Use navigation model keeper to restore state
+ state = (T) navigationModelKeeper.getState(parcelable, type);
+ logger.trace("Restore state by parcel state keeper - {}, {}ms used.",
+ type.getName(), System.currentTimeMillis() - ts);
+ } else {
+ //Use custom state keeper or gson state keeper to restore state.
+ if (customStateKeeper != null) {
+ if (parcelable != null) {
+ //Use custom state manager to restore state
+ state = (T) customStateKeeper.getState(parcelable, type);
+ logger.trace("Restore state by parcel state keeper - {}, {}ms used.",
+ type.getName(), System.currentTimeMillis() - ts);
+ }
+ }
+ if (state == null) {
+ //State is not restored successfully by custom state keeper nor navigation
+ //model keeper. So try to use Gson to restore state
+ state = deserialize(bundle, type);
+ }
+ }
+
+ if (state == null) {
+ throw new IllegalStateException("Can't find restore state for " + type.getName());
+ }
+ }
+ return state;
+ }
+
+ private T deserialize(Bundle outState, Class type) {
+ T state;
+ long ts = System.currentTimeMillis();
+
+ String stateKey = getStateKey(type.getName());
+ String json = outState.getString(stateKey);
+ try {
+ //recover the model
+ state = gson.fromJson(json, type);
+ //rebind the model to the controller
+ } catch (JsonSyntaxException exception) {
+ String errorMessage = String.format(
+ "Failed to restore state(%s) by json deserialization", type.getName());
+ throw new RuntimeException(errorMessage, exception);
+ }
+
+ logger.trace("Restore state by JSON - {}, {}ms used.",
+ type.getName(), System.currentTimeMillis() - ts);
+
+ return state;
+ }
+}
\ No newline at end of file
diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/DefaultStateKeeperHolder.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/DefaultStateKeeperHolder.java
new file mode 100644
index 0000000..e239e29
--- /dev/null
+++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/DefaultStateKeeperHolder.java
@@ -0,0 +1,68 @@
+package com.shipdream.lib.android.mvc.view;
+
+import android.os.Bundle;
+
+import com.shipdream.lib.android.mvc.Injector;
+import com.shipdream.lib.android.mvc.StateManaged;
+import com.shipdream.lib.android.mvc.controller.BaseController;
+
+import java.lang.reflect.Field;
+
+/**
+ * This class holds a stateKeeper as a singleton.
+ */
+class DefaultStateKeeperHolder {
+ static DefaultStateKeeper stateKeeper;
+
+ static {
+ stateKeeper = new DefaultStateKeeper();
+ }
+
+ static void saveStateOfAllControllers(Bundle outState) {
+ stateKeeper.bundle = outState;
+ Injector.getGraph().saveAllStates(stateKeeper);
+ stateKeeper.bundle = null;
+ }
+
+ static void restoreStateOfAllControllers(Bundle savedState) {
+ stateKeeper.bundle = savedState;
+ Injector.getGraph().restoreAllStates(stateKeeper);
+ stateKeeper.bundle = null;
+ }
+
+ static void saveControllerStateOfTheirOwn(Bundle outState, Object object) {
+ Field[] fields = object.getClass().getDeclaredFields();
+ stateKeeper.bundle = outState;
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ if (BaseController.class.isAssignableFrom(field.getType())) {
+ StateManaged stateManaged = null;
+ try {
+ field.setAccessible(true);
+ stateManaged = (StateManaged) field.get(object);
+ } catch (IllegalAccessException e) {
+ //ignore
+ }
+ stateKeeper.saveState(stateManaged.getState(), stateManaged.getStateType());
+ }
+ }
+ }
+
+ static void restoreControllerStateByTheirOwn(Bundle savedState, Object object) {
+ Field[] fields = object.getClass().getDeclaredFields();
+ stateKeeper.bundle = savedState;
+ for (int i = 0; i < fields.length; i++) {
+ Field field = fields[i];
+ if (BaseController.class.isAssignableFrom(field.getType())) {
+ try {
+ field.setAccessible(true);
+ StateManaged stateManaged = (StateManaged) field.get(object);
+ Object value = stateKeeper.getState(stateManaged.getStateType());
+ stateManaged.restoreState(value);
+ } catch (IllegalAccessException e) {
+ //ignore
+ }
+ }
+ }
+ }
+}
diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/EventBusV2V.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/EventBusV2V.java
new file mode 100644
index 0000000..d3bcb55
--- /dev/null
+++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/EventBusV2V.java
@@ -0,0 +1,17 @@
+package com.shipdream.lib.android.mvc.view;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * Indicates the annotated event bus is for communication among views. Events through the
+ * event bus annotated by this annotation should be posted and received on UI thread.
+ */
+@Qualifier
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EventBusV2V {
+}
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 a49f065..3407c8d 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
@@ -16,48 +16,73 @@
package com.shipdream.lib.android.mvc.view;
+import com.shipdream.lib.android.mvc.event.BaseEventV2V;
+import com.shipdream.lib.android.mvc.event.bus.EventBus;
+import com.shipdream.lib.android.mvc.event.bus.annotation.EventBusC2V;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.inject.Inject;
+
class EventRegister {
+ @Inject
+ @EventBusC2V
+ private EventBus eventBusC2V;
+
+ @Inject
+ @EventBusV2V
+ private EventBus eventBusV2V;
+
private Logger logger = LoggerFactory.getLogger(getClass());
private Object androidComponent;
private boolean eventsRegistered = false;
- public EventRegister(Object androidComponent) {
+ EventRegister(Object androidComponent) {
this.androidComponent = androidComponent;
}
/**
- * Register c2v and v2v event buses. This method should be called on view's onCreate life cycle callback.
+ * Register c2v and v2v event buses.
*/
- public void registerEventBuses() {
+ void registerEventBuses() {
if (!eventsRegistered) {
- AndroidMvc.getEventBusC2V().register(androidComponent);
- AndroidMvc.getEventBusV2V().register(androidComponent);
+ eventBusC2V.register(androidComponent);
+ eventBusV2V.register(androidComponent);
eventsRegistered = true;
- logger.trace("+Event bus registered for view - '{}'.",
+ logger.debug("+Event bus registered for view - '{}'.",
androidComponent.getClass().getSimpleName());
} else {
- logger.trace("!Event bus already registered for view - '{}' and its controllers.",
+ logger.debug("!Event bus already registered for view - '{}' and its controllers.",
androidComponent.getClass().getSimpleName());
}
}
/**
- * Unregister c2v and v2v event buses. This method should be called on view's onDestroy life cycle callback.
+ * Unregister c2v and v2v event buses.
*/
- public void unregisterEventBuses() {
+ void unregisterEventBuses() {
if (eventsRegistered) {
- AndroidMvc.getEventBusC2V().unregister(androidComponent);
- AndroidMvc.getEventBusV2V().unregister(androidComponent);
+ eventBusC2V.unregister(androidComponent);
+ eventBusV2V.unregister(androidComponent);
eventsRegistered = false;
- logger.trace("-Event bus unregistered for view - '{}' and its controllers.",
+ logger.debug("-Event bus unregistered for view - '{}' and its controllers.",
androidComponent.getClass().getSimpleName());
} else {
- logger.trace("!Event bus already unregistered for view - '{}'.",
+ logger.debug("!Event bus already unregistered for view - '{}'.",
androidComponent.getClass().getSimpleName());
}
}
+ void onCreate() {
+ AndroidMvc.graph().inject(this);
+ }
+
+ void onDestroy() {
+ AndroidMvc.graph().release(this);
+ }
+
+ void postEventV2V(BaseEventV2V event) {
+ eventBusV2V.post(event);
+ }
}
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 af3b0f7..6f81e08 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
@@ -16,7 +16,7 @@
package com.shipdream.lib.android.mvc.view;
-import android.app.Activity;
+import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@@ -39,10 +39,11 @@
import javax.inject.Inject;
public abstract class MvcActivity extends AppCompatActivity {
+ private static final String FRAGMENT_TAG_PREFIX = "__--AndroidMvc:Fragment:";
private DelegateFragment delegateFragment;
String getDelegateFragmentTag() {
- return AndroidMvc.FRAGMENT_TAG_PREFIX + getDelegateFragmentClass().getName();
+ return FRAGMENT_TAG_PREFIX + getDelegateFragmentClass().getName();
}
@SuppressWarnings("unchecked")
@@ -109,7 +110,7 @@ void addPendingOnViewReadyActions(Runnable runnable) {
* injected into any fragments extending {@link MvcFragment} by fields annotated by @Inject.
*/
public static abstract class DelegateFragment extends MvcFragment {
- private static final String MVC_STATE_BUNDLE_KEY = AndroidMvc.MVC_SATE_PREFIX + "RootBundle";
+ private static final String MVC_STATE_BUNDLE_KEY = DefaultStateKeeper.MVC_SATE_PREFIX + "RootBundle";
private Logger logger = LoggerFactory.getLogger(getClass());
//Track if the state is saved and not able to commit fragment transaction
private boolean canCommitFragmentTransaction = false;
@@ -145,8 +146,8 @@ protected FragmentManager childFragmentManager() {
* FIXME: ChildFragmentManager hack - remove this method when the bug is fixed in future android support library
*/
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
+ public void onAttach(Context context) {
+ super.onAttach(context);
if (retainedChildFragmentManager != null) {
//restore the last retained child fragment manager to the new
@@ -242,7 +243,7 @@ public boolean onBackButtonPressed() {
}
private String getFragmentTag(String locationId) {
- return AndroidMvc.FRAGMENT_TAG_PREFIX + locationId;
+ return FRAGMENT_TAG_PREFIX + locationId;
}
@Override
@@ -257,7 +258,7 @@ public void onCreate(Bundle savedInstanceState) {
Bundle mvcOutState = savedInstanceState.getBundle(MVC_STATE_BUNDLE_KEY);
long ts = System.currentTimeMillis();
- AndroidMvc.restoreStateOfAllControllers(mvcOutState);
+ DefaultStateKeeperHolder.restoreStateOfAllControllers(mvcOutState);
logger.trace("Restored state of all active controllers, {}ms used.", System.currentTimeMillis() - ts);
}
}
@@ -333,7 +334,7 @@ public void onSaveInstanceState(Bundle outState) {
long ts = System.currentTimeMillis();
Bundle mvcOutState = new Bundle();
- AndroidMvc.saveStateOfAllControllers(mvcOutState);
+ DefaultStateKeeperHolder.saveStateOfAllControllers(mvcOutState);
outState.putBundle(MVC_STATE_BUNDLE_KEY, mvcOutState);
logger.trace("Save state of all active controllers, {}ms used.", System.currentTimeMillis() - ts);
}
diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcDialogFragment.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcDialogFragment.java
index 363c15b..2099100 100644
--- a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcDialogFragment.java
+++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcDialogFragment.java
@@ -19,6 +19,7 @@
import android.app.AlertDialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
+import android.view.View;
import com.shipdream.lib.android.mvc.event.BaseEventV2V;
@@ -43,6 +44,13 @@ public void onCreate(Bundle savedInstanceState) {
AndroidMvc.graph().inject(this);
eventRegister = new EventRegister(this);
+ eventRegister.onCreate();
+ eventRegister.registerEventBuses();
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
eventRegister.registerEventBuses();
}
@@ -56,21 +64,31 @@ public void onDestroyView() {
}
//============================================
super.onDestroyView();
+ eventRegister.unregisterEventBuses();
}
@Override
public void onDestroy() {
super.onDestroy();
eventRegister.unregisterEventBuses();
+ eventRegister.onDestroy();
AndroidMvc.graph().release(this);
}
/**
- * Post an event from this view to other views
- * @param event The event
+ * Post an event from this view to other views. Using EventBusV2V is a handy way to
+ * inter-communicate among views but it's a little anti pattern. Best practice is that views
+ * communicates to other views through controllers and EventBusC2V. For example, if view1 wants
+ * to talk to view2, instead of sending V2V events, view1 can send a command to a controller and
+ * that controller will fire an C2VEvent that will be received by view2. In this way, more
+ * business logic can be wrapped into controllers rather than exposed to view1.
+ *
+ *
However, it's not absolute. If touching a controller is an overkill, sending events
+ * directly through V2V channel is still an option.
+ * @param event
*/
protected void postEventV2V(BaseEventV2V event) {
- AndroidMvc.getEventBusV2V().post(event);
+ eventRegister.postEventV2V(event);
}
}
diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcFragment.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcFragment.java
index 2a75b73..e6bca32 100644
--- a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcFragment.java
+++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcFragment.java
@@ -65,26 +65,6 @@ public static class Reason {
private boolean isRotated;
private boolean isPoppedOut;
- void setNewInstance(boolean isNewInstance) {
- this.isNewInstance = isNewInstance;
- }
-
- void setFirstTime(boolean isFirstTime) {
- this.isFirstTime = isFirstTime;
- }
-
- void setRestored(boolean isRestored) {
- this.isRestored = isRestored;
- }
-
- void setRotated(boolean isRotated) {
- this.isRotated = isRotated;
- }
-
- void setPoppedOut(boolean isPoppedOut) {
- this.isPoppedOut = isPoppedOut;
- }
-
/**
* @return Indicates whether the fragment is a new instance that all its fields need to be
* reinitialized and configured. This could happen when a fragment is created for the first
@@ -156,7 +136,7 @@ public String toString() {
}
}
- private final static String STATE_LAST_ORIENTATION = AndroidMvc.MVC_SATE_PREFIX + "LastOrientation--__";
+ private final static String STATE_LAST_ORIENTATION = DefaultStateKeeper.MVC_SATE_PREFIX + "LastOrientation--__";
private EventRegister eventRegister;
private CopyOnWriteArrayList onViewReadyListeners;
private boolean fragmentComesBackFromBackground = false;
@@ -214,6 +194,9 @@ void releaseDependencies() {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ eventRegister = new EventRegister(this);
+ eventRegister.onCreate();
+
if(savedInstanceState == null) {
lastOrientation = getResources().getConfiguration().orientation;
} else {
@@ -225,8 +208,10 @@ public void onCreate(Bundle savedInstanceState) {
}
injectDependencies();
- if (savedInstanceState != null && !isStateManagedByRootDelegateFragment) {
- AndroidMvc.restoreControllerStateByTheirOwn(savedInstanceState, this);
+ if (!isStateManagedByRootDelegateFragment) {
+ if (savedInstanceState != null) {
+ DefaultStateKeeperHolder.restoreControllerStateByTheirOwn(savedInstanceState, this);
+ }
}
}
@@ -258,7 +243,6 @@ final public View onCreateView(LayoutInflater inflater, ViewGroup container, Bun
@Override
final public void onViewCreated(final View view, final Bundle savedInstanceState) {
fragmentComesBackFromBackground = false;
- eventRegister = new EventRegister(this);
eventRegister.registerEventBuses();
final boolean restoring = savedInstanceState != null;
@@ -282,24 +266,24 @@ private void doOnViewCreatedCallBack(View view, Bundle savedInstanceState, boole
if (newInstanceChecker == null) {
newInstanceChecker = new Object();
- reason.setNewInstance(true);
+ reason.isNewInstance = true;
} else {
- reason.setNewInstance(false);
+ reason.isNewInstance = false;
}
if (orientationChanged) {
- reason.setRotated(true);
+ reason.isRotated = true;
}
if (aboutToPopOut) {
- reason.setPoppedOut(true);
+ reason.isPoppedOut = true;
aboutToPopOut = false;
}
if (restoring) {
- reason.setRestored(true);
+ reason.isRestored = true;
} else if (!orientationChanged) {
- reason.setFirstTime(true);
+ reason.isFirstTime = true;
}
onViewReady(view, savedInstanceState, reason);
@@ -394,7 +378,6 @@ public void onPause() {
public void onDestroyView() {
super.onDestroyView();
eventRegister.unregisterEventBuses();
- eventRegister = null;
}
/**
@@ -409,6 +392,8 @@ public void onDestroyView() {
public void onDestroy() {
super.onDestroy();
releaseDependencies();
+ eventRegister.onDestroy();
+ eventRegister = null;
}
@Override
@@ -417,7 +402,7 @@ public void onSaveInstanceState(Bundle outState) {
outState.putInt(STATE_LAST_ORIENTATION, lastOrientation);
if (!isStateManagedByRootDelegateFragment) {
- AndroidMvc.saveControllerStateOfTheirOwn(outState, this);
+ DefaultStateKeeperHolder.saveControllerStateOfTheirOwn(outState, this);
}
}
@@ -471,10 +456,18 @@ public boolean onBackButtonPressed() {
}
/**
- * Post an event from this view to other views
- * @param event The view to view event
+ * Post an event from this view to other views. Using EventBusV2V is a handy way to
+ * inter-communicate among views but it's a little anti pattern. Best practice is that views
+ * communicates to other views through controllers and EventBusC2V. For example, if view1 wants
+ * to talk to view2, instead of sending V2V events, view1 can send a command to a controller and
+ * that controller will fire an C2VEvent that will be received by view2. In this way, more
+ * business logic can be wrapped into controllers rather than exposed to view1.
+ *
+ *
However, it's not absolute. If touching a controller is an overkill, sending events
+ * directly through V2V channel is still an option.
+ * @param event
*/
protected void postEventV2V(BaseEventV2V event) {
- AndroidMvc.getEventBusV2V().post(event);
+ eventRegister.postEventV2V(event);
}
}
diff --git a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcService.java b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcService.java
index 2a25485..ecc1026 100644
--- a/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcService.java
+++ b/library/android-mvc/src/main/java/com/shipdream/lib/android/mvc/view/MvcService.java
@@ -37,6 +37,7 @@ public void onCreate() {
AndroidMvc.graph().inject(this);
eventRegister = new EventRegister(this);
+ eventRegister.onCreate();
eventRegister.registerEventBuses();
}
@@ -47,14 +48,23 @@ public void onCreate() {
public void onDestroy() {
super.onDestroy();
eventRegister.unregisterEventBuses();
+ eventRegister.onDestroy();
AndroidMvc.graph().release(this);
}
/**
- * Post an event from this view to other views
+ * Post an event from this view to other views. Using EventBusV2V is a handy way to
+ * inter-communicate among views but it's a little anti pattern. Best practice is that views
+ * communicates to other views through controllers and EventBusC2V. For example, if view1 wants
+ * to talk to view2, instead of sending V2V events, view1 can send a command to a controller and
+ * that controller will fire an C2VEvent that will be received by view2. In this way, more
+ * business logic can be wrapped into controllers rather than exposed to view1.
+ *
+ *
However, it's not absolute. If touching a controller is an overkill, sending events
+ * directly through V2V channel is still an option.
* @param event
*/
protected void postEventV2V(BaseEventV2V event) {
- AndroidMvc.getEventBusC2V().post(event);
+ eventRegister.postEventV2V(event);
}
}
diff --git a/library/poke/build.gradle b/library/poke/build.gradle
index b1b26b7..32487e3 100644
--- a/library/poke/build.gradle
+++ b/library/poke/build.gradle
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+plugins {
+ id "com.jfrog.bintray" version "1.4"
+}
+
apply plugin: 'java'
apply plugin: 'maven'
-apply plugin: 'com.jfrog.bintray'
task sourceJar(type: Jar) {
from sourceSets.main.java.srcDirs
diff --git a/library/poke/src/main/java/com/shipdream/lib/poke/Consumer.java b/library/poke/src/main/java/com/shipdream/lib/poke/Consumer.java
new file mode 100644
index 0000000..3e7c4c4
--- /dev/null
+++ b/library/poke/src/main/java/com/shipdream/lib/poke/Consumer.java
@@ -0,0 +1,5 @@
+package com.shipdream.lib.poke;
+
+public abstract class Consumer {
+ public abstract void consume(T instance);
+}
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 8301617..5ddfeb5 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
@@ -34,6 +34,8 @@
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import javax.inject.Inject;
+
/**
* Abstract graph manages how to inject dependencies to target objects.
*/
@@ -151,43 +153,154 @@ public void inject(Object target, Class extends Annotation> injectAnnotation)
monitors.get(i).onInject(target);
}
}
- doInject(target, null, null, injectAnnotation, true);
+ doInject(target, null, null, injectAnnotation);
visitedInjectNodes.clear();
revisitedNode = null;
visitedFields.clear();
}
/**
- * Get an instance matching the type and qualifier. If there is an instance cached, the cached
- * instance will be returned otherwise a new instance will be created.
- *
- *
Note that, not like {@link #inject(Object, Class)} this method will NOT increment
- * reference count for the injectable object with the same type and qualifier.
- * @param type the type of the object
- * @param qualifier the qualifier of the injected object. Null is allowed if no qualifier is specified
- * @return The cached object or a new instance matching the type and qualifier
- * @throws ProviderMissingException throw if the provider matching the requiredType and qualifier is not found
- * @throws ProvideException throw when failed to create a new instance
- * @throws CircularDependenciesException throw when circular dependency found during injecting the newly created instance
+ * Same as {@link #use(Class, Annotation, Class, Consumer)} except using un-qualified injectable type.
+ * @param type The type of the injectable instance
+ * @param injectAnnotation injectAnnotation
+ * @param consumer Consume to use the instance
+ * @throws ProvideException ProvideException
+ * @throws CircularDependenciesException CircularDependenciesException
+ * @throws ProviderMissingException ProviderMissingException
+ */
+ public void use(Class type, Class extends Annotation> injectAnnotation, Consumer consumer)
+ throws ProvideException, CircularDependenciesException, ProviderMissingException {
+ use(type, null, injectAnnotation, consumer);
+ }
+
+ /**
+ * Use an injectable instance in the scope of {@link Consumer#consume(Object)} without injecting
+ * it as a field of an object. This method will automatically retain the instance before
+ * {@link Consumer#consume(Object)} is called and released after it's returned. As a result,
+ * it doesn't hold the instance like the field marked by {@link Inject} that will retain the
+ * reference of the instance until {@link #release(Object, Class)} is called. However, in the
+ * scope of {@link Consumer#consume(Object)} the instance will be held.
+ *
For example,
+ *
+ private static class Device {
+ @MyInject
+ private Os os;
+ }
+
+ final SimpleGraph graph = new SimpleGraph();
+ ScopeCache scopeCache = new ScopeCache();
+
+ graph.register(Os.class, Android.class, scopeCache);
+
+ //OsReferenceCount = 0
+ graph.use(Os.class, null, Inject.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //First time to create the instance.
+ //OsReferenceCount = 1
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+
+ Device device = new Device();
+ graph.inject(device, MyInject.class); //OsReferenceCount = 1
+ //New instance created and cached
+
+ graph.use(Os.class, null, Inject.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //Since reference count is greater than 0, cached instance will be reused
+ //OsReferenceCount = 2
+ Assert.assertTrue(device.os == instance);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 1
+
+ graph.release(device, MyInject.class); //OsReferenceCount = 0
+ //Last instance released, so next time a new instance will be created
+
+ graph.use(Os.class, null, Inject.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.os != instance);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+
+ graph.use(Os.class, null, Inject.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.os != instance);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 0
+ //Cached instance cleared again
+
+ graph.use(Os.class, null, Inject.class, new Consumer() {
+ @Override
+ public void consume(Os instance) {
+ //OsReferenceCount = 1
+ graph.inject(device, MyInject.class);
+ //Injection will reuse the cached instance and increment the reference count
+ //OsReferenceCount = 2
+
+ //Since the cached instance is cleared, the new instance is a newly created one.
+ Assert.assertTrue(device.os == instance);
+ }
+ });
+ //Reference count decremented by use method automatically
+ //OsReferenceCount = 1
+
+ graph.release(device, MyInject.class); //OsReferenceCount = 0
+ *