您的位置:首页 > 运维架构 > 网站架构

MVP模式项目架构:泛型高度解耦且避免Activity内存泄漏

2017-11-07 11:27 288 查看

转载请标明出处: http://blog.csdn.net/u012430727/article/details/78466123[/code] 
写在前面:

以前总是在网上查找资料及其各位大神分享的博客文章,现在自己也要开始写一些笔记、记录或者分享一些有用的知识,希望对你有所帮助。以后一会坚持写一些有用的文章,多谢支持!由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出,谢谢大家:)

MVP模式简介:

简称:MVP 全称:Model-View-Presenter ;MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。
————百度百科

正文

为什么写这篇文章分享给大家

在此之前也看过一些大神分享的MVP模式文章,但是呢,发现他们理解的mvp和设计出来的架构和我的不太一样,可能各有所见吧。希望通过我的这篇文章能够加深你对mvp模式设计思想的不同理解,若有不对之处请大家留言指出,请收下留情哈。

主要参考资料:

灵活利用泛型的MVP模式T-MVP : http://blog.csdn.net/z2wenfa/article/details/52873009
T-MVP:泛型深度解耦下的MVP大瘦身:http://www.jianshu.com/p/b49958e1889d

区别:

这两篇文章介绍的MVP模式的思想及其架构如图:



这种设计模式的架构方式通过泛型一对一固定死了一个Model,如果想要多个model只能自己重新在对应的Presenter中添加,
并且代码实现中有2弊端:
1、Model和Presenter的实例化通过反射,虽然更自动化一点,但也使其需要在打包时设置不混淆相关代码;
2、Activity也拥有了model的实例,虽然也可以设置私有,但是不建议这么做。

当然也不是说别人的设计就不好,每个人的理解不一样,所以各位大大结合自己的理解设计出自己的MVP架构吧,如果你们有更好的想法可以分享一下给我吧。

我的理解:

MVC和MVP的结构就不讲了,网上很多,简单说一下。
MVC:

view ---- controller ---- model

MVP:

view ---- presenter ---- model

MVP相对于MVC的优点:

1、Activity职责更加明确(UI逻辑和使用哪个表示层);

2、代码结构更清楚,更容易维护;

3、方便单元测试;

4、避免Activity的内存泄漏;

MVP模式的理解:
1、View层职责更加明确 —— 负责UI逻辑相关和使用哪个表示层Presenter,不应持有Model的引用;

2、Presenter表示层 —— 负责业务逻辑,可以使用多个Model,持有一个View层引用和多个Model层引用,为什么需要多个Model层呢,因为你的每个Model层负责的数据来源可能不一样,当然你也可以像google使用MVP开发的官方demo设计方式,设计一个接口,2个Data来源实现类,另一个Data数据来源仓库实现类管理另外2个的加载数据逻辑;

3、Model层: 数据层 - 负责数据的来源(加载)。

如何在MVP中避免Activity的内存泄漏:
       大家可能会想到使用弱引用方式实现,当然这个也可以实现的,但是弱引用的对象会在垃圾收集器在某个时间点决定一个对象是弱可达的(weakly reachable)(也就是说当前指向它的全都是弱引用),这时垃圾收集器会清除所有指向该对象的弱引用,然后把这个弱可达对象标记为可终结(finalizable)的,这样它随后就会被回收,这样做是可以的,但是我们可以做到更好的避免Activity的内存泄漏,不能总是等待GC来回收吧,所以需要主动释放,因此我设计在Activity的生命周期中进行主动释放的,具体实现往下看吧。

我所设计的MVP模式的思想及其架构如图:



我这里去掉了一层契约Contract接口,当然你们也可以在实现的时候自行按需是否需要吧。

项目结构图:



接下来直接贴实现代码: (因为注释都写的比较清楚,并且代码规范符合阿里Java规约)

1、View层接口
package com.lh.mvparchitecture.mvp.base;

