Android中MVP模式的使用及内存泄漏原因分析(一)
正常情况
public class MainActivity extends AppCompatActivity implements RequestView{ @BindView(R.id.textView) TextView mTextView; @BindView(R.id.button) Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } public void clickRequest() { (new RetrofitUtils()) .getRetrofit("http://10.21.159.179:8080/") .create(LoginAPI.class) .login("136284008", "xbh") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<JSONObject>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(JSONObject jsonObject) { mTextView.setText(jsonObject.toString()); } }); } @OnClick(R.id.button) public void onClick(View v) { switch (v.getId()) { case R.id.button: clickRequest(); break; } }
就是最简单的点击Button,网络请求(采用rxjava+retrofit2),把获取的json或者错误信息显示在TextView中
现在用MVP来写
先写V层,它是需要让MainActivity去实现的
public interface RequestView { void requestLoading(); void resultSuccess(JSONObject object); void resultFailure(String errorMsg); }分别是请求中 请求完成 请求错误
在看M层
public class RequestModel { private static final String BASE_URL = "http://10.19.71.204:8080/"; public void loginRequest(String userName, String userPassword, Subscriber<JSONObject> subscriber) { (new RetrofitUtils()) .getRetrofit(BASE_URL) .create(LoginAPI.class) .login(userName, userPassword) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } }直接把我们的网络请求放在这里了。由于subscriber是rxjava+retrofit2的回调完成后的操作,所以也可以当成参数传。
再看看P层
public class RequestPresenter { private final RequestView mRequestView; private final RequestModel mRequestModel; public RequestPresenter(RequestView mRequestView) { this.mRequestView = mRequestView; this.mRequestModel = new RequestModel(); } public void clickRequest(String userName, String userPassword) { mRequestView.requestLoading(); mRequestModel.loginRequest(userName, userPassword, new Subscriber<JSONObject>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { mRequestView.resultFailure(e.getMessage()); } @Override public void onNext(JSONObject object) { mRequestView.resultSuccess(object); } }); } }由于它是连接者嘛,所以等于是连接业务和UI的纽带,所以他内部会维护M层和C层。
现在再来看一下我们的MainActivity,已经面目全非了
public class MainActivity extends AppCompatActivity implements RequestView{ @BindView(R.id.textView) TextView mTextView; @BindView(R.id.button) Button mButton; private RequestPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); presenter = new RequestPresenter(this); } @OnClick(R.id.button) public void onClick(View v) { switch (v.getId()) { case R.id.button: presenter.clickRequest("136284008", "xbh"); break; } } private ProgressDialog dialog; @Override public void requestLoading() { dialog = ProgressDialog.show(this, "提示", "正在登陆中", false, true); } @Override public void resultSuccess(JSONObject object) { mTextView.setText(object.toString()); dialog.cancel(); } @Override public void resultFailure(String errorMsg) { mTextView.setText(errorMsg); dialog.cancel(); } }仅仅只剩下了点击事件和完成点击事件后的回调操作了,使得你想做业务就专门做业务,UI和业务完全分离,舒服诶。
最后谈一下为什么会内存泄漏。
假如说网速很卡,你这个请求请求了10秒,但是在这10秒的过程中,我们直接把这个activity给关了,但是Presenter依然持有View的引用
public RequestPresenter(RequestView mRequestView) { this.mRequestView = mRequestView; this.mRequestModel = new RequestModel(); }
注意哦!P层的构造方法里是传了一个V层的引用进来,也就是说P层一直握住V层的地址。而且是强引用,所以activity对象在堆栈区中就根本无法释放了!因为还有人在用!解决方法是在onDestory中增加解绑操作即可。后面会讲。(可能有人会问M层怎么不会也内存泄漏。好吧,如果你能让M层的类突然终止,那么他这个情况肯定也就 内存泄漏了)
最后解析下调用流程
v层 clickrequest
public void clickRequest(String userName, String userPassword) { mRequestView.requestLoading(); mRequestModel.loginRequest(userName, userPassword, new Subscriber<JSONObject>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { mRequestView.resultFailure(e.getMessage()); mRequestView.resultFinish(); } @Override public void onNext(JSONObject object) { mRequestView.resultSuccess(object); mRequestView.resultFinish(); } }); }所以会按顺序执行。其中第loding个和第finish个(我来我新加的,完成回调)是直接就执行他自己方法。而success,failure涉及到网络请求,就先执行了M层,扔进去一个回调。这里就相当于接口回调了。当M层的代码执行到subscriber的时候
public void loginRequest(String userName, String userPassword, Subscriber<JSONObject> subscriber) { (new RetrofitUtils()) .getRetrofit(BASE_URL) .create(LoginAPI.class) .login(userName, userPassword) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); }就执行这个回调了。subscriber可以看成是 接口.方法名。而我们传进去的就可以看成是一个接口new 接口{方法名()}。
github地址:https://github.com/xubinhong/MVP2