1. 程式人生 > >RXJava 常用操作符整理(持續更新)

RXJava 常用操作符整理(持續更新)

參考文章:

扔物線大哥神作(對於轉換的原理及RXJava基礎講的非常透徹):



http://gank.io/post/560e15be2dca930e00da1083

操作符系列好文(系統學習操作符的不二選擇):

http://blog.chinaunix.net/uid-20771867-id-5187376.html

 

         RXJava最爽的莫過於鏈式程式設計,程式碼條理清晰,可以把各種回撥巢狀、時序混亂、型別轉換等的問題以一條鏈式呼叫統統搞定。而這麼神奇的功能就需要操作符來支援。

         看一段簡單的程式碼:

  Observable.just("張三","李四","王五").subscribe(new Action1<String>()
        {
            @Override public void call(String s)
            {
                Log.d("name",s);
            }
        });

         就是輸出幾個簡單的字串,其中just就是一個操作符(同理create、from)也是。在我理解中,操作符就是在資料流中,對資料進行各種轉換、處理的一些封裝好的方法。

而我們常用的操作符,除了剛才說的create、from、just這些,最常用的就是用於資料轉換處理(也可以用於其他用途)的map和flatMap了。除此之外,還有可以幫我們解決蒐集多個API結果狀態的merge啊(吐血推薦)、防抖throttleFirst啊(再也不用不停地getCurrentTime了)等等。。。

 

Map

      如果有一個模組,可以獲取到每個使用者的所有資訊,然後在UI上我需要繪製使用者的頭像,這樣需要在使用者資訊裡獲取到頭像的Url。這個使用者資訊包括了:

    class UserInfoBean
    {
        public String avatar;
        public String name;
        public String phoneNum;
    }

       那如果按照正常的辦法,我們會這樣去完成:

   ImageView ivAvatar = (ImageView)findViewById(R.id.iv_user_avatar);
        Observable.just(userInfo).subscribe(new Action1<UserInfoBean>()
        {
            @Override public void call(UserInfoBean bean)
            {
                Glide.with(mContext).load(bean.avatarUrl).into(ivAvatar);
            }
        });


      講道理說subscriber中應該只響應結果,這裡的subsriber只期望獲取一個url用來展示頭像,而對其他的東西並不感興趣,那麼好吧,修改一下程式碼:

      ImageView ivAvatar = (ImageView)findViewById(R.id.iv_user_avatar);
        Observable.just(userInfo.avatarUrl).subscribe(new Action1<String>()
        {
            @Override public void call(String url)
            {
                Glide.with(mContext).load(url).into(ivAvatar);
            }
        });

         在這裡,我們使observable中呼叫next的時候直接傳入一個頭像的Url,這樣不就好了嘛~  可是,如果我要在其他地方需要獲取到使用者的名字怎麼辦?現在這個Observable中直接返回的是頭像Url啊 ,難道我要重寫一個嗎?

         這時候就可以用到map了。

        

     ImageView ivAvatar = (ImageView)findViewById(R.id.iv_user_avatar);
        TextView tvName = (TextView)findViewById(R.id.tv_user_name);
        //更新頭像
        getUserInfo().map(new Func1<UserInfoBean,String>()
        {
            @Override public String call(UserInfoBean bean)
            {
                return bean.avatarUrl;
            }
        }).subscribe(new Action1<String>()
        {
            @Override public void call(String url)
            {
                Glide.with(mContext).load(url).info(ivAvatar);
            }
        });

        //更新使用者暱稱
        getUserInfo().map(new Func1<UserInfoBean,String>()
        {
            @Override public String call(UserInfoBean bean)
            {
                return bean.name;
            }
        }).subscribe(new Action1<String>()
        {
            @Override public void call(String name)
            {
                tvName.setText(name);
            }
        });
      Observable getUserInfo()
      {
        return Observable.just(userInfo);
       }


          map可以把obserable傳出的資料做一個轉換再放到subscriber的onNext方法中執行~~~這樣既實現了observable的複用,又使得subcriber中的工作減輕,最重要的是提高了程式碼的逼格。

 