/**
* UI层 - 负责UI逻辑,实现类自己实现
* <p>
* 当然你也可以加上一些共有的方法,让实现类必须实现
*
* @author LuoHao
*         Created on 2017/11/6 11:26
*/
public interface BaseView {
}


2、Presenter层接口
package com.lh.mvparchitecture.mvp.base;

import java.lang.ref.WeakReference;

/**
* 表示层 - 负责业务逻辑
*
* @author LuoHao
*         Created on 2017/11/6 11:25
*/
public abstract class BasePresenter<V> {

/**
* UI层的引用,使用弱引用并且配合{@link #attachView(Object)}
* 和{@link #detachView()}两个方法根除Activity的内存泄漏
* 注意:使用时推荐最好调用{@link #getBaseView()}方法获取UI层的引用,以免造成内存泄漏
* <p>
* 使用泛型解耦
*/
protected WeakReference<V> mBaseViewWeakReference;

/**
* 无参构造
*/
public BasePresenter() {
}

/**
* 子类获取BaseView对象
* 注意:获取后应该判断是否为空,并且不要使用缓存BaseView实例变量,不然会造成内存泄漏,
* 应直接调用该方法获取
*
* @return 获取BaseView对象
*/
protected V getBaseView() {
return mBaseViewWeakReference != null && mBaseViewWeakReference.get() != null ?
mBaseViewWeakReference.get() : null;
}

/**
* 关联View对象
*
* @param baseView View层实例
*/
public void attachView(V baseView) {
this.mBaseViewWeakReference = new WeakReference<>(baseView);
}

/**
* 取消关联View
*/
public void detachView() {
if (mBaseViewWeakReference != null) {
mBaseViewWeakReference.clear();
}
}
}


3、Model层
package com.lh.mvparchitecture.mvp.base;

/**
* 数据层 - 负责数据的来源(加载)
*
* @author LuoHao
*         Created on 2017/11/6 11:25
*/
public interface BaseModel {
}


4、MVP 中model加载请求后的监听结果回调接口
使用泛型进行数据解耦
package com.lh.mvparchitecture.mvp.base;

/**
* MVP 中model加载请求后的回调监听结果
*
* @author LuoHao
*         Created on 2017/11/6 13:33
*/
public interface BaseCallback<M> {

/**
* 加载成功
*
* @param m 返回的数据类型,使用泛型解耦
* @author LuoHao
* Created on 2017/11/6 14:00
*/
void onSuccess(M m);

/**
* 加载失败
* <p>
* 这里的参数可根据具体后台返回情况设置
*
* @param e   失败异常exception
* @param msg 加载失败原因 这里的msg一般会是后台返回的那个值
* @author LuoHao
* Created on 2017/11/6 14:00
*/
void onFail(Exception e, String msg);
}


5、封装的一个 MVP模式 Activity 基类
package com.lh.mvparchitecture.mvp.base;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

/**
* MVP模式 Activity 基类
*
* @author LuoHao
*         Created on 2017/11/6 11:20
*/
public abstract class BaseActivity<V extends BaseView, P extends BasePresenter<V>> extends AppCompatActivity {

/**
* 表示层引用
*/
public P mPresenter;

@SuppressWarnings("unchecked")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();
if (mPresenter != null) {
mPresenter.attachView((V) this);
}
}

/**
* 获取创建的Presenter对象,让子类去指定一个表示层
*
* @return 创建的Presenter对象
* @author LuoHao
* Created on 2017/11/6 14:02
*/
protected abstract P createPresenter();

@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.detachView();
}
}
}


6、封装的一个MVP模式 Fragment 基类
package com.lh.mvparchitecture.mvp.base;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;

