2015-08-11 25 views
15

W moim app pracuję z ContentProvider i wykorzystać LoaderManager.LoaderCallbacks<Cursor>.Android MVP: bezpieczne użytkowanie Kontekst w Presenter

fragment (Widok)

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; 
    } 

} 

Z punktu widzenia MVP muszę:

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) 
    } 

} 

Potrzebuję kontekstu w prezencie.

Istnieją pewne niuanse:

  1. Presenter wiedzieć o kontekście - to jest złe, nie Prezenter powinien wiedzieć o Androidzie.

  2. Posiadanie kontekstu w programie Presenter może prowadzić do wycieku pamięci.

Martwię się, jak uniknąć problemów, takich jak wycieki pamięci, i jak najlepiej przekazywać kontekst w prezencie, korzystać z kontekstu aplikacji lub aktywności/fragmentu?

+1

kontekst aplikacji jest do zrobienia. Jeśli widok wymaga kontekstu działania, może go zapisać samodzielnie (przekazany w konstruktorze), ale upewnij się, że nie posiadasz silnego odniesienia do widoku (niezależnie), jeśli twój prezenter przeżyje działanie/fragment. – JohanShogun

+0

Inną myślą jest, że możesz pozwolić swojej aktywności/fragmentowi wziąć rolę prezentera. Wydaje mi się, że sprawiłeś, że twój fragment wciela się w rolę widoku, to trochę dziwne, ponieważ podstawowa funkcjonalność tego fragmentu całkiem dobrze pogrąża się w stosunku do prezentera. Twój widok znajduje się w plikach xml i przeglądaj podklasy. – JohanShogun

+0

Dziękujemy za opinię. Mam sytuację, gdy część logiki biznesowej w Presenter (większa część) i innej części w Fragment (praca z CursorLoader) i to stwarza problemy. Chcę przenieść całą logikę biznesową w Presenter. – Alexandr

Odpowiedz

3

Dodawanie kontekstu do prezentera nie jest dobre, ponieważ prezenter odpowiada za logikę biznesową. Aby poradzić sobie z kontekstem, musisz mieć Fragment/Działania wykorzystywać Callbacki za pomocą interfejsów, które będą określały, jakie działania muszą być wykonane przez działanie/fragment, gdy mamy do czynienia z widokami. Fragment/Działania są odpowiedzialne za zapewnienie kontekstu.

Przykład:

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

czy mógłbyś wyjaśnić, jak to działa z callbackami Loader w pytaniu OP? – trocchietto

0

prostu nie zarejestrować jako prezenter Android konkretnego celu wywołania zwrotnego (np BroadcastReceiver, LoaderManager.LoaderCallbacks itd.). Obsługa metod wywołań zwrotnych w widoku (fragment lub działanie) i przekazywanie wszystkich powiązanych danych do prezentera.

Jeśli potrzebujesz Context do utworzenia obiektu, pozwól, aby twój widok utworzył ten obiekt (ponieważ ma odniesienie do Context). W twoim przypadku wywołanie

Loader loader = new ArticleCatalogLoader(context, categoryId) 

należy refactored

view.createLoaderForCategory(categoryId) 
0

kod jak poniżej

Loader loader = new ArticleCatalogLoader(context, categoryId); 

prowadzi do kodu nietestowalna. Powinieneś unikać tworzenia obiektów "biznesowych" w swoim kodzie i pozwolić, by ktoś zrobił to za Ciebie (dowolna struktura DI, taka jak Dagger 2 byłaby lepszą opcją niż samodzielne posługiwanie się nią)

Powiedziawszy to, twój problem jest czymś, co DI rozwiązał dawno temu. Czy potrzebujesz nowej, świeżej instancji dowolnego obiektu? Użyj obiektu, który "dostarcza" wystąpień obiektów.Więc zamiast

Loader loader = new ArticleCatalogLoader(context, categoryId); 

trzeba będzie

Loader loader = loaderProvider.get(categoryId); 

więc jedyną rzeczą, potrzebne jest coś takiego:

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; 
    } 

} 
Powiązane problemy