Android MVP模式深入实践探索(二),Android开发必会技术

public void onError(String result, Object tag) {

mMainView.setLoginText(“登录失败”);

}

}, 100);

}

}

第三步MainActivity实现定义的View接口并调用Presenter:

public class MainActivity extends Activity implements View.OnClickListener, IMainView {

private TextView mUserNameText;

private TextView mSexText;

private LinearLayout mAdsLayout;

private LinearLayout mNewsLayout;

private TextView mLoginText;

private LinearLayout mMyAttentionLayout;

private LinearLayout mMyCommentLayout;

private ImageView mVipImageView;

private MainPresenter mMainPresenter;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mMainPresenter = new MainPresenter(this);

mMainPresenter.setArguments(getIntent().getExtras());

initView();

}

private void initView() {

mUserNameText = (TextView) findViewById(R.id.tv_user_name);

mSexText = (TextView) findViewById(R.id.tv_sex);

mLoginText = (TextView) findViewById(R.id.tv_login);

mLoginText.setOnClickListener(this);

mMyAttentionLayout = (LinearLayout) findViewById(R.id.ll_my_attention);

mMyCommentLayout = (LinearLayout) findViewById(R.id.ll_my_comment);

mVipImageView = (ImageView) findViewById(R.id.iv_vip);

mMainPresenter.init();

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.tv_login:

mMainPresenter.sendLoginRequest();

break;

default:

break;

}

}

@Override

public void setUserNameText(String str) {

mUserNameText.setText(str);

}

@Override

public void setSexText(String str) {

mSexText.setText(str);

}

@Override

public void setLoginText(String str) {

mLoginText.setText(str);

}

@Override

public void setLoginTextVisibility(boolean visibility) {

mLoginText.setVisibility(visibility ? View.VISIBLE: View.GONE);

}

@Override

public void setIsShowVipImage(bool
ean isShowVipImage) {

if (isShowVipImage) {

mVipImageView.setVisibility(View.VISIBLE);

mVipImageView.setImageResource(R.drawable.ic_vip);

} else {

mVipImageView.setVisibility(View.GONE);

}

}

@Override

public void setMyAttentionLayoutVisibility(boolean visibility) {

mMyAttentionLayout.setVisibility(visibility ? View.VISIBLE: View.GONE);

}

@Override

public void setMyCommentLayoutVisibility(boolean visibility) {

mMyCommentLayout.setVisibility(visibility ? View.VISIBLE: View.GONE);

}

@Override

public Context getContext() {

return this;

}

}

这里我们需要定义的跟View交互的接口方法数量可能有点多,几乎是你每改一处就要增加一个,然后在Presenter当中去调用,显得很麻烦,但是我们也有方法能够减少这类麻烦(后续文章中会介绍Presenter与View交互的可以避免这些)。

另外,上述代码你可能关心的一个问题是,原来if-else也算是一种逻辑,这是当然的,上面的代码只是为了示例判断都比较简单,实际中可能会比较复杂,比如你的判断条件可能是一个综合判断条件,由多个判断因素共同决定,而这些因素又可能来自不同的数据源,比如你可能需要从本地取一个值然后去&&前一个页面传递过来的值,最后你还得调用一个查询接口从服务器获取一个状态,这都是有可能的。所有涉及这些判断因素的变量可能又会牵扯出一些逻辑处理。那么UI界面的所有if-else判断都是需要提取到Presenter当中吗?当然不是,再来看一个代码:

@Override

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

setIsShowCommentView(isChecked);

}

private void setIsShowCommentView(boolean isChecked) {

if (isChecked) {

mMyCommentText.setText(“AAA”);

mMyCommentLayout.setVisibility(View.VISIBLE);

} else if (mLoginText.getVisibility() == View.VISIBLE) {

mMyCommentText.setText(“BBB”);

mMyCommentLayout.setVisibility(View.GONE);

}

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.tv_my_attention:

if (mMyAttentionText.getText().toString().equals(getString(R.string.attention))) {

mMyAttentionText.setText(R.string.unattention);

} else if (mMyAttentionText.getText().toString().equals(getString(R.string.unattention))) {

mMyAttentionText.setText(R.string.attention);

}

break;

default:

break;

}

}

