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

本系列文章:

  • 拆轮子系列--RxJava前奏篇
  • 拆轮子系列--RxJava理解(一)--Map解析
  • 拆轮子系列--RxJava理解(二)--subscribeOn
  • 拆轮子系列--RxJava理解(三)--observeOn

本篇文章主要介绍为什么要使用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 {
        @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() {
                    @Override
                    public Bitmap call(String url) {
                        return getBitmapFromUrl(url);
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1() {
                    @Override
                    public void call(Bitmap bitmap) {
                        showBitMap();
                    }
                }); 

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

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

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

public class CutestPeople implements Comparable {

    private Bitmap image;
    private int cuteness;

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

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

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

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

public class DataHelper {
    private QueryApi mApi;

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

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

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

try {
       List 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 people);
        void onQueryError(Exception e);
    }

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

    /**
     * 获取可爱人儿集合
     *
     */
    List 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 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 people) {
        return Collections.max(people);
    }

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

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

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

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

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

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

public class ApiWarpper {

    private QueryApi mApi;

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

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

            @Override
            public void onQueryError(Exception e) {

                myCallback.onError(e);
            }
        });

    }

    public void storeUri(CutestPeople people, final MyCallback 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 myCallBack) {

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

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

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

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

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

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

public class ApiWrapper {

    private QueryApi mApi;

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

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

    public MyAsyncTask storeUri(final CutestPeople people){
        //这里做了修改,传入一个参数,返回一个临时变量  
        return new MyAsyncTask() {
            @Override
            public void start(final MyCallback 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 saveTheCuestPeople(final String query) {

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

        final MyAsyncTask myCutestAsyncTask = new MyAsyncTask() {

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

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

     MyAsyncTask uriMyAsyncTask = new MyAsyncTask() {
          @Override
          public void start(final MyCallback callback) {
           myCutestAsyncTask.start(new MyCallback() {
                    @Override
                    public void onGetResult(CutestPeople result) {
                        mApiWrapper.storeUri(result)
                                .start(new MyCallback() {
                                 @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 people) {
        return Collections.max(people);
    }
}

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

public class DataHelper {
    private QueryApi mApi;

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

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

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

public class DataHelper {

    ApiWrapper mApiWrapper;

    public MyAsyncTask saveTheCuestPeople(final String query) {

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

      final MyAsyncTask myCutestAsyncTask = new MyAsyncTask() { ...};
      MyAsyncTask uriMyAsyncTask = new MyAsyncTask() {...};
      return uriMyAsyncTask;
    }

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

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

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

public class ApiWrapper {

    private QueryApi mApi;

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

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

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

    public Observable storeUri(final CutestPeople people) {
        return Observable.create(new Observable.OnSubscribe() {
         @Override
         public void call(final Subscriber 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 saveTheCuestPeople(String query) {
        Observable> listObservable = mApiWrapper.queryQuestPeople(query);
        Observable cutestPeople = listObservable.map(new Func1, CutestPeople>() {
            @Override
            public CutestPeople call(List cutestPeoples) {
                return findCutestPeople(cutestPeoples);
            }
        });
        Observable storeObservable = cutestPeople.flatMap(new Func1>() {
            @Override
            public Observable call(CutestPeople people) {
                return mApiWrapper.storeUri(people);
            }
        });
        return storeObservable;
    }

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

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

3. 总结

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

你可能感兴趣的:(拆轮子系列---RxJava理解(零)前奏篇)