2016-08-23 3 views
5

Ich muss den Kontext der Aktivität im Modell verwenden, während MVP in Android, um die Liste aller installierten Anwendungen zu erhalten. Was ist der richtige Weg, um den Kontext oder eine Alternative zu erreichen, die Gleiches, während man dem MVP-Muster folgt.Need Kontext im Modell in MVP

hier sind die Klassen:

public class MainActivity extends BaseActivity 
    implements MainView,View.OnClickListener { 

private MainPresenter mPresenter; 



private Button sendButton; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    init(); 
    createPresenter(); 


} 

private void init(){ 

    sendButton= (Button) findViewById(R.id.button_send); 
    sendButton.setOnClickListener(this); 
} 

private void createPresenter() { 
    mPresenter=new MainPresenter(); 
    mPresenter.addView(this); 
} 





@Override 
public void onClick(View view) { 
    switch (view.getId()){ 
     case R.id.button_send: 
      mPresenter.onSendButtonClick(); 
      break; 
    } 
} 

@Override 
public void openOptionsActivity() { 
    Intent intent=new Intent(this,OptionsActivity.class); 
    startActivity(intent); 
} 

}

public class MainPresenter erweitert BasePresenter {

MainModel model; 
public void onSendButtonClick(){ 

    model.getListOfAllApps(); 

} 

@Override 
public void addView(MainView view) { 
    super.addView(view); 
model=new MainModel(); 
} 

}

public class MainModel { 

public void getListOfAllApps(){ 
    final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 
    mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 
    final List pkgAppsList = getPackageManager().queryIntentActivities(mainIntent, 0); 

} 

}

mit Problem in getPackageManager(). QueryIntentActivities (mainIntent, 0) .wie es zu tun, als keinen Kontext hier.

+1

Haben Sie nicht 'Application class'? –

+0

Ich habe die Anwendung class.but aber wie man es benutzt.Sie ​​sagen, dass Sie ein konstantes Feld für den Kontext verwenden, um in der Anwendungsklasse zu verwenden? Das Problem ist gemäß dem Muster, dass wir keine androidspezifische Klasse/Objekt im Modell verwenden sollten. –

+0

Überhaupt nicht. Erstellen Sie einfach applicationContext in onCreate (...) und verwenden Sie es –

Antwort

14

Ich beantwortete eine ähnliche Frage here, die Sie sich vielleicht auch ansehen möchten. Ich werde die Aufschlüsselung darüber geben, wie ich denke, dass Sie dieses spezielle Problem lösen könnten.

Verwenden Sie einen statischen Kontext aus der Anwendungsklasse

Diese Methode funktionieren würde, aber ich bin nicht gern davon. Es macht Tests schwieriger und koppelt Ihren Code zusammen.

public class App extends Application { 

    private static Context context; 

    public static Context getContext() { 
     return context; 
    } 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     context = getApplicationContext(); 
    } 
} 

Dann in Ihrem MainModel:

public class MainModel { 

    public List<String> getListOfAllApps(){ 

     final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 
     mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 
     final List<ResolveInfo> pkgAppsList = App.getContext().getPackageManager().queryIntentActivities(mainIntent, 0); 

     List<String> results = new ArrayList<>(); 
     for (ResolveInfo app : pkgAppsList) { 
      results.add(app.resolvePackageName); 
     } 
     return results; 
    } 
} 

Jetzt haben wir, dass sich die Art und Weise aufgestellt, wir uns einige bessere Möglichkeiten suchen.

Tun Sie es in der Aktivität

So Ihre Aktivität Ihre Ansicht implementiert. Wahrscheinlich macht es auch ein paar Anrdoidy-Sachen wie onActivityResult. Es gibt ein Argument für die Aktivität Android Code halten und es nur durch die View-Schnittstelle zugreifen:

public interface MainView { 

    List<String> getListOfAllApps(); 
} 

Die Aktivität:

public class MainActivity extends BaseActivity implements MainView { 

    //.. 

    @Override 
    public List<String> getListOfAllApps(){ 

     final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 
     mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 
     final List<ResolveInfo> pkgAppsList = getPackageManager().queryIntentActivities(mainIntent, 0); 

     List<String> results = new ArrayList<>(); 
     for (ResolveInfo app : pkgAppsList) { 
      results.add(app.resolvePackageName); 
     } 
     return results; 
    } 

    //.. 
} 

Und der Moderator:

public class MainPresenter extends BasePresenter { 

    public void onSendButtonClick(){ 

     view.getListOfAllApps(); 
    } 
} 

Zusammenfassung der Details in einer separaten Klasse

