RxJava简单使用、模仿访问服务器获取数据更新界面-基于mvp架构

原创 2016年08月29日 15:30:49

先看下项目结构目录-基于mvp架构


RxJava是一个实现Java响应式编程的库,让异步事件以序列的形式组织。MVP则通常用来将View业务层与Model层分离开来,两者结合起来可轻松实现业务解耦、线程控制、单元测试等等强大功能


目标:

假设有一个从服务端获取字符串并显示的手机上的简单功能。


这是一个比较典型的MVP结构图(图片来自参考文献),相比于mvc,多了两个层,一个是Presenter和DataManager层。


Activity里面包含了几个文件,一个是View层的对外接口MainView,一个是P层的Presenter。

package test.ban.com.mvp;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

import test.ban.com.mvp.presenter.MainPresenter;
import test.ban.com.mvp.view.MainView;

public class MainActivity extends Activity implements MainView {

    /**
     * View层通过注册Listener
     * 将自己的接口MainView(test.ban.com.mvp.view.MainView)交给了Presenter(P层)
     *
     * ===============白话解说:==========
     * 就是MainActivity实现了View的接口MainView,
     * 然后重写了MainView的方法onShowString()
     * 让mTextView的显示内容实现了相应的改变
     * ================================
     */

    /**
     * 抛出问题????:
     * MainActivity、MainView、MainPresenter三个文件可以看到,
     * View层通过注册Listener将自己的接口MainView交给了Presenter,
     * 而Presenter层持有Model层的也只是一个接口。
     * 通过Presenter层将业务层与展现层隔离了开来,这样的好处是什么?
     * ======================================
     * <p>
     * -------------====答案====--------------
     * 我们知道接口的一个作用通常是用来抽象行为,对外部屏蔽实现细节。
     * 所以对于View层来说,业务细节被屏蔽了,对业务层来说,展示细节被屏蔽了。
     * 而对于处于中间的Presenter层来说,它就像一个接口拼装器,把View层发出的请求传递给业务层,
     * 把业务层返回的数据又送还给View层展示,至于前后两端怎么实现的,它才不用关心。
     * --------------------------------------
     */

    TextView mTextView;
    MainPresenter mMainPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.tv);
        loadDatas();
    }

    private void loadDatas() {
        mMainPresenter = new MainPresenter().addTaskListener(this);

        /**
         * 两种获得数据的方式
         *
         * RxJava的使用场景远不止这些,线程变换、数据变换、接口顺序依赖、接口并发请求这些要求对它来说都是小菜一碟。
         * 当然,有些同学可能觉得RxJava入手有些困难,代码也会变得不那么直观,但相信只要大家慢慢熟悉它之后,它就会变得无比讨人喜欢。
         *
         * 下面列出了一些常见的RxJava的常用场景,其实还有更多的其它功能等待着大家去挖掘。
         * * 1、取数据先检查缓存的场景
         * * 2、需要等到多个接口并发取完数据,再更新
         * * 3、一个接口的请求依赖另一个API请求返回的数据
         * * 4、界面按钮需要防止连续点击的情况
         * * 5、响应式的界面
         * * 6、复杂的数据变换
         */
        //1、传统写法获得数据
        //mMainPresenter.getData();

        //2、通过Rxjava获得数据(推荐使用)
        //mMainPresenter.getDataUseRxJava();

        //2-1、Rxjava强化写法 获得数据
        mMainPresenter.getDataUseRxJavaWithCache();

    }

    //因为这个功能比较简单,只需要在设备上显示一个字符串,所以只有一个接口方法onShowString()
    @Override
    public void onShowString(String json) {
        mTextView.setText(json);
    }
}

View层的对外接口文件

package test.ban.com.mvp.view;

/**
 * Created by apple on 16/8/29.
 */

public interface MainView {
    //因为这个功能比较简单,只需要在设备上显示一个字符串,所以只有一个接口方法onShowString()
    void onShowString(String json);
}

