From 4d77b3637fd597ae7db8afce9bd00c957c341ee6 Mon Sep 17 00:00:00 2001 From: Hannes Dorfmann Date: Mon, 4 Sep 2017 21:02:43 +0200 Subject: [PATCH] Presenter now has destroy() and detach() method. Deprecating Presenter.detach(boolean retainInstance) #262 #271 #272 --- build.gradle | 6 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../mosby3/mvi/MviBasePresenter.java | 42 ++++--- .../backstack/first/FirstPresenter.java | 12 +- .../backstack/second/SecondPresenter.java | 13 +- .../lifecycle/LifecycleTestPresenter.java | 1 + .../mosby3/ActivityMviDelegateImpl.java | 12 +- .../mosby3/FragmentMviDelegateImpl.java | 38 +++--- .../mosby3/ViewGroupMviDelegateImpl.java | 119 +++++++++--------- .../mosby3/mvp/MvpPresenter.java | 19 ++- .../mvp/MvpNullObjectBasePresenter.java | 27 +++- .../mosby3/mvp/MvpBasePresenter.java | 28 +++-- .../mosby3/PresenterManagerTest.java | 6 + sample-mvi/build.gradle | 2 +- .../mosby3/sample/ApplicationTest.java | 13 -- .../mvp/lce/SimpleCountriesPresenter.java | 22 ++-- 17 files changed, 222 insertions(+), 144 deletions(-) delete mode 100644 sample/src/androidTest/java/com/hannesdorfmann/mosby3/sample/ApplicationTest.java diff --git a/build.gradle b/build.gradle index 9fd412dc..889ddfd2 100644 --- a/build.gradle +++ b/build.gradle @@ -9,9 +9,9 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.0.0-beta4' // classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.5.2' - classpath 'me.tatarka:gradle-retrolambda:3.5.0' + //classpath 'me.tatarka:gradle-retrolambda:3.5.0' } } @@ -40,7 +40,7 @@ allprojects { ext { minSdk = 14 targetSdk = 25 - buildToolsVersion = '26.0.0' + buildToolsVersion = '26.0.1' compileSdkVersion = 25 javaSourceCompatibility = JavaVersion.VERSION_1_7 diff --git a/gradle.properties b/gradle.properties index f4e13355..de435f6a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -31,4 +31,6 @@ POM_LICENCE_NAME=The Apache Software License, Version 2.0 POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt POM_LICENCE_DIST=repo POM_DEVELOPER_ID=hannesdorfmann -POM_DEVELOPER_NAME=Hannes Dorfmann \ No newline at end of file +POM_DEVELOPER_NAME=Hannes Dorfmann + +android.enableAapt2=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 900ae775..6f1c871f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/mvi-common/src/main/java/com/hannesdorfmann/mosby3/mvi/MviBasePresenter.java b/mvi-common/src/main/java/com/hannesdorfmann/mosby3/mvi/MviBasePresenter.java index 06ef97cc..8e4b0111 100644 --- a/mvi-common/src/main/java/com/hannesdorfmann/mosby3/mvi/MviBasePresenter.java +++ b/mvi-common/src/main/java/com/hannesdorfmann/mosby3/mvi/MviBasePresenter.java @@ -59,7 +59,8 @@ * viewState is a object (typically a POJO) that holds all the data the view needs to display * * - * By using {@link #intent(ViewIntentBinder)} and {@link #subscribeViewState(Observable, * ViewStateConsumer)} + * By using {@link #intent(ViewIntentBinder)} and {@link #subscribeViewState(Observable, * + * ViewStateConsumer)} * a relay will be established between the view and this presenter that allows the view to be * temporarily detached, without unsubscribing the underlying reactive business logic workflow and * without causing memory leaks (caused by recreation of the view). @@ -117,7 +118,8 @@ protected interface ViewIntentBinder { * This "binder" is responsible to bind the view state to the currently attached view. * This typically "renders" the view. * - * Typically this is used in {@link #bindIntents()} with {@link MviBasePresenter#subscribeViewState(Observable, * ViewStateConsumer)} + * Typically this is used in {@link #bindIntents()} with {@link MviBasePresenter#subscribeViewState(Observable, + * * ViewStateConsumer)} * like this: *

    *   Observable viewState =  ... ;
@@ -265,23 +267,11 @@ protected Observable getViewStateObservable() {
       bindIntentActually(view, intentRelayBinderPair);
     }
 
-
     viewAttachedFirstTime = false;
   }
 
-  @Override @CallSuper public void detachView(boolean retainInstance) {
-    if (!retainInstance) {
-      if (viewStateDisposable != null) {
-        // Cancel the overall observable stream
-        viewStateDisposable.dispose();
-      }
-
-      unbindIntents();
-      reset();
-      // TODO should we re emit the inital state? What if no initial state has been set.
-      // TODO should we rather throw an exception if presenter is reused after view has been detached permanently
-    }
-
+  @Override @CallSuper public void detachView() {
+    detachView(true);
     if (viewRelayConsumerDisposable != null) {
       // Cancel subscription from View to viewState Relay
       viewRelayConsumerDisposable.dispose();
@@ -295,6 +285,26 @@ protected Observable getViewStateObservable() {
     }
   }
 
+  @Override @CallSuper public void destroy() {
+    detachView(false);
+    if (viewStateDisposable != null) {
+      // Cancel the overall observable stream
+      viewStateDisposable.dispose();
+    }
+
+    unbindIntents();
+    reset();
+    // TODO should we re emit the inital state? What if no initial state has been set.
+    // TODO should we rather throw an exception if presenter is reused after view has been detached permanently
+
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Deprecated @Override @CallSuper public void detachView(boolean retainInstance) {
+  }
+
   /**
    * This is called when the View has been detached permantently (view is destroyed permanently)
    * to reset the internal state of this Presenter to be ready for being reused (even thought
diff --git a/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/backstack/first/FirstPresenter.java b/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/backstack/first/FirstPresenter.java
index 318e4979..a084aa02 100644
--- a/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/backstack/first/FirstPresenter.java
+++ b/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/backstack/first/FirstPresenter.java
@@ -32,6 +32,7 @@ public class FirstPresenter extends MviBasePresenter {
   public AtomicInteger unbindIntentCalls = new AtomicInteger(0);
   public AtomicInteger attachViewCalls = new AtomicInteger(0);
   public AtomicInteger detachViewCalls = new AtomicInteger(0);
+  public AtomicInteger destoryCalls = new AtomicInteger(0);
 
   @Override protected void bindIntents() {
     bindIntentCalls.incrementAndGet();
@@ -47,9 +48,14 @@ public class FirstPresenter extends MviBasePresenter {
     attachViewCalls.incrementAndGet();
   }
 
-  @Override public void detachView(boolean retainInstance) {
-    super.detachView(retainInstance);
-    Log.d("Presenters", "First Retain Presenter "+retainInstance);
+  @Override public void detachView() {
+    super.detachView();
+    Log.d("Presenters", "First Retain Presenter detachView");
     detachViewCalls.incrementAndGet();
   }
+
+  @Override public void destroy() {
+    super.destroy();
+    destoryCalls.incrementAndGet();
+  }
 }
diff --git a/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/backstack/second/SecondPresenter.java b/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/backstack/second/SecondPresenter.java
index 5091b037..7dfe0544 100644
--- a/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/backstack/second/SecondPresenter.java
+++ b/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/backstack/second/SecondPresenter.java
@@ -32,6 +32,8 @@ public class SecondPresenter extends MviBasePresenter {
   public AtomicInteger unbindIntentCalls = new AtomicInteger(0);
   public AtomicInteger attachViewCalls = new AtomicInteger(0);
   public AtomicInteger detachViewCalls = new AtomicInteger(0);
+  public AtomicInteger destroyCalls = new AtomicInteger(0);
+
 
   @Override protected void bindIntents() {
     bindIntentCalls.incrementAndGet();
@@ -47,9 +49,14 @@ public class SecondPresenter extends MviBasePresenter {
     attachViewCalls.incrementAndGet();
   }
 
-  @Override public void detachView(boolean retainInstance) {
-    super.detachView(retainInstance);
-    Log.d("Presenters", "SecondPresenter Retain Presenter "+retainInstance);
+  @Override public void detachView() {
+    super.detachView();
+    Log.d("Presenters", "SecondPresenter detachView Presenter");
     detachViewCalls.incrementAndGet();
   }
+
+  @Override public void destroy() {
+    super.destroy();
+    destroyCalls.incrementAndGet();
+  }
 }
diff --git a/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/lifecycle/LifecycleTestPresenter.java b/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/lifecycle/LifecycleTestPresenter.java
index 22d6620f..745b08c2 100644
--- a/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/lifecycle/LifecycleTestPresenter.java
+++ b/mvi-integration-test/src/main/java/com/hannesdorfmann/mosby3/mvi/integrationtest/lifecycle/LifecycleTestPresenter.java
@@ -40,6 +40,7 @@ public class LifecycleTestPresenter extends MviBasePresenter>
-    implements ViewGroupMviDelegate {
+    implements ViewGroupMviDelegate, Application.ActivityLifecycleCallbacks {
 
   // TODO allow custom save state hook in
 
@@ -63,6 +65,9 @@ public ViewGroupMviDelegateImpl(@NonNull View view,
     this.isInEditMode = view.isInEditMode();
     if (!isInEditMode) {
       this.activity = PresenterManager.getActivity(delegateCallback.getContext());
+      if (keepPresenterDuringScreenOrientationChange) {
+        this.activity.getApplication().registerActivityLifecycleCallbacks(this);
+      }
     } else {
       this.activity = null;
     }
@@ -155,66 +160,18 @@ private P createViewIdAndCreatePresenter() {
   @Override public void onDetachedFromWindow() {
     if (isInEditMode) return;
 
-    if (keepPresenterDuringScreenOrientationChange) {
-
-      boolean destroyedPermanently = !ActivityMviDelegateImpl.retainPresenterInstance(
-          keepPresenterDuringScreenOrientationChange, activity);
-
-      if (destroyedPermanently) {
-        // Whole activity will be destroyed
-        // Internally Orientation manager already does the clean up
-        if (DEBUG) {
-          Log.d(DEBUG_TAG, "Detaching View "
-              + delegateCallback.getMvpView()
-              + " from Presenter "
-              + presenter
-              + " and removing presenter permanently from internal cache because the hosting Activity will be destroyed permanently");
-        }
+    presenter.detachView();
+    if (DEBUG) {
+      Log.d(DEBUG_TAG,
+          "view " + delegateCallback.getMvpView() + " detached from Presenter " + presenter);
+    }
 
-        if (mosbyViewId
-            != null) { // mosbyViewId == null if keepPresenterDuringScreenOrientationChange == false
-          PresenterManager.remove(activity, mosbyViewId);
-        }
-        mosbyViewId = null;
-        presenter.detachView(false);
-      } else {
-        boolean detachedBecauseOrientationChange = ActivityMviDelegateImpl.retainPresenterInstance(
-            keepPresenterDuringScreenOrientationChange, activity);
-
-        if (detachedBecauseOrientationChange) {
-          // Simple orientation change
-          if (DEBUG) {
-            Log.d(DEBUG_TAG, "Detaching View "
-                + delegateCallback.getMvpView()
-                + " from Presenter "
-                + presenter
-                + " temporarily because of orientation change");
-          }
-          presenter.detachView(true);
-        } else {
-          // view detached, i.e. because of back stack / navigation
-          /*
-          if (DEBUG) {
-            Log.d(DEBUG_TAG, "Detaching View "
-                + delegateCallback.getMvpView()
-                + " from Presenter "
-                + presenter
-                + " because view has been destroyed. Also Presenter is removed permanently from internal cache.");
-          }
-          PresenterManager.remove(activity, mosbyViewId);
-          mosbyViewId = null;
-          presenter.detachView(false);
-          */
-        }
-      }
-    } else {
-      // retain instance feature disabled
-      presenter.detachView(false);
-      if (mosbyViewId
-          != null) { // mosbyViewId == null if keepPresenterDuringScreenOrientationChange == false
+    if (!keepPresenterDuringScreenOrientationChange) {
+      presenter.destroy();
+      if (mosbyViewId != null) {
+        // mosbyViewId == null if keepPresenterDuringScreenOrientationChange == false
         PresenterManager.remove(activity, mosbyViewId);
-      }
-      mosbyViewId = null;
+      } // else destroy presenter through activity lifecycle callbacks
     }
   }
 
