Android好文收录Android开发终端研发部

MVP+Retrofit+Rxjava在项目中实战解析 

2017-07-19  本文已影响1030人  goodgleCoder

文章目标

MVP在android中的原理解析 MVP+Retrofit+Rxjava在项目中实战解析 架构经验分享

MVP简单介绍

先说说MVC分层:

MVP模式的核心思想

MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。

如下图所示,

MVP在真实项目中的实战

上面已经介绍过MVP的核心思想以及基本架构,当然我们在实际项目中不仅仅要把建构划分出来,还要加以延伸,这样才能够使项目的整体架构具备可扩展行、可复用性、可维护性、灵活性。下面我用我在实际项目中的角度来解析我所理解的MVP。

总体架构图:

Paste_Image.png

项目目录结构:

mvp2
1、View层
a、Iview接口代码如下:
/**
 * @Description MVP之V层 是所有VIEW的基类,其他类可以继承该类
 * @Author ydc
 * @CreateDate 2016/10/10
 * @Version 1.0
 */
public interface Iview<T> {

    /**
     * @description 全局的显示加载框
     * @author ydc
     * @createDate
     * @version 1.0
     */
    void showLoading();

    /**
     * @description 全局的显示加载框
     * @author ydc
     * @createDate
     * @version 1.0
     */

    ...
    /**
     * @description 当前fragment是否有效
     * @author ydc
     * @createDate
     * @version 1.0
     */
    boolean isActive();
    }

可以看出Iview 接口是所以activity 和fragment最基本且共有的方法定义。

b、NewsView接口代码如下:


/**ydc 新闻列表所特有的方法定义
 * Created by Administrator on 2017/7/6.
 */

public interface NewsView extends Iview {
    void addNews(List<NewsBean> newsList);
    void showLoadFailMsg(String msg);
}

NewsView接口继承自Iview接口,定义新闻列表特有的方法。

c、封装BaseActivity基类

BaseActivity作为所以activity的基类,你可以把所以activity共有的方法和属性提取到该中。

d、activity_main.xml布局文件代码如下:

布局里面仅仅放了一个RecyclerView,用来展示数据列表。

e、NewListActivity代码如下:


public class NewListActivity extends BaseActivity implements NewsView {

    private RecyclerView mRecyclerView;
    private LinearLayoutManager mLayoutManager;
    private NewsAdapter mAdapter;
    private List<NewsBean> mData;
    private int pageIndex = 0;


    private NewsPresenter mPresenter;

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

        mPresenter = new NewsPresenter();
        mPresenter.attachView(this);
        mPresenter.subscribe();
........
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mAdapter = new NewsAdapter(getApplicationContext());
        mRecyclerView.setAdapter(mAdapter);



    }
........
    @Override
    public void addNews(List<NewsBean> newsList) {
        mAdapter.isShowFooter(true);
        if(mData == null) {
            mData = new ArrayList<NewsBean>();
        }
        mData.addAll(newsList);
        if(pageIndex == 0) {
            mAdapter.setmDate(mData);
        } else {
            //如果没有更多数据了,则隐藏footer布局
            if(newsList == null || newsList.size() == 0) {
                mAdapter.isShowFooter(false);
            }
            mAdapter.notifyDataSetChanged();
        }
    }

    @Override
    public void showLoadFailMsg(String msg) {

    }
}

在NewListActivity 中我们可以看到,NewListActivity 显示实现了NewsView 接口,实现了NewsView和Iview 未实现的方法,在代码中可以看出NewListActivity并没有做一些逻辑处理工作,仅仅做了添加数据和展示数据以及一些提示消息等工作,数据处理的工作都是调用 NewsPresenter 完成的。

2、Presenter层

a、Ipresenter 代码如下:

/**
 * @Description MVP的P层
 * @Author ydc
 * @CreateDate 2016/10/10
 * @Version 1.0
 */
public interface Ipresenter<T extends Iview> {

    /**
     * @description 关联P与V(绑定,VIEW销毁适合解绑)
     * @author ydc
     * @createDate
     * @version 1.0
     */
    void attachView(T view);

    /**
     * @description 取消关联P与V(防止内存泄漏)
     * @author ydc
     * @createDate
     * @version 1.0
     */
    void detachView();

    /**
     * @description RX订阅
     * @author ydc
     * @createDate
     * @version 1.0
     */
    void subscribe();

    /**
     * @description RX取消订阅
     * @author ydc
     * @createDate
     * @version 1.0
     */
    void unsubscribe();

Ipresenter定义了所有presenter最基本且共有的方法。

b、BasePresenter代码如下:

/**
 * @Description 抽象的公用Presenter
 * @Author ydc
 * @CreateDate 20170707
 * @Version 1.0
 */
public abstract class BasePresenter<T extends Iview> implements Ipresenter<T> {
    protected T mMvpView;//所有View
    protected SubscriptionList mSubscriptions;//rx注册中心
    protected DataRepository mDataCenter;//数据中心
    //protected abstract SubscriptionList createSubscriptionList();//引入darger后取缔
  ......
    /**
     * @description p&v没有绑定的异常
     * @author ydc
     * @createDate
     * @version 1.0
     */
    public static class MvpViewNotAttachedException extends RuntimeException {
        public MvpViewNotAttachedException() {
            super("Please call Presenter.attachView(MvpView) before requesting data to the Presenter");
        }
    }