这个代码里面已经都是纯UI的操作,所以不需要再提取了,如果再增加一些逻辑变量去控制上面的代码,反而又增加了逻辑的复杂性。你会发现这个代码有个特点就是它是由界面中其他控件的状态改变直接导致某个控件的变化,比如点击状态、复选框状态发生变化,需要另一些UI状态对此作出变化。也是UI状态的变化直接导致的UI状态变化,而非业务数据导致的。

所以这里可以总结出只有那些是由业务数据造成的影响而导致的UI变化,导致这种变化的因素我们需要将它从View层剥离出来,挪到Presenter中去。当然,如果你乐意并且时间充足,也可以把所有导致的UI变化的因素全部抽象成逻辑,但是这样会增加时间成本。

前面例子是改变UI的状态,下面看一个提交UI的数据的代码,就用第一篇中登录的例子来修改一下:

在这里插入图片描述

上面代码也是我随意写的,这里除了用户名密码增加了一些参数,而增加的这些参数的特点是跟UI没有任何关系,所以这部分的代码做MVP的改造的话,主要是把绿框中的UI相关的代码留在当前页面,其他全部挪到Presenter当中即可,改造代码这里就不贴了,跟前面的过程类似。

到这里差不多能把View层中跟UI相关或者无关的代码分离开来了, 至少从主观上我们能够做一些区分了,然而前面我们只是简单的将逻辑部分全部移到了Presenter中, 那么Model层呢? 其实如果你的项目比较简单,到这里我觉得就足够了,为什么,因为再分下去,除了增加调用链的长度以外,没有任何好处。但是如果你的项目稍微大一点复杂一点,那么最好将Presenter里的调用再次抽取,也就是我们所谓的Model层,那哪些代码需要放到这一层呢? 主要是数据的存取、转换、过滤操作等相关的代码,比如请求网络回来判断结果值是否成功、json数据解析成Java实体类、本地数据库的查询等。当然在Model层也可以有业务逻辑,不过这部分的逻辑主要跟数据有关系的,比如数据的转换。如果这一层的逻辑较少只是纯粹的读写的话,那么它看起来只不过是将取得的数据直接抛给了Presenter层而已(因为IO读写实际上只需一个工具类即可完成,这个工具类你可以把它看做Model层)。说白了,只是封装的层次深浅和调用链长短的区别了。但是如果涉及数据的业务比较重(如有大量的数据类转换处理),那就必须进行抽取Model层来做。所以这个是要根据实际情况进行取舍的,不一定层次越多越好,维护成本也要考虑进去。

下面将前面例子中提取的MainPresenter中的代码进一步提取,将其中的数据存取的部分提取到Model层,先看一下哪些是可以从Presenter中剥离的:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

其中绿色框起来的部分是可以被放到Model层进行处理,应该是比较显而易见的,其实判断方法很简单,理论上在Presenter中除了直接调用view层接口的,剩下的部分都可以算作是Model层。但是如果你真的按照这个去区分的话,又会有点过分,比如Presenter中的一些if-else判断也是可以挪到Model层处理的,又比如下面这行代码,这个参数是由Activity中传递到Presenter然后在Presenter中获取出来的,挪到Model层有点浪费时间。

在这里插入图片描述

先不管这些细节的,我们先把上面绿色框起来的部分提取到Model层:

先抽取Model接口:

public interface IMainModel {

void getSavedUser();

boolean getIsLogin();

void sendLoginRequest();

}

Model实现类:

