All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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