    /**
     * @description 统一添加订阅关联被观察者和观察者
     * @author ydc
     * @createDate
     * @version 1.0
     */
    public void addSubscription(Observable observable, Subscriber subscriber) {
        if( observable!=null && subscriber!=null ){
            if (mSubscriptions == null) {
                mSubscriptions = new SubscriptionList();
            }
            mSubscriptions.clear();
            mSubscriptions.add(observable
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(subscriber));
        }
    }

}

BasePresenter是一个abstract类,在实现Ipresenter的未实现的方法之外,又扩展了几个所以presenter共用的方法。

c、Presenter代码如下:


/**ydc 新闻类的协议也可以是接口
 * Created by Administrator on 2017/7/6.
 */
abstract class Presenter extends BasePresenter<NewsView> {
    public abstract void loadNews(int type, int page);
}

可以看出Presenter也是一个abstract类、继承自BasePresenter抽象类,同时定义了新闻列表所特有的方法。

d、NewsPresenter代码如下:

/**
 * Created by Administrator on 2017/7/6.
 */

public class NewsPresenter extends Presenter {

    private Model mModel;
    public  NewsPresenter(){
        mModel=new NewsModel();
    }

    @Override
    public void loadNews(int type, int page) {
        addSubscription(mModel.loadNews("nc/article/headline/T1348647909107/0-20.html",0), new ApiCallBack<NewsRequestModel>() {

            @Override
            public void onStart() {
                getMvpView().showLoading();
            }

            @Override
            public void onSuccess(NewsRequestModel modelBean) {
                if(modelBean!=null){
                    getMvpView().addNews(modelBean.getT1348647909107());
                }

            }

            @Override
            public void onFailure(String errorMsg) {
               getMvpView().showLoadFailMsg(errorMsg);
            }

            @Override
            public void onFinished() {
                getMvpView().hideLoading();
            }
        });
    }

    @Override
    public void subscribe() {

    }
}

可以看出NewsPresenter持有view和model的接口或是抽象类,起到中转的作用。

3、Model层

a、Imodel接口代码如下:

**
 * @Description MVP的M层
 * @Author ydc
 * @CreateDate 2016/10/10
 * @Version 1.0
 */
public interface Imodel {
}

我这里其实并没有做什么,只是留了一个接口而已,你可以定义所以model的基本方法。

b、BaseModel代码如下:

/**
 * @Description 数据模型基础类
 * @Author ydc
 * @CreateDate 2016/11/2
 * @Version 1.0
 */
public abstract class BaseModel implements Imodel {

    /**
     * @description 返回服务接口对象实例
     * @author ydc
     * @createDate
     * @version 1.0
     */
    public <T> T createService(final Class<T> clazz) {
        validateServiceInterface(clazz);
        return (T) RxService.RETROFIT.createRetrofit().create(clazz);
    }

    /**
     * @description 校验接口合法性
     * @author ydc
     * @createDate
     * @version 1.0
     */
    public <T> void validateServiceInterface(Class<T> service) {
        if (service == null) {
            //AppToast.ShowToast("服务接口不能为空!");
        }
        if (!service.isInterface()) {
            throw new IllegalArgumentException("API declarations must be interfaces.");
        }
        if (service.getInterfaces().length > 0) {
            throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
        }
    }

}

这个类我是用它来做Retrofit的初始化以及网络请求的工作,当然我要进一步包装Retrofit,所以这里只看到一个方法调用。

c、Model代码如下:

/**ydc 获取数据的逻辑模块协议,也可以是接口,提供给P调用,在callback中更新V
 * Created by Administrator on 2017/7/6.
 */

public  abstract class Model extends BaseModel {
   public abstract Observable<NewsRequestModel> loadNews(String url, int type);
}

既然公共BaseModel的职责任命为整个网络调用的工作,那么我就要在抽象一个Model抽象类来定义新闻数据处理逻辑模块协议,提供给P调用。

d、NewsModel代码如下:

/**ydc 新闻数据处理协议
 * Created by Administrator on 2017/7/6.
 */

public class NewsModel extends Model {
    private INewService service=createService(INewService.class);

    @Override
    public Observable<NewsRequestModel> loadNews(String url, int type) {
        Map<String, String> map = new HashMap<>();
        //map.put("type", type+"");
        return service.getNewList(url,map);
    }
}

这个类实现了Model作为具体的新闻列表数据处理层。

MVP总结:

RxJava 与 Retrofit 的结合简单介绍

@GET("/user")
public void getUser(@Query("userId") String userId, Callback<User> callback);

在程序的构建过程中, Retrofit 会把自动把方法实现并生成代码,然后开发者就可以利用下面的方法来获取特定用户并处理响应:

getUser(userId, new Callback<User>() {
    @Override
    public void success(User user) {
        userView.setUser(user);
    }

    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
};

其实 Retrofit传统的API调用与okHTTP功能和使用上没有什么本质的区别,它的强大之处在于与RxJava结合使用。

而使用 RxJava 形式的 API,定义同样的请求是这样的:

@GET("/user")
public Observable<User> getUser(@Query("userId") String userId);

使用的时候是这样的:

getUser(userId)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<User>() {
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }

        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
    });

