![JAR search and dependency download from the Maven repository](/logo.png)
com.hannesdorfmann.mosby3.mvp.MvpNullObjectBasePresenter Maven / Gradle / Ivy
package com.hannesdorfmann.mosby3.mvp;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* A {@link MvpPresenter} implmenetation that implements the null
* object pattern for the attached mvp view. So whenever the view gets detached from this
* presenter by calling{@link #detachView(boolean)}, a new "null object" view gets dynamically
* instantiated by using reflections and set as the current
* view (instead of null). The new "null object" view simply does nothing. This avoids
* NullPointerExceptions and checks like {@code if (getView() != null)}
*
*
* Please note that when creating the "null object" the first generic parameter (left depth-first
* search) that will be found that is subtype of {@link MvpView} will be used as the type of the
* view. So avoid having multiple generic parameters for "View" like this {@code
* MyPresenter} because we can't know wheter FooMvpView or OtherMvpView is
* the
* real type of this presenter's view. In that case (left depth-first search) FooMvpView will be
* used (may cause ClassCastException if OtherMvpView was the desired one)
*
*
* @param The type of the {@link MvpView}
* @author Jens Dirller , Hannes Dorfmann
* @since 1.2.0
*/
public abstract class MvpNullObjectBasePresenter implements MvpPresenter {
private WeakReference view;
private final V nullView;
private boolean viewAttachedAtLeastOnce = false;
public MvpNullObjectBasePresenter() {
try {
// Scan the inheritance hierarchy until we reached MvpNullObjectBasePresenter
Class viewClass = null;
Class> currentClass = getClass();
while (viewClass == null) {
Type genericSuperType = currentClass.getGenericSuperclass();
while (!(genericSuperType instanceof ParameterizedType)) {
// Scan inheritance tree until we find ParameterizedType which is probably a MvpSubclass
currentClass = currentClass.getSuperclass();
genericSuperType = currentClass.getGenericSuperclass();
}
Type[] types = ((ParameterizedType) genericSuperType).getActualTypeArguments();
for (int i = 0; i < types.length; i++) {
Class> genericType = (Class>) types[i];
if (genericType.isInterface() && isSubTypeOfMvpView(genericType)) {
viewClass = (Class) genericType;
break;
}
}
// Continue with next class in inheritance hierachy (see genericSuperType assignment at start of while loop)
currentClass = currentClass.getSuperclass();
}
nullView = NoOp.of(viewClass);
} catch (Throwable t) {
throw new IllegalArgumentException(
"The generic type must be the first generic type argument of class "
+ getClass().getSimpleName()
+ " (per convention). Otherwise we can't determine which type of View this"
+ " Presenter coordinates.", t);
}
}
/**
* Scans the interface inheritnace hierarchy and checks if on the root is MvpView.class
*
* @param klass The leaf interface where to begin to scan
* @return true if subtype of MvpView, otherwise false
*/
private boolean isSubTypeOfMvpView(Class> klass) {
if (klass.equals(MvpView.class)) {
return true;
}
Class[] superInterfaces = klass.getInterfaces();
for (int i = 0; i < superInterfaces.length; i++) {
if (isSubTypeOfMvpView(superInterfaces[i])) {
return true;
}
}
return false;
}
@UiThread @Override public void attachView(@NonNull V view) {
this.view = new WeakReference(view);
viewAttachedAtLeastOnce = true;
}
@UiThread @NonNull protected V getView() {
if (!viewAttachedAtLeastOnce){
throw new IllegalStateException("No view has ever been attached to this presenter!");
}
if (view != null) {
V realView = view.get();
if (realView != null) {
return realView;
}
}
return nullView;
}
@UiThread @Override public void detachView(boolean retainInstance) {
if (view != null) {
view.clear();
view = null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy