RxJava实现Android MVP

前言:MVP是什么我就不讲了,如果需要的可以去百度查查资料。

RxJava是观察者模式的一种实现,观察者为Consumer或者Subscriber 被观察者为Flowable

我们大致理一理,实现一个Present需要什么内容:

  • 1.基本的,Present要得到Activity的实例对象,它们是一对一的关系。

  • 2.首先,Present肯定是被观察者,他要处理完事件后返回结果给观察它的人。所以他要实现Flowable中具体的方法。

  • 3.他要有接受事物处理的对象,也就是函数的参数,然后根据参数来进行处理和返回结果

  • 4.他要有返回结果的能力,所以需要接受观察者的监听容器(Consumer,Subscriber),这也是需要写在函数之中的。

  • 5.他要有处理多线程并能返回到主线程的能力,因为我调用了网络请求后不可能将子线程回到主线程的操作交由View处理。

  • 6.他要避免内存泄漏,因为Present很多情况下需要用到多线程,如果持有Activity的实例,那么就会造成Activity释放的不及时。

这就是我整理出来的对Present的4点要求,下面我们一一实现。

以下主要涉及三个类,MainActivity(视图类),BasePresent(Present的基类),MainActivityPresent(MainActivity对应的Present实现类)

** 公共属性 **

我觉得把一些能够一起实现的放在基类里面,然后根据各有的特色进行继承,这样优化了代码的可读性和代码量
这里的公共属性我觉得有两个,一是回到主线程的能力,二是处理内存泄漏,不多说,先帖代码

public class BasePresent {

    private WeakReference<Context> mContext ;

    protected BasePresent(Context context){
        mContext = new WeakReference<Context>(context);
    }


    protected Context getContext(){
        return mContext.get();
    }

    protected void runOnUIThread(Runnable runnable){
        //判断Context的合理
        if(runnable==null || mContext.get() == null || !(mContext.get() instanceof Activity)){
            return ;
        }
        ((Activity)mContext.get()).runOnUiThread(runnable);

    }
}

可以看到,我们通过WeakRefrence实现了对Activity的弱应用,而且通过给子类暴露的getContext方法,一是解决了内存泄漏的问题,二是对子类感受不到处理内存泄漏过程的存在。我们用构造方法来表明,我的Present必须持有Activity对象。

** View **

对于View来说,我只需要显示界面和调用Present,所以在View中必须实现对Present的初始化

public class MainActivity extends BaseActivity {

    MainActivityPresent present ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        present = new MainActivityPresent(this);
    }
}

** 业务 **

我现在有两个需求:

  • 1.MainActivity需要进行登录操作
  • 2.MainActivity需要下载图片并监听整个下载过程

这两点有什么不同?

确实有不同,第一个需求表示我传入一个值,返回给我一个结果就行了,但是第二个需求,需要我发送请求下载,然后下载的进度要实时的返回给我。

** RxJava2 实现 **

先第一个需求,首先我们不写MainActivity,我们写Present

首先我们需要有一个方法,

MainActivityPresent.java:
public void login(final Login login , final Consumer<String> consumer){
  ...
}

我们可以看到,我们登陆,需要接受View传给我的登陆信息,这里用Login,从而进行对数据的处理,处理完成后,需要返回结果给View,所以这里需要Consumer类,我们写一个完整的。 tip:使用final因为有用到线程读取数据

public class MainActivityPresent extends BasePresent {


    public MainActivityPresent(Context context) {
        super(context);
    }

    public void login(final Login login , final Consumer<String> consumer){
        new Thread(new Runnable() {
            @Override
            public void run() {
                //登陆代码.....
                LogUtils.d("login");
                //runOnUIThread
                String result = "登陆成功";
                final Flowable flowable = Flowable.just(result);
                runOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        flowable.subscribe(consumer);
                    }
                });

            }
        }).start();
}
}

重点有两句话

 final Flowable flowable = Flowable.just(result);
 flowable.subscribe(consumer);

这里我们创建了一个Flowable对象,传入结果即将要返回的结果result ,在登陆完成后我们通过第二句代码来反馈给View。我们可以看到第二句话写在了runOnUIThread中,第二句话执行意味着回馈View

接下来我们实现MainActivity的调用

MainActivity.java:
//对于只需要结果的数据,可以使用简单的Consumer
present.login(new Login(), new Consumer<String>() {
    @Override
     public void accept(@NonNull String s) throws Exception {
        //登陆结果,s表示view需要present返回的类型
        LogUtils.d("accept");
         showTip(s);
     }
});

可以看到,我们调用时实现了Consumer类,并重载了accept方法,这个方法便是在登陆处理完成后Present回调的方法。


下面我们实现第二个需求,第二个需求强调过程性,我们只能使用Subscriber来处理这个需求。

也是,第一步,写Present

方法

MainActivityPresent.java:
public void downloadImage(final String url ,final Subscriber<Integer> subscriber){}

一样的,就不多说了,下面帖全部代码

public class MainActivityPresent extends BasePresent {


    public MainActivityPresent(Context context) {
        super(context);
    }

    public void downloadImage(final String url ,final Subscriber<Integer> subscriber){

        Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(final FlowableEmitter<Integer> e) throws Exception {
                LogUtils.d("subscribe");
                //下载代码
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(3000);
                            //传回更新数据
                            runOnUIThread(new Runnable() {
                                @Override
                                public void run() {
                                    e.onNext(47);
                                }
                            });
                            Thread.sleep(3000);
                            //传回下载完成数据
                            runOnUIThread(new Runnable() {
                                @Override
                                public void run() {
                                    e.onComplete();
                                }
                            });
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        }, BackpressureStrategy.BUFFER);
        flowable.subscribe(subscriber);

    }


}

我们可以看到过程很复杂,我们实现Flowable的方式也不同,这里最后也有一句,flowable.subscribe(subscriber); 这里注意的是,这句话执行并不意味着回馈消息,而是正式的注册监听,相当于关联起来。回馈消息重载的subscribe里面。

我们可以看到在subscribe 我开启了子线程,过3秒后我回到主线程执行了e.onNext(47); 又过3秒我回到主线程执行了e.onComplete(); 这里你可能不清楚含义,我先大致说一下,第一句话表示下载过程时的实时更新反馈,第二句话表示完成下载后的反馈。

好了 我们贴MainActivity的调用代码

MainActivity.java:
    //对于需要过程的数据,需要用Subscriber,我们需要一个进度,这里选用Integer为进度消息
    present.downloadImage("", new Subscriber<Integer>() {
        @Override
        public void onSubscribe(Subscription s) {
            LogUtils.d("onSubscribe");
            //开始请求
            showTip("start", Toast.LENGTH_SHORT);
            s.request(Long.MAX_VALUE);
        }

        @Override
        public void onNext(Integer integer) {
            LogUtils.d("onNext");
            //进度有更新
            showTip(integer+"", Toast.LENGTH_SHORT);
        }

        @Override
        public void onError(Throwable t) {
            LogUtils.d("onError");
            //下载出错
        }

        @Override
        public void onComplete() {
            LogUtils.d("onComplete");
            //下载完成
            showTip("complete");
        }
    });

看到了什么?我们实现Subscriber类时,我们重载了onNext方法,onComplete方法,是不是和上面对应起来了? 是的,在MainActivityPresent中的 ”过3秒后我回到主线程执行了e.onNext(47); 又过3秒我回到主线程执行了e.onComplete();” 和这里对应,那里反馈的消息在这里被调用。

😄,很难懂,自己慢慢理解

贴个所有的代码

public class BasePresent {

    private WeakReference<Context> mContext ;

    protected BasePresent(Context context){
        mContext = new WeakReference<Context>(context);
    }


    protected Context getContext(){
        return mContext.get();
    }

    protected void runOnUIThread(Runnable runnable){
        //判断Context的合理
        if(runnable==null || mContext.get() == null || !(mContext.get() instanceof Activity)){
            return ;
        }
        ((Activity)mContext.get()).runOnUiThread(runnable);

    }
}

public class MainActivityPresent extends BasePresent {


    public MainActivityPresent(Context context) {
        super(context);
    }

    public void login(final Login login , final Consumer<String> consumer){
        new Thread(new Runnable() {
            @Override
            public void run() {
                //登陆代码.....
                LogUtils.d("login");
                //runOnUIThread
                String result = "登陆成功";
                final Flowable flowable = Flowable.just(result);
                runOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        flowable.subscribe(consumer);
                    }
                });

            }
        }).start();


    }

    public void downloadImage(final String url ,final Subscriber<Integer> subscriber){

        Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(final FlowableEmitter<Integer> e) throws Exception {
                LogUtils.d("subscribe");
                //下载代码
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(3000);
                            //传回更新数据
                            runOnUIThread(new Runnable() {
                                @Override
                                public void run() {
                                    e.onNext(47);
                                }
                            });
                            Thread.sleep(3000);
                            //传回下载完成数据
                            runOnUIThread(new Runnable() {
                                @Override
                                public void run() {
                                    e.onComplete();
                                }
                            });
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        }, BackpressureStrategy.BUFFER);
        flowable.subscribe(subscriber);

    }


}
public class MainActivity extends BaseActivity {

    MainActivityPresent present ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        present = new MainActivityPresent(this);

        //对于只需要结果的数据,可以使用简单的Consumer
        present.login(new Login(), new Consumer<String>() {
            @Override
            public void accept(@NonNull String s) throws Exception {
                //登陆结果,s表示view需要present返回的类型
                LogUtils.d("accept");
                //showTip(s);
            }
        });

        //对于需要过程的数据,需要用Subscriber,我们需要一个进度,这里选用Integer为进度消息
        present.downloadImage("", new Subscriber<Integer>() {
            @Override
            public void onSubscribe(Subscription s) {
                    LogUtils.d("onSubscribe");
                //开始请求
                showTip("start", Toast.LENGTH_SHORT);
                s.request(Long.MAX_VALUE);
            }

            @Override
            public void onNext(Integer integer) {
                LogUtils.d("onNext");
                //进度有更新
                showTip(integer+"", Toast.LENGTH_SHORT);
            }

            @Override
            public void onError(Throwable t) {
                LogUtils.d("onError");
                //下载出错
            }

            @Override
            public void onComplete() {
                LogUtils.d("onComplete");
                //下载完成
                showTip("complete");
            }
        });
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容