2015-08-11 5 views
15

In meiner app ich mit ContentProvider arbeiten und verwenden LoaderManager.LoaderCallbacks<Cursor>.Android MVP: sichere Anwendung Context in Presenter

Fragment (Ansicht)

public class ArticleCatalogFragment extends BaseFragment 
     implements ArticleCatalogPresenter.View, 
     LoaderManager.LoaderCallbacks<Cursor> { 

    @Override 
    public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
     return onCreateArticleCatalogLoader(args); 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {   
     data.registerContentObserver(new LoaderContentObserver(new Handler(), loader)); 
     updateUI(data);   
    } 

    private Loader onCreateArticleCatalogLoader(Bundle args) { 
      int categoryId = args.getInt(CATEGORY_ID); 
      Loader loader = new ArticleCatalogLoader(this.getActivity(), categoryId);    
      return loader; 
    } 

} 

Aus Sicht MVP ich brauche:

Presenter

public class ArticleCatalogPresenter extends BasePresenter 
     implements LoaderManager.LoaderCallbacks<Cursor> { 

    View view; 

    @Override 
    public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
     return onCreateArticleCatalogLoader(args); 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {   
     data.registerContentObserver(new LoaderContentObserver(new Handler(), loader)); 
     view.updateUI(data);   
    }    

    private Loader onCreateArticleCatalogLoader(Bundle args) {  
      int categoryId = args.getInt(CATEGORY_ID); 
      Loader loader = new ArticleCatalogLoader(context, categoryId); // need Context 
      return loader; 
    } 


    interface View { 
     updateUI(Cursor data) 
    } 

} 

Also, ich brauche einen Kontext in Presenter.

Es gibt einige Nuancen:

  1. Presenter über den Kontext kennen - es ist schlecht, Moderator sollte über die Android nicht wissen.

  2. Ein Kontext in Presenter kann zu Speicherverlust führen.

Ich bin jetzt besorgt darüber, wie Probleme wie Speicherlecks zu vermeiden, und wie man am besten Context in Presenter geben, Anwendungskontext oder Aktivität/Fragment verwenden?

+1

App Kontext ist der Weg zu gehen. Wenn die Ansicht den Aktivitätskontext benötigt, den sie selbst speichern kann (im Konstruktor übergeben), müssen Sie sicherstellen, dass Sie keine starke Referenz auf die Ansicht haben (unabhängig davon, ob Ihr Moderator die Aktivität/das Fragment überlebt). – JohanShogun

+0

Ein anderer Gedanke ist, dass Sie Ihre Aktivität/Ihr Fragment die Rolle des Präsentators übernehmen lassen könnten. Für mich scheint es, dass Sie Ihr Fragment dazu gebracht haben, die Rolle des Views zu übernehmen, das ist ein bisschen seltsam, da die grundlegende Funktionalität des Fragments ziemlich weit über die des Moderators hinausgeht. Ihre Ansicht befindet sich in den XML-Dateien und Unterklassen anzeigen. – JohanShogun

+0

Vielen Dank für Ihr Feedback. Ich habe Situation, wenn Teil Geschäftslogik in Presenter (größter Teil) und ein anderer Teil in Fragment (Arbeit mit CursorLoader) und das schafft Probleme. Ich möchte alle Geschäftslogik in Presenter verschieben. – Alexandr

Antwort

3

Das Hinzufügen von Kontext zu Presenter ist nicht gut, da der Moderator für die Geschäftslogik zuständig ist. Um mit dem Kontext umgehen zu können, müssen Sie das Fragment/die Aktivitäten Callbacks mit Hilfe von Schnittstellen verwenden, die angeben, welche Aktionen von der Aktivität/dem Fragment ausgeführt werden müssen, wenn Sie mit Ansichten arbeiten. Fragment/Aktivitäten sind verantwortlich für den Kontext.

Beispiel:

interface BaseContract { 
     interface BaseView { 
      //Methods for View 
      void onDoSomething(); 
     } 

     interface BasePresenter { 
      void doSomething(); 

     } 
    } 

    class BaseMainPresenter implements BaseContract.BasePresenter { 
     BaseContract.BaseView view; 

     BaseMainPresenter(BaseContract.BaseView view) { 
      this.view = view; 
     } 

     @Override 
     public void doSomething() { 
      if (view != null) 
       view.onDoSomething(); 
     } 
    } 

    class DemoClass implements BaseContract.BaseView { 

     //Create object of Presenter 

     /**** 
     * Example : 
     * BaseMainPresenter baseMainPresenter = new BaseMainPresenter(this); 
     */ 
     @Override 
     public void onDoSomething() { 
      //Deal with Context here. 
     } 
    } 
+1

könnten Sie erarbeiten, wie dies mit den Loader Callbacks wie pro OP Frage funktioniert? Danke – trocchietto

0

einfach nicht registriert Ihren Moderator als Android spezifisches Callback-Ziel (z BroadcastReceiver, LoaderManager.LoaderCallbacks etc.). Behandeln Sie die Rückrufmethoden in Ihrer Ansicht (Fragment oder Aktivität) und übergeben Sie alle zugehörigen Daten an den Präsentator.

Wenn Sie Context für die Objekterstellung benötigen, lassen Sie Ihre Ansicht dieses Objekt erstellen (da es einen Verweis auf die Context hat). In Ihrem Fall

Loader loader = new ArticleCatalogLoader(context, categoryId) 

sollte der Aufruf an

wie diese zu untestable Code

Loader loader = new ArticleCatalogLoader(context, categoryId); 

führt

view.createLoaderForCategory(categoryId) 
0

-Code Refactoring. Sie sollten vermeiden, "Geschäfts" -Objekte in Ihrem Code zu erstellen und jemand anderes macht es für Sie (jedes DI-Framework wie Dagger 2 wäre eine bessere Option, als es selbst zu behandeln)

Nachdem gesagt, Ihr Problem ist etwas, das DI hat sich vor langer Zeit gelöst. Brauchen Sie eine neue Instanz eines Objekts? Verwenden Sie einen Provider

Ein Provider ist ein Objekt, das Instanzen von Objekten "zur Verfügung stellt".Also statt

Loader loader = new ArticleCatalogLoader(context, categoryId); 

des Habens werden Sie

Loader loader = loaderProvider.get(categoryId); 

haben also das einzige, was Sie brauchen so etwas wie das ist:

public class ArticleCatalogPresenter ... { 
    ... 
    private final Provider<Loader> loaderProvider; 

    public ArticleCatalogPresenter(Provider<Loader> loaderProvider, ...) { 
     this.loaderProvider = loaderProvider; 
     ... 
    } 

    private Loader onCreateArticleCatalogLoader(Bundle args) {  
     int categoryId = args.getInt(CATEGORY_ID); 
     Loader loader = loaderProvider.get(categoryId); // no context needed anymore! 
     return loader; 
    } 

}