public class MainModel implements IMainModel {

private IMainPresenter mMainPresenter;

public MainModel(IMainPresenter presenter) {

mMainPresenter = presenter;

}

@Override

public void getSavedUser() {

User user = LocalDataManager.getSavedObj(mMainPresenter.getContext(), “User”, User.class);

mMainPresenter.onGetUser(user);

}

@Override

public boolean getIsLogin() {

return LocalDataManager.getShareBool(mMainPresenter.getContext(), “isLogin”);

}

@Override

public void sendLoginRequest() {

String[] values = LocalDataManager.getShareStrings(

mMainPresenter.getContext(), “userName”, “userPassword”);

String userName = values[0];

String userPassword = values[1];

if (!TextUtils.isEmpty(userName) && !TextUtils.isEmpty(userPassword)) {

String url = “http://xx.xxx.xxx”;

StringHashMap requestParams = new StringHashMap();

requestParams.put(“userName”, userName);

requestParams.put(“password”, userPassword);

HttpDataManager.post(url, requestParams, new HttpCallback() {

@Override

public void onSuccess(String result, Object tag) {

LoginResultBean loginResult = JsonUtils.jsonToObject(result, LoginResultBean.class);

if (loginResult != null) {

if (loginResult.getErrCode() == 200) {

mMainPresenter.onLoginSuccess();

} else {

mMainPresenter.onLoginFail();

}

} else {

mMainPresenter.onLoginFail();

}

}

@Override

public void onError(String result, Object tag) {

mMainPresenter.onLoginFail();

}

}, 100);

}

}

}

其中有一部分纯IO的做成了工具类方法放到了LocalDataManager类中:

public class LocalDataManager {

public static T getSavedObj(Context context, String key, Class cls) {

String json = getShareString(context, key);

T t = JsonUtils.jsonToObject(json, cls);

return t;

}

public static String getShareString(Context context, String key) {

SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);

return preferences.getString(key, “”);

}

public static boolean getShareBool(Context context, String key) {

SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);

return preferences.getBoolean(key, false);

}

public static String[] getShareStrings(Context context, String…keys) {

SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);

String[] res = new String[keys.length];

for (int i = 0; i < keys.length; i++) {

res[i] = preferences.getString(keys[i], “”);

}

return res;

}

}

然后就是我们的Presenter, 上面的代码中调用了IMainPresenter接口,所以我们要先定义跟Presenter交互的接口IMainPresenter:

public interface IMainPresenter {

void onGetUser(User user);

void onLoginSuccess();

void onLoginFail();

Context getContext();

}

这几个方法基本都是回传数据,或者回调方法,最后就是MainPresenter实现这个接口并且调用Model类了:

public class MainPresenter implements IMainPresenter {

private IMainView mMainView;

private Bundle arguments;

private IMainModel mMainModel;

public MainPresenter(IMainView mainView) {

this.mMainView = mainView;

mMainModel = new MainModel(this);

}

public void setArguments(Bundle arguments) {

this.arguments = arguments;

}

public Bundle getArguments() {

return arguments;

}

public void init() {

mMainModel.getSavedUser();

mMainView.setLoginTextVisibility(!mMainModel.getIsLogin());

mMainView.setIsShowVipImage(getArguments().getBoolean(“isShowVip”, false));

}

@Override

public void onGetUser(User savedUser) {

if (savedUser != null) {

if (!TextUtils.isEmpty(savedUser.getName())) {

mMainView.setUserNameText(savedUser.getName());

} else {

mMainView.setUserNameText(“匿名用户”);

}

if (savedUser.getSex() == 1) {

mMainView.setSexText(“男”);

} else if (savedUser.getSex() == 2) {

mMainView.setSexText(“女”);

}

}

}

public void sendLoginRequest() {

mMainModel.sendLoginRequest();

}

@Override

public void onLoginSuccess() {

mMainView.setMyAttentionLayoutVisibility(true);

mMainView.setMyCommentLayoutVisibility(true);

}

@Override

public void onLoginFail() {

mMainView.setMyAttentionLayoutVisibility(false);

mMainView.setMyCommentLayoutVisibility(false);

mMainView.setLoginText(“登录失败”);

}

@Override

public Context getContext() {

return mMainView.getContext();

}

}