MainPresenter层 (p层)

package test.ban.com.mvp.presenter;/**
 * Created by apple on 16/8/29.
 */

import android.os.Handler;
import android.os.Looper;

import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
import test.ban.com.mvp.data.DataSource;
import test.ban.com.mvp.data.impl.DataSourceImpl;
import test.ban.com.mvp.data.impl.DataSourceTestImpl;
import test.ban.com.mvp.view.MainView;

/**
 * 作者:ban on 16/8/29 14:17
 */
public class MainPresenter {

    MainView mMainView;
    DataSource mDataSource;

    public MainPresenter() {
        this.mDataSource = new DataSourceImpl();
    }

    public MainPresenter test() {
        this.mDataSource = new DataSourceTestImpl();
        return this;
    }

    public MainPresenter addTaskListener(MainView viewListener) {
        this.mMainView = viewListener;
        return this;
    }

    /**
     * 很多同学可能已经发现了,Presenter层在调用业务层的时候是直接调用的,
     * 而Android规定,主线程是无法直接进行网络请求,会抛出NetworkOnMainThreadException异常。
     * <p>
     * 所以在presenter层,我们需要进行一项线程切换的工作,这样才能保证“所有的IO操作都应当在线程中完成,
     * 主线程只负责页面渲染的工作”这一优化准则。当然,Android本身提供一些方案,比如下面这种:
     */

    /**
     * 写法一:
     * 通过新建子线程进行IO读写获取数据,然后通过主线程的Looper将结果通过传回主线程进行展示,这种方案是勉强也行得通的。
     * <p>
     * 但问题有以下两点:
     * 一是线程需要额外管理,不可能每次发请求都要开启一个线程;
     * 二是适应性差,假如数据请求有先后依赖,有并行的情况,这样的写法变得脏乱无比。
     */

    public void getData() {
        final Handler mainHandler = new Handler(Looper.getMainLooper());
        new Thread(new Runnable() {
            @Override
            public void run() {
                final String str = mDataSource.getStringFromRemote();
                mainHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mMainView.onShowString(str);
                    }
                });
            }
        }).start();
    }

    /**
     * 写法二: 使用RxJava来进行线程控制
     * RxJava是一个天生用来做异步的工具,相比AsyncTask,Handler等,它的优点就是简洁,无比的简洁。
     * 在Android中使用RxJava需要加入下面两个依赖。
     * compile 'io.reactivex:rxjava:1.0.14'
     * compile 'io.reactivex:rxandroid:1.0.1'
     */

    public void getDataUseRxJava() {

        final Func1<String, String> dataAction = new Func1<String, String>() {
            @Override
            public String call(String s) {
                return s + mDataSource.getStringFromCache() + mDataSource.getStringFromRemote();
            }
        };

        Action1<String> viewAction = new Action1<String>() {
            @Override
            public void call(String s) {
                mMainView.onShowString(s);
            }
        };

        /**
         * Observable.just就是用来创建只发出一个事件就结束的Observable对象,就是发送了一个消息"Rxjava"
         *
         * ========:简单说明:========
         * dataAction是我们的数据业务逻辑,viewAction是界面的显示逻辑,
         * 通过RxJava的传递和变换,
         * dataAction会在由RxJava管理的IO线程–Schedulers.io() 中执行,
         * 而viewAction则会在UI线程–AndroidSchedulers.mainThread()中执行。
         */
        Observable.just("Rxjava")
                .observeOn(Schedulers.io())
                .map(dataAction)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(viewAction);
    }


    /**
     * RxJava当然不止这么简单,还有别的玩法,
     * 比方说进入一个界面的时候,需要先加载缓存的数据,然后再从网络获取更新的数据进行刷新。
     * 有的时候,可能还需要处理IO过程中的异常情况,加入RxJava的异常处理参数。
     */
    public void getDataUseRxJavaWithCache() {
        Func1<Boolean, String> dataAction = new Func1<Boolean, String>() {
            @Override
            public String call(Boolean isCache) {
                if (isCache) {
                    return mDataSource.getStringFromCache();
                } else {
                    return mDataSource.getStringFromRemote();
                }
            }
        };

        Action1<String> viewAction = new Action1<String>() {
            @Override
            public void call(String s) {
                mMainView.onShowString(s);
            }
        };

        Observable.just(false)
                .observeOn(Schedulers.io())
                .map(dataAction)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(viewAction, getDefaultErrorAction());

    }

    private Action1<Throwable> getDefaultErrorAction() {
        return new Action1<Throwable>() {
            @Override
            public void call(Throwable throwable) {
                throwable.printStackTrace();
            }
        };
    }