@@ -257,4 +214,48 @@ public void onRestoreInstanceState(Parcelable state) {
     restoreSavedState(savedState);
     delegateCallback.superOnRestoreInstanceState(savedState.getSuperState());
   }
+
+  @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+  }
+
+  @Override public void onActivityStarted(Activity activity) {
+  }
+
+  @Override public void onActivityResumed(Activity activity) {
+  }
+
+  @Override public void onActivityPaused(Activity activity) {
+  }
+
+  @Override public void onActivityStopped(Activity activity) {
+  }
+
+  @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+  }
+
+  @Override public void onActivityDestroyed(Activity activity) {
+    if (activity == this.activity) {
+      // The hosting activity of this view has been destroyed, so time to destoryed the presenter too?
+
+      activity.getApplication().unregisterActivityLifecycleCallbacks(this);
+
+      boolean destroyedPermanently = !ActivityMviDelegateImpl.retainPresenterInstance(
+          keepPresenterDuringScreenOrientationChange, activity);
+
+      if (destroyedPermanently) {
+        // Whole activity will be destroyed
+        // Internally Orientation manager already does the clean up
+
+        if (mosbyViewId != null) {
+          // mosbyViewId == null if keepPresenterDuringScreenOrientationChange == false
+          PresenterManager.remove(activity, mosbyViewId);
+        }
+        mosbyViewId = null;
+        presenter.destroy();
+        if (DEBUG) {
+          Log.d(DEBUG_TAG, "Presenter " + presenter + " destroyed permanently");
+        }
+      }
+    }
+  }
 }