/**
* MVP模式 Fragment 基类
*
* @author LuoHao
*         Created on 2017/11/6 13:05
*/
public abstract class BaseFragment<V extends BaseView, P extends BasePresenter<V>> extends Fragment {

/**
* 表示层引用
*/
public P mPresenter;

@SuppressWarnings("unchecked")
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();
if (mPresenter != null) {
mPresenter.attachView((V) this);
}
}

/**
* 获取创建的Presenter对象,让子类去指定一个表示层
*
* @return 创建的Presenter对象
* @author LuoHao
* Created on 2017/11/6 14:02
*/
protected abstract P createPresenter();

@Override
public void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.detachView();
}
}
}


具体实现使用:

7、Main主界面UI层 接口- 负责UI逻辑
package com.lh.mvparchitecture.mvp.ui.main;

import com.lh.mvparchitecture.bean.Person;
import com.lh.mvparchitecture.mvp.base.BaseView;

import java.util.List;

/**
* 主界面UI层 - 负责UI逻辑
*
* @author LuoHao
*         Created on 2017/11/6 13:36
*/
public interface MainView extends BaseView {

/**
* 显示数据
*
* @param list list data
* @author LuoHao
* Created on 2017/11/6 14:35
*/
void showData(List<Person> list);

/**
* 显示msg
*
* @param msg msg
* @author LuoHao
* Created on 2017/11/6 14:03
*/
void showMsg(String msg);
}


8、main界面 表示层实现  - 负责业务逻辑
package com.lh.mvparchitecture.mvp.ui.main;

import com.lh.mvparchitecture.bean.Person;
import com.lh.mvparchitecture.mvp.base.BaseCallback;
import com.lh.mvparchitecture.mvp.base.BasePresenter;

import java.util.List;

/**
* main界面 表示层 - 负责业务逻辑
*
* @author LuoHao
*         Created on 2017/11/6 14:11
*/
public class MainPresenter extends BasePresenter<MainView> {

/**
* model数据层的引用,使用泛型
*/
private MainModel mMainModel;

/**
* 构造函数
* 初始化一些model,可有多个model
*/
public MainPresenter() {
mMainModel = new MainModel();
}

/**
* 加载数据
*
* @author LuoHao
* Created on 2017/11/6 14:03
*/
public void loadData() {
mMainModel.loadData(new BaseCallback<List<Person>>() {
@Override
public void onSuccess(List<Person> list) {
if (getBaseView() != null) {
getBaseView().showData(list);
getBaseView().showMsg("加载成功");
}
}

@Override
public void onFail(Exception e, String msg) {
if (getBaseView() != null) {
e.printStackTrace();
getBaseView().showMsg(msg);
}
}
});
}
}


9、main 界面的数据层 - 负责数据的来源(加载)
package com.lh.mvparchitecture.mvp.ui.main;

import com.lh.mvparchitecture.bean.Person;
import com.lh.mvparchitecture.mvp.base.BaseCallback;
import com.lh.mvparchitecture.mvp.base.BaseModel;

import java.util.ArrayList;
import java.util.List;

/**
* main 界面的数据层 - 负责数据的来源(加载)
*
* @author LuoHao
*         Created on 2017/11/6 13:31
*/
public class MainModel implements BaseModel {

/**
* 加载数据 - 这里只是模仿网络请求,具体实现可根据自己的业务实现
*
* @param callback 数据加载后的回调
* @author LuoHao
* Created on 2017/11/6 14:03
*/
public void loadData(BaseCallback<List<Person>> callback) {
// list data
final List<Person> mListData = new ArrayList<>();
try {
Thread.sleep(3000);

// 加载数据
mListData.add(new Person("Rabbit", "10"));
mListData.add(new Person("Joke", "20"));
mListData.add(new Person("Tiger", "30"));
mListData.add(new Person("LuoHao", "40"));

mListData.add(new Person("Rabbit2", "10"));
mListData.add(new Person("Joke2", "20"));
mListData.add(new Person("Tiger2", "30"));
mListData.add(new Person("LuoHao2", "40"));

// 加载数据成功
callback.onSuccess(mListData);

} catch (Exception e) {
e.printStackTrace();

String msg = "加载失败";
// 加载数据失败
callback.onFail(e, msg);
}
}

}


