拆轮子系列---RxJava理解(零)前奏篇

本系列文章:

本篇文章主要介绍为什么要使用RxJava,属于RxJava理解的前奏篇。本文大纲如下:

    1. RxJava的常规写法
    1. 一个具体的例子---从普通写法逐渐演变成RxJava写法
    1. 总结

1. RxJava 的常规写法

有人说RxJava非常好用,那么,它究竟好用在哪里?今天来具体分析下。首先,先来阐述RxJava到底是什么,RxJava官方的解释是:“a library for composing asynchronous and event-based programs using observable sequences for the Java VM”,其核心就是“asynchronous”这个词,直白的说,RxJava就是一个实现异步操作的库。那为什么大家会觉得RxJava好用,而不是使用AsyncTask/Handler...?这里可以归结一个词,简洁。举个例子,我们要从网络上获取图片然后显示。利用AsyncTask的做法是这样的:

public class ImageTask extends AsyncTask<String, Void, Bitmap> {
        @Override
        protected Bitmap doInBackground(String... params) {
            String imageUrl = params[0];
            try {
                URL url = new URL(imageUrl);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                InputStream is = conn.getInputStream();
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                if (bitmap != null) {
                    return bitmap;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            if(bitmap!=null){
                showBitMap();
            }
        }
    }

如果将这个需求用RxJava实现会是怎么样?我们先实现看看:

Observable.just(imageUrl)
                .map(new Func1<String, Bitmap>() {
                    @Override
                    public Bitmap call(String url) {
                        return getBitmapFromUrl(url);
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<Bitmap>() {
                    @Override
                    public void call(Bitmap bitmap) {
                        showBitMap();
                    }
                }); 

可能有的朋友要说话了没看出什么区别啊,好用个毛线啊。其实,我们观察下就能发现,RxJava的这个实现,是一条链式调用,在逻辑上没有任何的嵌套,简单明了。更进一步说,当我们需求变得复杂时,比如在大量的图片资源中选择一张,亦或者是选择前几张图片的时候,你还能在你那片代码中能迅速理清逻辑,并快速调整?RxJava在处理复杂逻辑时,一条链式调用,虽然很长,但胜在逻辑清晰。

2. 一个具体的例子---从普通写法逐渐演变成RxJava写法

接下来,以一个例子具体阐述下RxJava到底逻辑清晰在哪里。假设有一个需求:谁是最可爱的人(SB需求,哈哈哈)。该需求大体就是网络请求查询一组图片,每张图片有一个可爱系数(一个整型值),而我们的任务,就是下载一组可爱人的照片集合,然后选择最可爱的那个。
好了,需求弄明白了接下来就进行实现吧,先定义一个简单的数据结构:

public class CutestPeople implements Comparable<CutestPeople> {

    private Bitmap image;
    private int cuteness;

    @Override
    public int compareTo(CutestPeople cutestPeople) {
        return Integer.compare(cuteness, cutestPeople.cuteness);
    }
 }  

然后,提供两个接口,查询与保存:

public interface QueryApi {
    /**
     * 查询可爱的人
     */
    List<CutestPeople>  queryCuestPeople(String query);
    /**
     * 获取需要保存的Uri
     */
    Uri store(CutestPeople people);
}

好了,大体结构基本完成,接下来定义一个帮助类,该类就是调用前面定义的方法获取结果:

public class DataHelper {
    private QueryApi mApi;

    public Uri saveTheCutestPeople(String query) {
        List<CutestPeople> cutestPeoples = mApi.queryCuestPeople(query);
        CutestPeople cutestPeople = findCutestPeople(cutestPeoples);
        Uri savedUrl = mApi.store(cutestPeople);
        return savedUrl;
    }

    /**
     * 获取最可爱的人
     */
    public CutestPeople findCutestPeople(List<CutestPeople> people) {
        return Collections.max(people);
    }
}

上面似乎完成了?不对,还有一个问题,加上异常处理机制:

try {
       List<CutestPeople> cutestPeoples = mApi.queryCuestPeople(query);
       CutestPeople cutestPeople = findCutestPeople(cutestPeoples);
       Uri savedUrl = mApi.store(cutestPeople);
       return savedUrl;
   } catch (Exception e) {
       e.printStackTrace();
       return error;
   }

到这里,需求算是大体完成了,程序可以跑起来了。但是,有没有发现,以上的代码还有很大的优化空间,比如,我们在接口里面定义异步回调机制,这样程序运行的结果或者异常都可以从回调中拿到:

public interface QueryApi {

    interface CuestPeopleQueryCallback {
        void onCuestPeopleReceived(List<CutestPeople> people);
        void onQueryError(Exception e);
    }

    interface StoreCallback {
        void onCuestPeopleStore(Uri uri);
        void onStoreError(Exception e);
    }

    /**
     * 获取可爱人儿集合
     *
     */
    List<CutestPeople> queryCutestPeople(String query, CutestPeopleQueryCallback callback);

    /**
     * 保存最可爱的人到本地
     *
     */
    Uri store(CutestPeople people, StoreCallback callback);
}

既然接口发生了改变,那么相应的DataHelper类的逻辑也要发生改变,继续修改:

public class DataHelper {

    private QueryApi mApi;

    public void saveTheCuestPeople(String query, final CutestPeopleCallback cutestCallback) {
     mApi.queryCutestPeople(query, new QueryApi.CutestPeopleQueryCallback() {
     @Override
     public void onCutestPeopleReceived(List<CutestPeople> people) {
        CutestPeople cutestPeople = findCutestPeople(people);
        mApi.store(cutestPeople, new QueryApi.StoreCallback() {
        @Override
        public void onCutestPeopleStore(Uri uri) {
           cutestPeopleCallback.onCutestPeopleSaved(uri);
        }

        @Override
        public void onStoreError(Exception e) {
            cutestPeopleCallback.onError(e);
            }
          });
        }

            @Override
            public void onQueryError(Exception e) {
                cutestPeopleCallback.onError(e);
            }
        });
    }

    /**
     * 获取最可爱的人
     */
    public CutestPeople findCutestPeople(List<CutestPeople> people) {
        return Collections.max(people);
    }

    /**
     * 最可爱的人保存结果回调
     */
    public interface CutestPeopleCallback {
        void onCutestPeopleSaved(Uri uri);
        void onError(Exception e);
    }
}

经过上面的修改,代码看起来是不是好了很多。可是,有没有发现接口调用的太多了,而且在每一个关键点都需要手动的插入相应的接口回调,而且,错误传递机制也木有。这样子还不行,需要继续优化。目前的情况看,我们QuaryApi中的接口是这个样子的:

void onCutestPeopleReceived(List<CutestPeople> people);
void onCutestPeopleStore(Uri uri);
...
void onQueryError(Exception e);
void onStoreError(Exception e);

这个时候已经定义的接口方法是不能改变了,但是,我们可以在不改变方法的前提下对接口进行包装:

public interface Callback<T> {
    void onGetResult(T result);
    void onError(Exception e);
}

继续搞事情,将之前定义的QueryApi进行包装下:

public class ApiWarpper {

    private QueryApi mApi;

    public void queryQuestPeople(String query, final MyCallback<List<CutestPeople>> myCallback) {

        mApi.queryCutestPeople(query, new QueryApi.CutestPeopleQueryCallback() {
            @Override
            public void onCutestPeopleReceived(List<CutestPeople> people) {
                myCallback.onGetResult(people);
            }

            @Override
            public void onQueryError(Exception e) {

                myCallback.onError(e);
            }
        });

    }

    public void storeUri(CutestPeople people, final MyCallback<Uri> myCallback) {
        mApi.store(people, new QueryApi.StoreCallback() {
            @Override
            public void onCutestPeopleStore(Uri uri) {
                myCallback.onGetResult(uri);
            }

            @Override
            public void onStoreError(Exception e) {
                myCallback.onError(e);
            }
        });
    }
}

在对QueryApi进行包装之后,我们再来修改下DataHelper中的逻辑,调整如下:

public class DataHelper {

    ApiWrapper mApiWrapper;

    public void saveTheCutestPeople(String query, final MyCallback<Uri> myCallBack) {

        mApiWrapper.queryQuestPeople(query, new MyCallback<List<CutestPeople>>() {
            @Override
            public void onGetResult(List<CutestPeople> result) {
                CutestPeople cutestPeople = findCutestPeople(result);
                mApiWrapper.storeUri(cutestPeople, myCallBack);
            }

            @Override
            public void onError(Exception e) {
                myCallBack.onError(e);
            }
        });
    }

    /**
     * 获取最可爱的人
     */
    public CutestPeople findCutestPeople(List<CutestPeople> people) {
    return Collections.max(people);
    }
}

好了,到了这一步基本上没什么问题,需求已经完成。但是,没错还有但是,我们可以想想,能不能将每个异步操作分解成更小的粒子,也就是说,每个异步操作只携带一个参数对象,然后返回携带者回调信息的临时变量。
接下来定义一个通用的临时变量,该对象只是携带一个参数:

public abstract class MyAsyncTask<T> {
    public abstract void start(MyCallback<T> callback);
}

修改ApiWrapper中的方法,将异步请求分解成更小的粒子:

public class ApiWrapper {

    private QueryApi mApi;

    public MyAsyncTask<List<CutestPeople>> queryQuestPeople(final String query) {
    //这里做了修改,传入一个参数,返回一个临时变量
    return new MyAsyncTask<List<CutestPeople>>() {
    @Override
    public void start(final MyCallback<List<CutestPeople>> callback) {
       mApi.queryCutestPeople(query, new QueryApi.CutestPeopleQueryCallback() {
            @Override
             public void onCutestPeopleReceived(List<CutestPeople> people) {
             callback.onGetResult(people);
             }

             @Override
              public void onQueryError(Exception e) {
              callback.onError(e);
             }
         });
       }
     };
   }

    public MyAsyncTask<Uri> storeUri(final CutestPeople people){
        //这里做了修改,传入一个参数,返回一个临时变量  
        return new MyAsyncTask<Uri>() {
            @Override
            public void start(final MyCallback<Uri> callback) {

                mApi.store(people, new QueryApi.StoreCallback() {
                    @Override
                    public void onCutestPeopleStore(Uri uri) {
                        callback.onGetResult(uri);
                    }

                    @Override
                    public void onStoreError(Exception e) {

                        callback.onError(e);
                    }
                });
            }
        };
    }
}

改了ApiWrapper当然同步的需要修改DataHelper这个类:

public class DataHelper {

    ApiWrapper mApiWrapper;

    public MyAsyncTask<Uri> saveTheCuestPeople(final String query) {

        final MyAsyncTask<List<CutestPeople>> listMyAsyncTask = mApiWrapper.queryQuestPeople(query);

        final MyAsyncTask<CutestPeople> myCutestAsyncTask = new MyAsyncTask<CutestPeople>() {

            @Override
            public void start(final MyCallback<CutestPeople> callback) {
            listMyAsyncTask.start(new MyCallback<List<CutestPeople>>){
                     @Override
                     public void onGetResult(List<CutestPeople> result) {                       
                      callback.onGetResult(findCutestPeople(result));
                     }

                     @Override
                     public void onError(Exception e) {
                     callback.onError(e);
                    }
               });
            }
        };

     MyAsyncTask<Uri> uriMyAsyncTask = new MyAsyncTask<Uri>() {
          @Override
          public void start(final MyCallback<Uri> callback) {
           myCutestAsyncTask.start(new MyCallback<CutestPeople>() {
                    @Override
                    public void onGetResult(CutestPeople result) {
                        mApiWrapper.storeUri(result)
                                .start(new MyCallback<Uri>() {
                                 @Override
                                  public void onGetResult(Uri result) {
                                  callback.onGetResult(result);
                                  }

                                  @Override
                                  public void onError(Exception e) {
                                  callback.onError(e);
                                  }
                              });
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
        return uriMyAsyncTask;
    }


    /**
     * 获取可爱的人
     */

    public CutestPeople findCutestPeople(List<CutestPeople> people) {
        return Collections.max(people);
    }
}

修改之后似乎变得比之前要复杂,我们回顾最初我们写的代码:

public class DataHelper {
    private QueryApi mApi;

    public Uri saveTheCutestPeople(String query) {
        List<CutestPeople> cutestPeoples = mApi.queryCuestPeople(query);
        CutestPeople cutestPeople = findCutestPeople(cutestPeoples);
        Uri savedUrl = mApi.store(cutestPeople);
       return savedUrl;
    }

    /**
     * 获取最可爱的人
     */
    public CutestPeople findCutestPeople(List<CutestPeople> people) {
        return Collections.max(people);
    }
}

将我们最后修改的代码简化下:

public class DataHelper {

    ApiWrapper mApiWrapper;

    public MyAsyncTask<Uri> saveTheCuestPeople(final String query) {

      final MyAsyncTask<List<CutestPeople>> listMyAsyncTask = mApiWrapper.queryQuestPeople(query);

      final MyAsyncTask<CutestPeople> myCutestAsyncTask = new MyAsyncTask<CutestPeople>() { ...};
      MyAsyncTask<Uri> uriMyAsyncTask = new MyAsyncTask<Uri>() {...};
      return uriMyAsyncTask;
    }

    /**
     * 获取最可爱的人
     */

    public CutestPeople findCutestPeople(List<CutestPeople> people) {
        return Collections.max(people);
    }
}

对比上面两份代码我们发现,其实大体的逻辑是保持一致的,区别就是我们将异步操作细分成了更小的模块,然后组合在一起,最后返回一个组合后的结果对象而已。
写了这么多,好像跟RxJava没有什么关系啊,别急,下面我们用RxJava来实现这个需求:

public class ApiWrapper {

    private QueryApi mApi;

    public Observable<List<CutestPeople>> queryQuestPeople(final String query) {
        return Observable.create(new Observable.OnSubscribe<List<CutestPeople>>() {
            @Override
            public void call(final Subscriber<? super List<CutestPeople>> subscriber) {

                mApi.queryCutestPeople(query, new QueryApi.CutestPeopleQueryCallback() {
                    @Override
                    public void onCutestPeopleReceived(List<CutestPeople> people) {
                        subscriber.onNext(people);
                    }

                    @Override
                    public void onQueryError(Exception e) {
                        subscriber.onError(e);
                    }
                });
            }
        });
    }

    public Observable<Uri> storeUri(final CutestPeople people) {
        return Observable.create(new Observable.OnSubscribe<Uri>() {
         @Override
         public void call(final Subscriber<? super Uri> subscriber) {
                mApi.store(people, new QueryApi.StoreCallback() {
                    @Override
                    public void onCutestPeopleStore(Uri uri) {
                        subscriber.onNext(uri);
                    }

                    @Override
                    public void onStoreError(Exception e) {
                        subscriber.onError(e);
                    }
                });
            }
        });
    }
}

DataHelper类:

public class DataHelper {

    ApiWrapper mApiWrapper;

    public Observable<Uri> saveTheCuestPeople(String query) {
        Observable<List<CutestPeople>> listObservable = mApiWrapper.queryQuestPeople(query);
        Observable<CutestPeople> cutestPeople = listObservable.map(new Func1<List<CutestPeople>, CutestPeople>() {
            @Override
            public CutestPeople call(List<CutestPeople> cutestPeoples) {
                return findCutestPeople(cutestPeoples);
            }
        });
        Observable<Uri> storeObservable = cutestPeople.flatMap(new Func1<CutestPeople, Observable<Uri>>() {
            @Override
            public Observable<Uri> call(CutestPeople people) {
                return mApiWrapper.storeUri(people);
            }
        });
        return storeObservable;
    }

    /**
     * 获取最可爱的人
     */
    public CutestPeople findCutestPeople(List<CutestPeople> people) {
        return Collections.max(people);
    }
}

看到上面的逻辑,是否发现跟我们最后写的逻辑基本一致?而区别就是利用RxJava将一个个的异步操作单独的抽象出来,这样我们就能避免各种嵌套的回调,然后将这些抽象出来的异步操作进行组合作为一个结果返回即可。

3. 总结

RxJava的核心思想就是处理异步操作,将异步操作独立的抽象出来,在异步条件非常复杂的情况下,RxJave以一条链式调用将所有复杂的异步操作串成一条线,从而实现代码的易读性与简洁性。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,987评论 25 707
  • 第1章从是与非探求宇宙的意义 1.天理与人理2.不尽同意3.这律的确存在4.在“人理”後面是什麽?5.我们内心不安...
    卡皮阅读 931评论 0 1
  • 午睡前,在微信发了一条,给你方便,是情分,不是本分。一次可以,两次可以,再多自己也不想想,应不应该。长着嘴,永远只...
    还是小丫头呢阅读 159评论 0 0
  • 2017年8月13日,应该对我来说是个人生的分界线,在这之前我一直当了十几年的学生,终于在这一天,画风一转突然...
    控卫阅读 575评论 0 0