Need Context In Model In Mvp
Solution 1:
I answered a similar question here which you may want to have a look at too. I'll give the breakdown of how I think you could solve this particular problem though.
Use a static context from the Application class
This method would work but I'm not fond of it. It makes testing harder and couples your code together.
publicclassAppextendsApplication {
privatestaticContext context;
publicstaticContextgetContext() {
return context;
}
@OverridepublicvoidonCreate() {
super.onCreate();
context = getApplicationContext();
}
}
Then in your MainModel:
publicclassMainModel {
public List<String> getListOfAllApps(){
finalIntentmainIntent=newIntent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> pkgAppsList = App.getContext().getPackageManager().queryIntentActivities(mainIntent, 0);
List<String> results = newArrayList<>();
for (ResolveInfo app : pkgAppsList) {
results.add(app.resolvePackageName);
}
return results;
}
}
Now we've got that out the way, let's look at some better options.
Do it in the Activity
So your Activity implements your View. It's probably doing a few Anrdoidy things too such as onActivityResult. There's an argument for keeping Android code in the Activity and just accessing it through the View interface:
publicinterfaceMainView {
List<String> getListOfAllApps();
}
The Activity:
publicclassMainActivityextendsBaseActivityimplementsMainView {
//..@Overridepublic List<String> getListOfAllApps(){
finalIntentmainIntent=newIntent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> pkgAppsList = getPackageManager().queryIntentActivities(mainIntent, 0);
List<String> results = newArrayList<>();
for (ResolveInfo app : pkgAppsList) {
results.add(app.resolvePackageName);
}
return results;
}
//..
}
And the Presenter:
publicclassMainPresenterextendsBasePresenter {
publicvoidonSendButtonClick(){
view.getListOfAllApps();
}
}
Abstract the details in a separate class
Whilst the last option doesn't break the rules of MVP it doesn't feel quite right as getting a list of packages isn't really a View operation. My preferred option is to hide the use of Context behind an interface/class.
Create a class PackageModel
(or whatever name takes your fancy):
publicclassPackageModel {
private Context context;
publicPackageModel(Context context) {
this.context = context;
}
public List<String> getListOfAllApps(){
finalIntentmainIntent=newIntent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> pkgAppsList = context.getPackageManager().queryIntentActivities(mainIntent, 0);
List<String> results = newArrayList<>();
for (ResolveInfo app : pkgAppsList) {
results.add(app.resolvePackageName);
}
return results;
}
}
Now have your Presenter require this as a constructor parameter:
publicclassMainPresenterextendsBasePresenter {
private PackageModel packageModel;
publicMainPresenter(PackageModel packageModel) {
this.packageModel = packageModel;
}
publicvoidonSendButtonClick(){
packageModel.getListOfAllApps();
}
}
Finally in your Activity:
publicclassMainActivityextendsBaseActivityimplementsMainView {
private MainPresenter presenter;
privatevoidcreatePresenter() {
PackageModelpackageModel=newPackageModel(this);
presenter = newMainPresenter(packageModel);
presenter.addView(this);
}
}
Now the use of Context is hidden from the Presenter and it can carry on without any knowledge of Android. This is known as constructor injection. If you're using a dependency injection framework it can build all the dependencies for you.
If you wanted to you could make an interface for PackageModel but I don't think it's really necessary as a mocking framework like Mockito can create a stub without using an interface.
Solution 2:
Basically, you have the following options:
1) Always pass a Context
to the Model. Whatever event happens in Android, you always have some kind of Context
available. (And your code is invoked only in response to events.)
2) getApplicationContext()
and store it for future use in a static variable.
There are the following gotchas:
An Activity
is a Context
, but if you store a link to an Activity, you get a memory leak. Activities are re-created when for example the screen turns.
The same about contexts passed to BroadcastReceivers and other kinds of Context. All of them have a lifetime, and that lifetime is not what you need for Model.
It is possible that your application is killed and restarted by Android. In this case, some global (static) variables may be set to null. That is, they will be null unless your application happens to write something to them. In particular, a static variable pointing to the application context may become null in one of restart scenarios. Such problems are difficult to test against.
Post a Comment for "Need Context In Model In Mvp"