看到区别了吗?

当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。

对比来看, Callback 形式和 Observable 形式长得不太一样,但本质都差不多,而且在细节上 Observable 形式似乎还比 Callback 形式要差点。那 Retrofit 为什么还要提供 RxJava 的支持呢?

单个请求体现不出它的优势所在,但是情景复杂起来, Callback 形式马上就会开始让人头疼。

假设 /user 接口并不能直接访问,而需要填入一个在线获取的 token ,代码应该怎么写?

Callback 方式,可以使用嵌套的 Callback:

GET("/token")
public void getToken(Callback<String> callback);

@GET("/user")
public void getUser(@Query("token") String token, @Query("userId") String userId, Callback<User> callback);

...

getToken(new Callback<String>() {
    @Override
    public void success(String token) {
        getUser(token, userId, new Callback<User>() {
            @Override
            public void success(User user) {
                userView.setUser(user);
            }

            @Override
            public void failure(RetrofitError error) {
                // Error handling
                ...
            }
        };
    }

    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
});

倒是没有什么性能问题,可是迷之缩进而且充满了无穷无尽的回调,这种后果你懂我也懂,做过大项目的人应该更懂。

而使用 RxJava 的话,代码是这样的:

@GET("/token")
public Observable<String> getToken();

@GET("/user")
public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId);

...

getToken()
    .flatMap(new Func1<String, Observable<User>>() {
        @Override
        public Observable<User> onNext(String token) {
            return getUser(token, userId);
        })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<User>() {
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }

        @Override
        public void onCompleted() {
        }

        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
    });

用一个 flatMap() 就搞定了逻辑,整个请求都在一条链当中。读者看到这里应该明白我为什么选择RxJava 与 Retrofit 的结合来处理网络请求。其实RxJava有两个比较核心的功能就是数据转换和线程调度,当然它还有其它的强大之处,只是我们用的最多的是这两个而已。

RxJava 与 Retrofit 的结合在本项目中的应用

1、创建RxService类代:

RxService类主要用来初始化Retrofit以及添加头部和系统参数,NewsModel初始化时,顺带完成了以上工作。

2、INewService接口代码如下:

/**网络接口
 * Created by Administrator on 2017/7/6.
 */

public interface INewService {

    @GET
    Observable<NewsRequestModel> getNewList(@Url String url,
                                            @QueryMap Map<String, String> params);
}

这个类是来定义新闻列表网络接口

3、管理被观察者和观察者

统一添加订阅关联被观察者和观察者

 protected SubscriptionList mSubscriptions;//rx注册中心


    /**
     * @description 统一添加订阅关联被观察者和观察者
     * @author ydc
     * @createDate
     * @version 1.0
     */
    public void addSubscription(Observable observable, Subscriber subscriber) {
        if( observable!=null && subscriber!=null ){
            if (mSubscriptions == null) {
                mSubscriptions = new SubscriptionList();
            }
            mSubscriptions.clear();
            mSubscriptions.add(observable
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(subscriber));
        }
    }

RX取消订阅代码如下:

@Override
    public void unsubscribe(){
        if(mSubscriptions!=null){
            mSubscriptions.clear();
        }
    }

以上两段代码是在BasePresenter抽象类中。

4、把请求添加到rx注册中心SubscriptionLis中

Paste_Image.png

5、NewsPresenter类
ApiCallBack是一个抽象类,处理网络数据处理完成后的回调响应(即RxJava的观察者),把它和被观察者作为参数一起传入到addSubscription方法中:

Paste_Image.png

6、NewsRequestModel代码如下

public class NewsRequestModel extends BaseFeed {
    public List<NewsBean> getT1348647909107() {
        return T1348647909107;
    }

    public void setT1348647909107(List<NewsBean> t1348647909107) {
        T1348647909107 = t1348647909107;
    }

    private List<NewsBean> T1348647909107;
}

该类继承自BaseFeed,作为新闻列表接口返回实体映射,这个需要和后台api接口开发人员协商好再定义。

作为所以接口返回实体映射基类,这个也需要和后台api开发人员协商好,至少我是这么做的。

RxJava 与 Retrofit 的结合小结

Demo现在地址:

[http://download.csdn.net/download/xinanheishao/9892674]

项目地址:

http://blog.csdn.net/xinanheishao/article/details/74732933

项目地址:

https://github.com/aa112901/remusic

如果你觉得此文对您有所帮助,欢迎入群 QQ交流群 :232203809
微信公众号:终端研发部

技术+职场

微信公众号:终端研发部

上一篇下一篇

猜你喜欢

热点阅读