可以看到我们这时的MainPresenter变的比较简单一点了,但是你会发现还是有一些if-else被留在了当前类,没有提到Model里:在这里插入图片描述

那这部分可不可以抽取到Model层呢,当然可以,我们只要在IMainPresenter接口中增加两个方法:

在这里插入图片描述

然后将if-else逻辑移到Model层:在这里插入图片描述

最后MainPresenter中会变成下面这样:

在这里插入图片描述

看上去更简洁了,MainPresenter就像一个只有getter和setter的普通Java类,但是这样的话几乎所有的事都扛在了Model的肩上,Model层的责任显得过重。实际上这样做只是增加了接口方法的数量而已,对业务上的管理而言并没有带来太大的方便,我个人还是倾向于将这部分留在Presenter当中,对UI的逻辑控制还是由Presenter掌握比较好,如果抽取的话,则是由Model来掌控这个逻辑权了。

下面对提取各层代码的基本判断进行一个简单的总结:

  • View层纯UI业务的操作,包括任何UI页面的加载显示(如布局、弹窗),UI属性的变化(状态、颜色、大小、位置等),UI内容的变化(填充控件数据、获取控件数据),由一个UI控件导致的另一个UI控件的状态变化,任何不涉及业务数据导致的UI变化(业务数据可能是来自网络、本地数据、页面传值等)。 这部分代码的特点是接收UI输入的变化,输出的仍然是UI相关的变化。

  • Presenter层纯逻辑业务的操作,包括在UI页面中由于任何的业务数据的获取、解析、转换等导致的UI状态变化的逻辑(业务数据可能是来自网络、本地数据库、文件、SharePref、页面传值等),在UI界面中的不限于if-else等条件判断语句导致的UI状态变化的逻辑(除UI控件本身的状态外,业务数据导致的),UI界面中其他任何不掺杂View层代码的纯逻辑代码(如一段纯算法逻辑的代码)。这部分代码的特点是接收UI输入的变化会导致业务数据的变化,而接收业务数据的输入也会导致UI的变化。

,MainPresenter就像一个只有getter和setter的普通Java类,但是这样的话几乎所有的事都扛在了Model的肩上,Model层的责任显得过重。实际上这样做只是增加了接口方法的数量而已,对业务上的管理而言并没有带来太大的方便,我个人还是倾向于将这部分留在Presenter当中,对UI的逻辑控制还是由Presenter掌握比较好,如果抽取的话,则是由Model来掌控这个逻辑权了。

下面对提取各层代码的基本判断进行一个简单的总结:

  • View层纯UI业务的操作,包括任何UI页面的加载显示(如布局、弹窗),UI属性的变化(状态、颜色、大小、位置等),UI内容的变化(填充控件数据、获取控件数据),由一个UI控件导致的另一个UI控件的状态变化,任何不涉及业务数据导致的UI变化(业务数据可能是来自网络、本地数据、页面传值等)。 这部分代码的特点是接收UI输入的变化,输出的仍然是UI相关的变化。

  • Presenter层纯逻辑业务的操作,包括在UI页面中由于任何的业务数据的获取、解析、转换等导致的UI状态变化的逻辑(业务数据可能是来自网络、本地数据库、文件、SharePref、页面传值等),在UI界面中的不限于if-else等条件判断语句导致的UI状态变化的逻辑(除UI控件本身的状态外,业务数据导致的),UI界面中其他任何不掺杂View层代码的纯逻辑代码(如一段纯算法逻辑的代码)。这部分代码的特点是接收UI输入的变化会导致业务数据的变化,而接收业务数据的输入也会导致UI的变化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值