Need Context in Model in MVP

前端 未结 2 800
太阳男子
太阳男子 2020-12-31 09:42

I need to use the Context of activity in the model while using MVP in android to get the list of all the installed application.what is the correct way to access the context

2条回答
  •  温柔的废话
    2020-12-31 10:05

    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.

    public class App extends Application {
    
        private static Context context;
    
        public static Context getContext() {
            return context;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            context = getApplicationContext();
        }
    }
    

    Then in your MainModel:

    public class MainModel {
    
        public List getListOfAllApps(){
    
            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
            final List pkgAppsList = App.getContext().getPackageManager().queryIntentActivities(mainIntent, 0);
    
            List results = new ArrayList<>();
            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:

    public interface MainView {
    
        List getListOfAllApps();
    }
    

    The Activity:

    public class MainActivity extends BaseActivity implements MainView {
    
        //..
    
        @Override
        public List getListOfAllApps(){
    
            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
            final List pkgAppsList = getPackageManager().queryIntentActivities(mainIntent, 0);
    
            List results = new ArrayList<>();
            for (ResolveInfo app : pkgAppsList) {
                results.add(app.resolvePackageName);
            }
            return results;
        }
    
        //..
    }
    

    And the Presenter:

    public class MainPresenter extends BasePresenter {
    
        public void onSendButtonClick(){
    
            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):

    public class PackageModel {
    
        private Context context;
    
        public PackageModel(Context context) {
            this.context = context;
        }
    
        public List getListOfAllApps(){
    
            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
            final List pkgAppsList = context.getPackageManager().queryIntentActivities(mainIntent, 0);
    
            List results = new ArrayList<>();
            for (ResolveInfo app : pkgAppsList) {
                results.add(app.resolvePackageName);
            }
            return results;
        }
    } 
    

    Now have your Presenter require this as a constructor parameter:

    public class MainPresenter extends BasePresenter {
    
        private PackageModel packageModel;
    
        public MainPresenter(PackageModel packageModel) {
            this.packageModel = packageModel;
        }
    
        public void onSendButtonClick(){
    
            packageModel.getListOfAllApps();
        }
    }
    

    Finally in your Activity:

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

    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.

提交回复
热议问题