Android MVP-编程思想3(MVP-内存泄露问题处理,基类封装,有没有必要再使用软引用?)
Posted 0 and 1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android MVP-编程思想3(MVP-内存泄露问题处理,基类封装,有没有必要再使用软引用?)相关的知识,希望对你有一定的参考价值。
前言
通过一个小的登录功能模块案例,帮助大家了解MVP。最终实现一个结合Rxjava2,Retrofit 的MVP通用框架。代码放到github上。(如有错误之处,请在评论区指出,谢谢。如果感觉写的不错,请点赞,关注,谢谢。)
目录:
Android MVP-编程思想1(什么是MVC-MVP-MVVM模式?)
Android MVP-编程思想2(代码实现初级版)
Android MVP-编程思想3(内存泄露问题处理,基类封装,有没有必要再使用软引用?)
Android MVP-编程思想4(AOP思想-动态代理运用,反射创建M层实例对象)
Android MVP-编程思想5(如何处理多个P层的问题?)
Android MVP-编程思想6(依赖注入多个P层优化—注解,反射)
Android MVP-编程思想7(为什么使用代理类抽取通用方法而不是工具类?,基类BaseMvpFragment)
未完待续--------
android MVP-编程思想8(集成Rxjava2,Retrofit)
本篇是接着上一篇写的。但是并不需要按照顺序阅读,对MVP已经有一定理解和实践的可以直接阅读。如果没有接触过MVP请先阅读第一小节
这一节主要实现两个小目标,1.内存泄露问题处理,2.基类抽取封装
第一个问题,内存泄露,为什么会内存泄露?
Activity又持有P层的引用,P持有V层(Activity)的强引用,循环引用了(所以即使在onDestory中设置mPresenter=null 也不会被回收,因为P还持有了V的引用)。当用户点击返回按钮,关闭Activity时,由于Activity 被强引用了,所以就不会去回收它,导致内存泄露。当你再次打开 Activity 又关闭时,Activity 又申请了一段新的内存空间,GC 又没去回收它,久而久之,势必会内存溢出。
如何解决内存泄露?
这里我们就在Activity需要回收的时候,断开循环强引用不就行了。看代码如下,注入view的方法attachView,释放view的方法detachView。然后再Activity的onDestory() 方法中调用P层的detachView,释放mView,来断开循环引用链,在释放mPresenter=null。继续看就知道了
P层基类抽取BasePresenter (多个P的情况后面讲)
每个P都有V,M的引用,可以抽取到父类,由于没有实现类的V,M不同,所以使用泛型。P持有的V的引用使用attachView 注入,P持有M引用,通过抽象方法createModel注入。detachView解决循环引用,内存无法释放问题。
public abstract class BasePresenter<V, M>
protected V mView;
protected M mModel;
/**
* 注入view
*
* @param view
*/
protected void attachView(final V view)
mView = view;
mModel = createModel();
/**
* 释放View
*/
protected void detachView()
mView = null;
mModel = null;
protected abstract M createModel();
有没有必要使用软引用
有一些人说没必要了,觉得我们已经使用detachView 的方式处理了内存泄露问题了。其实你有没有考虑过某些情况下Activity的onDestroy方法不会执行?所以这才是使用软引用的原因。
public abstract class BasePresenter<V, M>
private SoftReference<V> mViewReference;
protected V mView;
protected M mModel;
/**
* 注入view
*
* @param view
*/
protected void attachView(final V view)
mViewReference = new SoftReference<>(view);
mView = mViewReference.get();
mModel = createModel();
/**
* 释放View
*/
protected void detachView()
mViewReference.clear();
mViewReference = null;
mView = null;
mModel = null;
protected abstract M createModel();
抽取基类BaseMvpActivity
为什么叫BaseMvpActivity? 为了和BaseMvcActivity区别,BaseMvcActivity的抽取后面几节会贴上。大家也看到了Mvp有个缺点就是需要写很多接口。所以一些简单的功能模块我们还是使用Mvc模式开发更快。在一个应用中根据模块的复杂度同时使用Mvc和Mvp模式才是正解。
每个V层都持有P的引用,(多个P层的情况到后面讲,我们一步一步来),抽取到基类,P的实现类不同,使用泛型。提供createPresenter()抽象方法注入P。解决内存泄露:onDestroy方法先调用 mPresenter.detachView();释放V,然后mPresenter=null释放P。
public abstract class BaseMvpActivity<P extends BasePresenter> extends AppCompatActivity
protected P mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
View rootView;
final Object layout = getLayout();
if (layout instanceof Integer)
rootView = View.inflate(this, (int) getLayout(), null);
else if (layout instanceof View)
rootView = (View) getLayout();
else
throw new ClassCastException("type of setLayout() must be int or View!");
setContentView(rootView);
mPresenter = createPresenter();
mPresenter.attachView(this);
afterCreate();
@Override
protected void onDestroy()
super.onDestroy();
mPresenter.detachView();
mPresenter = null;
@SuppressWarnings("ConstantConditions")
protected <T extends View> T $(@IdRes int viewId)
return findViewById(viewId);
public void showProgress(boolean isShow)
if (isShow)
Log.i("mvp_", "显示等待框");
else
Log.i("mvp_", "隐藏等待框");
public void showToast(String msg)
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
protected abstract Object getLayout();
protected abstract P createPresenter();
protected abstract void afterCreate();
抽取基类IBaseView
每个V层都有下面两个行为,所以这里做一个基类
public interface IBaseView
//请求网络显示等待框
void showProgress(boolean isShow);
//显示提示框-登录失败,或其他提示信息
void showToast(String string);
其它相关类的变化
由于抽取类IBaseView,这里变化了LoginContract类,不多解释,懂得都懂
interface IView extends IBaseView
//登录成功跳转到主界面
void goToMainActivity();
由于抽取了BasePresenter,所以LoginPresenter变化如下
public class LoginPresenter extends BasePresenter<LoginContract.IView, LoginContract.IModel> implements LoginContract.IPresenter
@Override
protected LoginContract.IModel createModel()
return new LoginModel();
@Override
public void onClickLogin(String username, String pwd)
mView.showProgress(true);
mModel.getUserInfo(username, pwd, new ICallBack<String>()
@Override
public void success(String data)
if (mView != null)
mView.showToast("登录成功");
mView.showProgress(false);
mView.goToMainActivity();
@Override
public void error(String error)
if (mView != null)
mView.showProgress(false);
mView.showToast("登录失败");
);
由于抽取了BaseMvpActivity,所以LoginActivity变化如下
public class LoginActivity extends BaseMvpActivity<LoginPresenter> implements LoginContract.IView
@Override
protected Object getLayout()
return R.layout.activity_main;
@Override
protected LoginPresenter createPresenter()
return new LoginPresenter();
@Override
protected void afterCreate()
$(R.id.btn_login).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
mPresenter.onClickLogin("xxx", "xxx");
);
@Override
public void goToMainActivity()
runOnUiThread(new Runnable()
@Override
public void run()
//todo 登录成功跳转到主界面
Log.i("mvp_", "登录成功跳转到主界面");
);
@Override
public void showProgress(boolean isShow)
if (isShow)
Log.i("mvp_", "显示等待框");
else
Log.i("mvp_", "隐藏等待框");
@Override
public void showToast(String str)
Toast.makeText(this, str, Toast.LENGTH_LONG).show();
这一节讲解了内存泄露的原因以及解决方案,还有基类的封装抽取,复用。但是还是不够完美,比如,每次调用mView之前需要进行非空判断。这个重复的逻辑能不能做统一封装?这个问题留到下一节处理。
以上是关于Android MVP-编程思想3(MVP-内存泄露问题处理,基类封装,有没有必要再使用软引用?)的主要内容,如果未能解决你的问题,请参考以下文章
Android MVP-编程思想4(AOP思想-动态代理运用,反射创建M层实例对象)
Android MVP-编程思想5(如何处理多个P层的问题?)
Android MVP-编程思想5(如何处理多个P层的问题?)