Während die letzte Option nicht die Regeln von MVP bricht, fühlt es sich nicht ganz richtig an, da das Abrufen einer Liste von Paketen nicht wirklich ist eine View-Operation. Meine bevorzugte Option ist, die Verwendung von Kontext hinter einer Schnittstelle/Klasse zu verbergen.

Erstellen Sie eine Klasse PackageModel (oder was auch immer Name Sie Lust):

public class PackageModel { 

    private Context context; 

    public PackageModel(Context context) { 
     this.context = context; 
    } 

    public List<String> getListOfAllApps(){ 

     final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 
     mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 
     final List<ResolveInfo> pkgAppsList = context.getPackageManager().queryIntentActivities(mainIntent, 0); 

     List<String> results = new ArrayList<>(); 
     for (ResolveInfo app : pkgAppsList) { 
      results.add(app.resolvePackageName); 
     } 
     return results; 
    } 
} 

nun Ihre Presenter benötigen diese Parameter als Konstruktor haben:

public class MainPresenter extends BasePresenter { 

    private PackageModel packageModel; 

    public MainPresenter(PackageModel packageModel) { 
     this.packageModel = packageModel; 
    } 

    public void onSendButtonClick(){ 

     packageModel.getListOfAllApps(); 
    } 
} 

schließlich in Ihrer Aktivität:

public class MainActivity extends BaseActivity implements MainView { 

    private MainPresenter presenter; 

    private void createPresenter() { 

     PackageModel packageModel = new PackageModel(this); 
     presenter = new MainPresenter(packageModel); 
     presenter.addView(this); 
    } 
} 

Jetzt ist die Verwendung von Kontext vom Presenter und es c verborgen ein Weitermachen ohne Android-Kenntnisse. Dies wird als Konstruktorinjektion bezeichnet. Wenn Sie ein Dependency-Injection-Framework verwenden, kann es alle Abhängigkeiten für Sie erstellen.

Wenn Sie wollten, könnten Sie eine Schnittstelle für PackageModel machen, aber ich denke nicht, dass es wirklich notwendig ist, wie ein Spott Framework wie Mockito einen Stub erstellen kann, ohne eine Schnittstelle zu verwenden.

+3

Als [dieser Kommentar] (http: // stackoverflow.com/questions/37137624/how-to-use-shared-preferences-in-mvp-ohne-dolch-und-nicht-verursacht-presenter-to # comment61849985_37138822) sagte, es ist nur die Abhängigkeit versteckt, die schlimmer ist. [Die Antwort] (http://stackoverflow.com/a/37216407/1276636) auf dem gleichen Thread ist sehr gut, da es Abhängigkeiten sehr klar macht. Ich fand [dieses Video] (https://www.youtube.com/watch?v=-FRm3VPhseI&index=2&list=PL693EFD059797C21E) großartig auf Singletons und versteckten Abhängigkeiten. – Sufian

+0

Ich würde nur eine kleine Anpassung an Punkt 3 vornehmen und PackageModel zu einer Schnittstelle machen und getListOfAllApps() zu seiner einzigen Methode machen. –

+0

Ihr erster Weg ist schrecklich. Löschen Sie einfach den ersten Weg. es ist absolut falsch. Der Moderator und das Modell sollten in reinem Java sein. die Schnittstelle ist der wahre Weg. –

-1

Grundsätzlich haben Sie folgende Möglichkeiten:

1) passieren immer ein Context zum Modell. Egal welches Ereignis in Android passiert, Sie haben immer eine Art von Context zur Verfügung. (Und Ihr Code wird nur als Reaktion auf Ereignisse aufgerufen.)

2) getApplicationContext() und für zukünftige Verwendung in einer statischen Variablen speichern.

Es gibt folgende gotchas:

Ein Activity ein Context ist, aber wenn Sie einen Link zu einer Aktivität zu speichern, erhalten Sie einen Speicherverlust. Aktivitäten werden neu erstellt, wenn sich beispielsweise der Bildschirm dreht. Das gleiche gilt für Kontexte, die an BroadcastReceiver und andere Arten von Context übergeben werden. Alle von ihnen haben eine Lebensdauer, und diese Lebensdauer ist nicht, was Sie für Modell benötigen.

Es ist möglich, dass Ihre Anwendung von Android abgebrochen und neu gestartet wird. In diesem Fall können einige globale (statische) Variablen auf gesetzt werden. Das heißt, sie sind null, es sei denn, Ihre Anwendung schreibt etwas an sie. Insbesondere kann eine statische Variable, die auf den Anwendungskontext zeigt, in einem der Neustartszenarien null werden. Solche Probleme sind schwer zu testen.

+2

. Sie sollten keinen Kontext in Presenter und Modell verwenden. Dieser Code wird nicht testbar sein. –