diff --git a/mvp-common/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpPresenter.java b/mvp-common/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpPresenter.java
index a9f8092e..0d0c90b1 100644
--- a/mvp-common/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpPresenter.java
+++ b/mvp-common/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpPresenter.java
@@ -40,7 +40,24 @@ public interface MvpPresenter {
   /**
    * Will be called if the view has been destroyed. Typically this method will be invoked from
    * Activity.detachView() or Fragment.onDestroyView()
-   */
+   *
+   * @deprecated This method has been split into 2 methods: {@link #detachView()} and {@link #destroy()}
+    */
   @UiThread
+  @Deprecated
   void detachView(boolean retainInstance);
+
+  /**
+   * Will be called if the view has been detached from the Presenter.
+   * Usually this happens on screen orientation changes or view (like fragment) has been put on the backstack.
+   */
+  @UiThread
+  void detachView();
+
+  /**
+   * Will be called if the presenter is no longer needed because the View has been destroyed permanently.
+   * This is where you do clean up stuff.
+   */
+  @UiThread
+  void destroy();
 }
diff --git a/mvp-nullobject-presenter/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpNullObjectBasePresenter.java b/mvp-nullobject-presenter/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpNullObjectBasePresenter.java
index f17f7282..771fcf65 100644
--- a/mvp-nullobject-presenter/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpNullObjectBasePresenter.java
+++ b/mvp-nullobject-presenter/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpNullObjectBasePresenter.java
@@ -18,7 +18,8 @@
  * 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
+ * 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)
@@ -100,7 +101,7 @@ private boolean isSubTypeOfMvpView(Class klass) {
   }
 
   @UiThread @NonNull protected V getView() {
-    if (!viewAttachedAtLeastOnce){
+    if (!viewAttachedAtLeastOnce) {
       throw new IllegalStateException("No view has ever been attached to this presenter!");
     }
     if (view != null) {
@@ -113,10 +114,30 @@ private boolean isSubTypeOfMvpView(Class klass) {
     return nullView;
   }
 
-  @UiThread @Override public void detachView(boolean retainInstance) {
+  /**
+   * {@inheritDoc}
+   */
+  @Override @UiThread public void detachView() {
+
+    detachView(true);
+
     if (view != null) {
       view.clear();
       view = null;
     }
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override @UiThread public void destroy() {
+    detachView(false);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Deprecated @UiThread @Override public void detachView(boolean retainInstance) {
+
+  }
 }
diff --git a/mvp/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpBasePresenter.java b/mvp/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpBasePresenter.java
index 7be51382..2f499d6d 100644
--- a/mvp/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpBasePresenter.java
+++ b/mvp/src/main/java/com/hannesdorfmann/mosby3/mvp/MvpBasePresenter.java
@@ -16,9 +16,7 @@
 
 package com.hannesdorfmann.mosby3.mvp;
 
-import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
-
 import java.lang.ref.WeakReference;
 
 /**
@@ -72,8 +70,7 @@ public class MvpBasePresenter implements MvpPresenter {
 
   private WeakReference viewRef;
 
-  @UiThread
-  @Override public void attachView(V view) {
+  @UiThread @Override public void attachView(V view) {
     viewRef = new WeakReference(view);
   }
 
@@ -92,16 +89,31 @@ public class MvpBasePresenter implements MvpPresenter {
    * Checks if a view is attached to this presenter. You should always call this method before
    * calling {@link #getView()} to get the view instance.
    */
-  @UiThread
-  public boolean isViewAttached() {
+  @UiThread public boolean isViewAttached() {
     return viewRef != null && viewRef.get() != null;
   }
 
-  @UiThread
-  @Override public void detachView(boolean retainInstance) {
+  /**
+   * {@inheritDoc}
+   */
+  @Deprecated @UiThread @Override public void detachView(boolean retainInstance) {
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override public void detachView() {
+    detachView(true);
     if (viewRef != null) {
       viewRef.clear();
       viewRef = null;
     }
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override public void destroy() {
+    detachView(false);
+  }
 }
diff --git a/presentermanager/src/test/java/com/hannesdorfmann/mosby3/PresenterManagerTest.java b/presentermanager/src/test/java/com/hannesdorfmann/mosby3/PresenterManagerTest.java
index 6daf5774..3cea933e 100644
--- a/presentermanager/src/test/java/com/hannesdorfmann/mosby3/PresenterManagerTest.java
+++ b/presentermanager/src/test/java/com/hannesdorfmann/mosby3/PresenterManagerTest.java
@@ -403,6 +403,12 @@ public void putGetRemovePresenter(){
 
       @Override public void detachView(boolean retainInstance) {
       }
+
+      @Override public void detachView() {
+      }
+
+      @Override public void destroy() {
+      }
     };
 
     String viewId ="123";
diff --git a/sample-mvi/build.gradle b/sample-mvi/build.gradle
index 3a144e97..6ae8a24d 100644
--- a/sample-mvi/build.gradle
+++ b/sample-mvi/build.gradle
@@ -1,5 +1,5 @@
 apply plugin: 'com.android.application'
-apply plugin: 'me.tatarka.retrolambda'
+//apply plugin: 'me.tatarka.retrolambda'
 apply from: '../findbugs.gradle'
 
 
diff --git a/sample/src/androidTest/java/com/hannesdorfmann/mosby3/sample/ApplicationTest.java b/sample/src/androidTest/java/com/hannesdorfmann/mosby3/sample/ApplicationTest.java
deleted file mode 100644
index 742c0d21..00000000
--- a/sample/src/androidTest/java/com/hannesdorfmann/mosby3/sample/ApplicationTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.hannesdorfmann.mosby3.sample;
-
-import android.app.Application;
-import android.test.ApplicationTestCase;
-
-/**
- * Testing Fundamentals
- */
-public class ApplicationTest extends ApplicationTestCase {
-  public ApplicationTest() {
-    super(Application.class);
-  }
-}
\ No newline at end of file
diff --git a/sample/src/main/java/com/hannesdorfmann/mosby3/sample/mvp/lce/SimpleCountriesPresenter.java b/sample/src/main/java/com/hannesdorfmann/mosby3/sample/mvp/lce/SimpleCountriesPresenter.java
index bdff7b15..870cc9ec 100644
--- a/sample/src/main/java/com/hannesdorfmann/mosby3/sample/mvp/lce/SimpleCountriesPresenter.java
+++ b/sample/src/main/java/com/hannesdorfmann/mosby3/sample/mvp/lce/SimpleCountriesPresenter.java
@@ -77,19 +77,17 @@ public SimpleCountriesPresenter() {
     countriesLoader.execute();
   }
 
-  @Override public void detachView(boolean retainInstance) {
-    super.detachView(retainInstance);
-
-    StringBuilder builder = new StringBuilder("---- detachView(" + retainInstance + ") ");
-    if (!retainInstance) {
-      if (countriesLoader != null) {
-        countriesLoader.cancel(true);
-      }
-      builder.append(" --> cancel Loader");
-    }
+  @Override public void detachView() {
+    super.detachView();
+    Log.d(TAG, "View detached from presenter");
+  }
 
-    builder.append(" ----");
-    Log.d(TAG, builder.toString());
+  @Override public void destroy() {
+    super.destroy();
+    if (countriesLoader != null) {
+      countriesLoader.cancel(true);
+    }
+    Log.d(TAG,"Presenter destroyed");
   }
 
   @Override public void attachView(CountriesView view) {