data层

DataSource

package test.ban.com.mvp.data;

/**
 * Created by apple on 16/8/29.
 */

public interface DataSource {

    /**
     * 接口的第二个作用是可以用来切换实现。
     *
     * 从DataSource、DataSourceImpl和DataSourceTestImpl)
     * 三个文件可以看到,业务层对外的只有一个接口,实现却有两个(DataSourceImpl和DataSourceTestImpl)。
     * 从名字大家就能看出来有什么作用了,一个是正常环境的业务层实现,一个是测试环境的业务层实现。
     */

    String getStringFromRemote();
    String getStringFromCache();
}
DataSourceImpl

package test.ban.com.mvp.data.impl;/**
 * Created by apple on 16/8/29.
 */

import test.ban.com.mvp.data.DataSource;

/**
 * 作者:ban on 16/8/29 14:13
 */
public class DataSourceImpl implements DataSource {
    @Override
    public String getStringFromRemote() {
        String str = "remote hello";
        return str;
    }

    @Override
    public String getStringFromCache() {
        return "World";
    }
}

DataSourceTestImpl(测试类,在需要的时候模仿生产环境返回进行测试)

package test.ban.com.mvp.data.impl;/**
 * Created by apple on 16/8/29.
 */

import test.ban.com.mvp.data.DataSource;

/**
 * 作者:ban on 16/8/29 14:13
 */
public class DataSourceTestImpl implements DataSource {
    @Override
    public String getStringFromRemote() {
        String str = "hello Test";
        return str;
    }

    @Override
    public String getStringFromCache() {
        return "World Test";
    }
}

源码地址:http://download.csdn.net/detail/u010566681/9615918


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

RxJava(三) flatMap操作符用法详解

flatMap操作符的作用官方文档解释:Returns an Observable that emits items based on applying a function that you sup...

RxJava(四) concatMap操作符用法详解

concatMap操作符的作用concatMap操作符和flatMap操作符非常类似。如果对 flatMap操作符 不是很了解可以点击链接去看看我的上一篇博文。下面是concatMap操作符的流程图:...

RxJava(二) map操作符用法详解

1 map操作符的作用 Returns an Observable that applies a specified function to each item emitted by the sour...

RxJava系列番外篇:一个RxJava解决复杂业务逻辑的案例

之前写过一系列RxJava的文章,也承诺过会尽快有RxJava2的介绍。无奈实际项目中还未真正的使用RxJava2,不敢妄动笔墨。所以这次还是给大家分享一个使用RxJava1解决问题的案例,希望对大家...

RxJava使用场景小结

取数据先检查缓存的场景取数据,首先检查内存是否有缓存 然后检查文件缓存中是否有 最后才从网络中取 前面任何一个条件满足,就不会执行后面的final Observable memory = ...
  • lzyzsd
  • lzyzsd
  • 2015-11-30 23:16
  • 41317

使用RxJava从多个数据源获取数据

试想,需要一些动态数据的时候,只要每次都请求网络就可以了。但是,更有效率的做法是,把联网得到的数据,缓存到磁盘或内存。 具体的说,计划如下: 偶尔的联网操作,只为获取最新数据。 尽可...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)