MVP 模型中需要上下文
Need Context in Model in MVP
我需要在模型中使用 activity 的上下文,而在 android 中使用 MVP 来获取所有已安装的列表 application.what 是访问上下文的正确方法或在遵循 MVP 模式的同时实现相同目标的任何替代方法。
这里是 classes:
主要Activity.java
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);
}
}
主要Presenter.java
public class MainPresenter 扩展 BasePresenter {
MainModel model;
public void onSendButtonClick() {
model.getListOfAllApps();
}
@Override
public void addView(MainView view) {
super.addView(view);
model = new MainModel();
}
}
主要Model.java
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);
}
}
在 getPackageManager().queryIntentActivities(mainIntent, 0)
中有问题。如何解决这里没有任何上下文。
基本上,您有以下选择:
1) 始终将 Context
传递给 Model。无论 Android 中发生什么事件,您总会有某种可用的 Context
。 (并且您的代码仅在响应事件时被调用。)
2) getApplicationContext()
并将其存储在静态变量中以备将来使用。
存在以下陷阱:
Activity
是 Context
,但如果将 link 存储到 Activity,则会发生内存泄漏。例如在屏幕转动时重新创建活动。
传递给 BroadcastReceivers 的上下文和其他类型的上下文也是如此。它们都有生命周期,而生命周期不是 Model.
所需要的
您的应用程序可能被 Android 终止并重新启动。在这种情况下,一些全局(静态)变量可能被设置为空。也就是说,除非您的应用程序碰巧向它们写入内容,否则它们将为空。特别是,指向应用程序上下文的静态变量可能在其中一种重启情况下变为空。这样的问题很难测试。
我回答了一个类似的问题 ,您可能也想看看。不过,我将详细说明我认为您可以如何解决这个特定问题。
使用来自应用程序的静态上下文class
这个方法可以,但我不喜欢。它使测试更难并将您的代码耦合在一起。
public class App extends Application {
private static Context context;
public static Context getContext() {
return context;
}
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
}
然后在您的 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;
}
}
现在我们已经解决了这个问题,让我们看看一些更好的选择。
在Activity
中做
所以您的 Activity 实现了您的视图。它可能也在做一些 Anrdoidy 的事情,比如 onActivityResult。在 Activity 中保留 Android 代码并仅通过 View 界面访问它是有争议的:
public interface MainView {
List<String> getListOfAllApps();
}
Activity:
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;
}
//..
}
主持人:
public class MainPresenter extends BasePresenter {
public void onSendButtonClick(){
view.getListOfAllApps();
}
}
在单独的文件中提取详细信息class
虽然最后一个选项没有违反 MVP 的规则,但感觉不太正确,因为获取包列表并不是 真正的 视图操作。我的首选选项是将 Context 的使用隐藏在 interface/class.
后面
创建一个 class PackageModel
(或您喜欢的任何名称):
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;
}
}
现在让您的 Presenter 要求将此作为构造函数参数:
public class MainPresenter extends BasePresenter {
private PackageModel packageModel;
public MainPresenter(PackageModel packageModel) {
this.packageModel = packageModel;
}
public void onSendButtonClick(){
packageModel.getListOfAllApps();
}
}
终于在你的 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);
}
}
现在 Context 的使用对 Presenter 是隐藏的,它可以在不知道 Android 的情况下继续使用。这称为构造函数注入。如果您使用的是依赖项注入框架,它可以为您构建所有依赖项。
如果您愿意,可以为 PackageModel 创建一个接口,但我认为这不是真正必要的,因为像 Mockito 这样的模拟框架可以在不使用接口的情况下创建存根。
我需要在模型中使用 activity 的上下文,而在 android 中使用 MVP 来获取所有已安装的列表 application.what 是访问上下文的正确方法或在遵循 MVP 模式的同时实现相同目标的任何替代方法。
这里是 classes:
主要Activity.java
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);
}
}
主要Presenter.java
public class MainPresenter 扩展 BasePresenter { MainModel model;
public void onSendButtonClick() {
model.getListOfAllApps();
}
@Override
public void addView(MainView view) {
super.addView(view);
model = new MainModel();
}
}
主要Model.java
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);
}
}
在 getPackageManager().queryIntentActivities(mainIntent, 0)
中有问题。如何解决这里没有任何上下文。
基本上,您有以下选择:
1) 始终将 Context
传递给 Model。无论 Android 中发生什么事件,您总会有某种可用的 Context
。 (并且您的代码仅在响应事件时被调用。)
2) getApplicationContext()
并将其存储在静态变量中以备将来使用。
存在以下陷阱:
Activity
是 Context
,但如果将 link 存储到 Activity,则会发生内存泄漏。例如在屏幕转动时重新创建活动。
传递给 BroadcastReceivers 的上下文和其他类型的上下文也是如此。它们都有生命周期,而生命周期不是 Model.
您的应用程序可能被 Android 终止并重新启动。在这种情况下,一些全局(静态)变量可能被设置为空。也就是说,除非您的应用程序碰巧向它们写入内容,否则它们将为空。特别是,指向应用程序上下文的静态变量可能在其中一种重启情况下变为空。这样的问题很难测试。
我回答了一个类似的问题
使用来自应用程序的静态上下文class
这个方法可以,但我不喜欢。它使测试更难并将您的代码耦合在一起。
public class App extends Application {
private static Context context;
public static Context getContext() {
return context;
}
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
}
然后在您的 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;
}
}
现在我们已经解决了这个问题,让我们看看一些更好的选择。
在Activity
中做所以您的 Activity 实现了您的视图。它可能也在做一些 Anrdoidy 的事情,比如 onActivityResult。在 Activity 中保留 Android 代码并仅通过 View 界面访问它是有争议的:
public interface MainView {
List<String> getListOfAllApps();
}
Activity:
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;
}
//..
}
主持人:
public class MainPresenter extends BasePresenter {
public void onSendButtonClick(){
view.getListOfAllApps();
}
}
在单独的文件中提取详细信息class
虽然最后一个选项没有违反 MVP 的规则,但感觉不太正确,因为获取包列表并不是 真正的 视图操作。我的首选选项是将 Context 的使用隐藏在 interface/class.
后面创建一个 class PackageModel
(或您喜欢的任何名称):
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;
}
}
现在让您的 Presenter 要求将此作为构造函数参数:
public class MainPresenter extends BasePresenter {
private PackageModel packageModel;
public MainPresenter(PackageModel packageModel) {
this.packageModel = packageModel;
}
public void onSendButtonClick(){
packageModel.getListOfAllApps();
}
}
终于在你的 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);
}
}
现在 Context 的使用对 Presenter 是隐藏的,它可以在不知道 Android 的情况下继续使用。这称为构造函数注入。如果您使用的是依赖项注入框架,它可以为您构建所有依赖项。
如果您愿意,可以为 PackageModel 创建一个接口,但我认为这不是真正必要的,因为像 Mockito 这样的模拟框架可以在不使用接口的情况下创建存根。