flatMap

       也行你會覺得map沒啥用,甚至有些畫蛇添足,那麼看下接下來的這個進階加強版。

       還是剛才的那個例子,現在每個使用者有若干好友,那麼需要在userInfo中加入一個List,這裡存放著所有好友的使用者Id

   class UserInfoBean
    {
        public String avatarUrl;
        public String name;
        public String phoneNum;
        public List<String> friendList;
    }

        現在我想獲取這個使用者所有好友的姓名,那好,那我正常情況下肯定是需要遍歷這個好友列表,獲取每個好友的id然後去查詢好友姓名吧。像這樣
      

        UserInfoBean userInfoBean = getCurrentUser();

        Observable.just(userInfoBean).subscribe(new Action1<UserInfoBean>()
        {
            @Override public void call(UserInfoBean bean)
            {
                for(String id:bean.friendList)
                {
                    UserInfoBean friend = queryUserById(id);
                    System.out.print(friend.name);
                }
            }
        });


          為什麼我都用RXJava這麼洋氣的東西了,還要在響應方法中寫這麼多的程式碼呢,而且還用到了for each!好吧,解決這個問題就用到flatMap了!

          先看使用flatMap改寫的程式碼!

          

        Observable.just(userInfoBean).flatMap(new Func1<UserInfoBean, Observable<String>>() {
            @Override
            public Observable<String> call(UserInfoBean userInfoBean)
            {
                List<String> names = new ArrayList<String>();
                for(String id:userInfoBean.friendList)
                {
                    UserInfoBean friend = queryUserById(id);
                    names.add(friend.name);
                }
                return Observable.from(names);
            }
        }).subscribe(new Action1<String>() {
            @Override
            public void call(String name) {
                System.out.print(name);
            }
        });

          在flatMap的參入中放入一個Func1物件!這個Fucn1物件封裝了個有一個入參並有返回值call方法。之前還有個Action封裝的是不同入參但沒有返回值的call方法,現在對比下,顧名思義,func作為一個方法,需要有返回值,所以funcX都是封裝了不同數量入參且有一個返回值的方法。而Action作為一個動作,不需要有反饋,所以ActionX封裝的是不同數量入參且沒有返回值的方法。

 

          接著看flatMap中func1的call方法做了些什麼。在call方法中遍歷了所有好友的id,然後根據Id查詢出好友的姓名,然後通過from將姓名返回,這樣就可以通過from來實現了1對多的一個轉換,而且把所有耗時的邏輯操作都封裝到非同步方法中執行,使得subsciber只去響應。

          從這可以看出,flatMap中存放的是一個轉換的操作,但是這個轉換的操作是在什麼時候執行呼叫的呢?而且func1中的call返回的為什麼是一個observbale物件?這塊兒我覺得理解起來還是有一定難度的,至少對於我來講,耗費了不少的時間來理解。

         這地方建議參考扔物線的文章(最上面的連結)並結合原始碼來看,我在這邊簡單的把我個人的理解記錄下。

         首先,點開flatMap的原始碼:

 

    public final <R> Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func) {
        if (getClass() == ScalarSynchronousObservable.class) {
            return ((ScalarSynchronousObservable<T>)this).scalarFlatMap(func);
        }
        return merge(map(func));
    }

        忽略同步的那個判斷,直接看return,發現這裡有兩個方法,分別是merge和map,點開map:

 

       

    public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {
        return lift(new OperatorMap<T, R>(func));
    }

        這裡注意兩點:1.利用func建立了一個OperatorMap(一個實現了Operator介面的類)。2.將這個OperatorMap放入List中。看到Operator了吧,這下和我們這篇文章的題目貼切了吧~~~ 而且List這個東西這的是整個轉換操作符的精華,這裡的程式碼很少,但是很重要:

 

       

  public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
        return new Observable<R>(new OnSubscribe<R>() {
            @Override
            public void call(Subscriber<? super R> o) {
                try {
                    Subscriber<? super T> st = hook.onLift(operator).call(o);
                    try {
                        // new Subscriber created and being subscribed with so 'onStart' it
                        st.onStart();
                        onSubscribe.call(st);
                    } catch (Throwable e) {
                        // localized capture of errors rather than it skipping all operators 
                        // and ending up in the try/catch of the subscribe method which then
                        // prevents onErrorResumeNext and other similar approaches to error handling
                        Exceptions.throwIfFatal(e);
                        st.onError(e);
                    }
                } catch (Throwable e) {
                    Exceptions.throwIfFatal(e);
                    // if the lift function failed all we can do is pass the error to the final Subscriber
                    // as we don't have the operator available to us
                    o.onError(e);
                }
            }
        });
    }

        可以看到list傳入了一個operator物件,返回了一個Observable物件。我們上面使用flatMap的例子中,我們自己定義了一個輸出某個使用者資訊的Observable為ObservableA,列印這個使用者所有好友姓名的subscriber為subscriberB。那麼設這個List中生成的Observable物件為ObserableB,看下這個ObservableB中的call方法,這個call方法中利用傳入的operator和原始的subsriberA生成了一個新的subscriber物件為subsriberB,然後呼叫OnSubsribe.call來註冊這個新的subsriberB。Ok,那麼看一下這個subseriberB都做了什麼,這就要先看下OperatorMap這個類中做了什麼。

 

     

public final class OperatorMap<T, R> implements Operator<R, T> {

    final Func1<? super T, ? extends R> transformer;

    public OperatorMap(Func1<? super T, ? extends R> transformer) {
        this.transformer = transformer;
    }

    @Override
    public Subscriber<? super T> call(final Subscriber<? super R> o) {
        return new Subscriber<T>(o) {

            @Override
            public void onCompleted() {
                o.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                o.onError(e);
            }

            @Override
            public void onNext(T t) {
                try {
                    o.onNext(transformer.call(t));
                } catch (Throwable e) {
                    Exceptions.throwOrReport(e, this, t);
                }
            }

        };
    }

}

 

 

 

       這個構造方法中的入參就是我們在flatMap中定義的func1,而call方法的入參就是我們的subsriberA,會發現在這裡call方法中,新建了一個subsriber,這個subsriber就是subsriberB,而這個subsriberB其實就是對subsriberA進行了一個帶來,其中OnComplete和OnError都是直接呼叫A的,只有在onNext中做了個轉換,而這個轉換的方法就是func1中的call方法。也就是說,現在其實是subscriberB訂閱了observbaleA,而subcriberB在獲取了ObservableA的處理結果後,經過轉換,又傳送給了ObserableA。而此時,subsriberA訂閱的就不是ObserableA,而是ObserableB了,也就是說B在A之間做了一個代理的作用。