10、最后主界面MainActivity简单实现类:
package com.lh.mvparchitecture.mvp.ui.main;

import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

import com.lh.mvparchitecture.R;
import com.lh.mvparchitecture.base.CommonAdapter;
import com.lh.mvparchitecture.base.CommonVHolder;
import com.lh.mvparchitecture.bean.Person;
import com.lh.mvparchitecture.mvp.base.BaseActivity;

import java.util.ArrayList;
import java.util.List;

/**
* 主界面
*
* @author LuoHao
*         Created on 2017/11/6 10:06
*/
public class MainActivity extends BaseActivity<MainView, MainPresenter> implements MainView {

/**
* listView
*/
private ListView mListView;
/**
* adapter
*/
private CommonAdapter<Person> mListAdapter;
/**
* list data
*/
private List<Person> mListData = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 初始化
initViews();
initListAdapter();
setListeners();
loadData();

//        initData();
}

@Override
protected MainPresenter createPresenter() {
return new MainPresenter();
}

/**
* 加载数据
*
* @author LuoHao
* Created on 2017/11/6 14:37
*/
public void loadData() {
mPresenter.loadData();
}

/**
* 加载成功后显示数据
*
* @param list list data
* @author LuoHao
* Created on 2017/11/6 14:40
*/
@Override
public void showData(List<Person> list) {
if (list != null && list.size() > 0) {
mListData.addAll(list);
// 刷新界面
mListAdapter.notifyDataSetChanged();
}
}

/**
* 显示提示msg
*
* @param msg msg
* @author LuoHao
* Created on 2017/11/6 14:41
*/
@Override
public void showMsg(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}

//    /**
//     * 初始化数据
//     */
//    private void initData() {
//        mListData.add(new Person("Rabbit", "10"));
//        mListData.add(new Person("Joke", "20"));
//        mListData.add(new Person("Tiger", "30"));
//        mListData.add(new Person("LuoHao", "40"));
//
//        mListData.add(new Person("Rabbit2", "10"));
//        mListData.add(new Person("Joke2", "20"));
//        mListData.add(new Person("Tiger2", "30"));
//        mListData.add(new Person("LuoHao2", "40"));
//
//        mListAdapter.notifyDataSetChanged();
//    }

/**
* 初始化View
*
* @author LuoHao
* Created on 2017/11/6 10:38
*/
private void initViews() {
mListView = findViewById(R.id.listView);
}

/**
* 初始化ListView Adapter
*
* @author LuoHao
* Created on 2017/11/6 10:51
*/
private void initListAdapter() {
mListAdapter = new CommonAdapter<Person>(this, mListData, R.layout.item_card_main) {
@Override
protected void bindView(CommonVHolder holder, Person model, int position, View convertView, ViewGroup parent) {
holder.setText(R.id.mTv_msg, model.name + "\n如果我有机器猫,变大变小变漂亮");
}
};
mListView.setAdapter(mListAdapter);
}

/**
* 设置监听事件
*
* @author LuoHao
* Created on 2017/11/6 10:51
*/
private void setListeners() {
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this,
"You click item name is :" + mListData.get(position).name,
Toast.LENGTH_SHORT).show();
}
});
}

}


好了,终于搞好了。
我这里就只是封装了一个Activity和Fragment基类,没有封装进其它第三方框架进来如DataBinding也可以与MVP相结合的,具体实现各位大大有更好的想法可自行发挥哈:)

总结

关于MVP的资料网上很多,也希望我这篇文章能够对大家更好的实现MVP项目架构有所帮助。

如果对你有所帮助,各位大大给个赞吧,也希望以后会有更好的文章技术分享给大家,╰( ̄ω ̄o)下次见面啦。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: