RxJava2使用详解

RxJava2使用详解

学习RxJava2之前需要了解下观察者模式,参考上一篇博客 Design pattern–观察者模式

RxJava2是什么

一个在Java VM上使用可观测的序列来组成异步的基于事件的程序的库
官网:https://github.com/ReactiveX/RxJava

Observer/Subscriber观察者

Observable被观察者(主题Subject)

Subscribe订阅

主题(Observable)和观察者(Observer)通过Subscribe方法来完成订阅关系,当主题发生状态变化,会通知每一个观察者更新自身状态。


RxJava2基本用法

用之前,需要在app/build.grade中添加依赖:

dependencies {
  compile "io.reactivex.rxjava2:rxjava:2.0.0-RC5"
  compile "io.reactivex.rxjava2:rxandroid:2.0.0-RC1"
}

看一个简单的例子

public class RxjavaActivity extends AppCompatActivity{

  @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_rxjava);
  }

  // 按钮点击事件
  public void click(View view) {
    Observer observer = createObserver();
    Observable observable = createObservable();
    // observable订阅observer
    observable.subscribe(observer);
  }
  // 创建Observable主题对象
  public Observable<String> createObservable() {
    return Observable.create(new ObservableOnSubscribe<String>() {
      @Override public void subscribe(ObservableEmitter<String> e) throws Exception {
        e.onNext("择天记更新了!!");
        e.onNext("人民的名义大结局!!");
        e.onComplete();
      }
    });
  }

  // 创建Observer观察者对象
  public Observer<String> createObserver() {
    return new Observer<String>() {
      @Override public void onSubscribe(Disposable d) {
        System.out.println("-- onSubscribe");
      }

      @Override public void onNext(String value) {
        System.out.println("-- onNext:"+value);
      }

      @Override public void onError(Throwable e) {
        System.out.println("-- Throwable");
      }

      @Override public void onComplete() {
        System.out.println("-- onComplete");
      }
    };
  }
}

例子中可以看到,单主题Observeable状态变化的时候可以调用onNext\ onError\ onComplete\ onSubscribe来通知观察者对象,和上一篇博客 Design pattern–观察者模式 中调用主题的change方法通知到每一个观察者对象的update方法是一样的。我们运行一下,看下结果:

04-30 15:16:46.491 25444-25444/com.testdes.des I/System.out: -- onSubscribe
04-30 15:16:46.491 25444-25444/com.testdes.des I/System.out: -- onNext:择天记更新了!!
04-30 15:16:46.491 25444-25444/com.testdes.des I/System.out: -- onNext:人民的名义大结局!!
04-30 15:16:46.491 25444-25444/com.testdes.des I/System.out: -- onComplete

当我收到择天记消息的时候,我希望可以专心看择天记,不想再被别的消息骚扰怎么办?我们可以调用Disposable对象的dispose方法,通过dispose来解除订阅关系,如下:

public Observer<String> createObserver() {
    return new Observer<String>() {
      Disposable disposable = null;
      @Override public void onSubscribe(Disposable d) {
        disposable = d;
        System.out.println("-- onSubscribe");
      }
      @Override public void onNext(String value) {
        if (value.contains("择天记")) {
          disposable.dispose();
        }
        System.out.println("-- onNext:"+value);
      }
      @Override public void onError(Throwable e) {
        System.out.println("-- Throwable");
      }
      @Override public void onComplete() {
        System.out.println("-- onComplete");
      }
    };

运行一下:

04-30 15:35:49.070 26198-26198/com.testdes.des I/System.out: -- onSubscribe
04-30 15:35:49.071 26198-26198/com.testdes.des I/System.out: -- onNext:择天记更新了!!

我们再看下简单的用法,比如Observer对象可以通过Consumer来构建,Observable通过just来返回,简化后的代码:

public void click(View view) {
  Observable observable = createObservable();
  observable.subscribe(new Consumer<String>() {
    @Override public void accept(String o) throws Exception {
      System.out.println("-- accept:" + o);
    }
  });
}

public Observable<String> createObservable() {
  return Observable.just("择天记", "速度激情8");
}

除了just还有fromArray、fromCallable和fromFuture等,看下运行结果:

04-30 15:50:21.613 26579-26579/com.testdes.des I/System.out: -- accept:择天记
04-30 15:50:21.613 26579-26579/com.testdes.des I/System.out: -- accept:速度激情8

RxJava2线程控制

  • Scheduler.immediate()
    直接运行在当前线程,这是默认的scheduler

  • Scheduler.newThread()
    开一个新的线程,并在新的线程中执行操作

  • Scheduler.io()
    io操作(读写文件、网络交互)所使用的scheduler,和newThead类似,区别是io内部维护了一个没有数量上限的线程池,使用io可以避免不必要的线程创建

  • Scheduler.computation()
    计算所用的scheduler,计算指的是cpu密集型计算,如图形的计算,使用固定数量(cpu核心数)的线程池

  • Scheduler.mainThread()
    在android主线程中运行

  public void click(View view) {
    Observable.create(new ObservableOnSubscribe<String>() {
      @Override public void subscribe(ObservableEmitter<String> e) throws Exception {
        Log.d("rxjava2", Thread.currentThread().getName());
        // 执行网络操作
        String tvName = getTVNameFromNetwork();
        e.onNext(tvName);
        e.onComplete();
      }
    }).subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe(new Consumer<String>() {
      @Override public void accept(String s) throws Exception {
        Log.d("rxjava2", "--- accept:"+s);
      }
    });
  }

  public String getTVNameFromNetwork() {
    // 假设执行网络操作,获取tvName
    return "三生三世";
  }

如果不设置subscribeOn(Schedulers.io()),那subscribe方法是在主线程运行的,执行网络操作会报NetworkOnMainThreadException;设置observeOn(AndroidSchedulers.mainThread())是为了让accept方法运行在主线程中,便于操作view对象,运行一下:

04-30 16:49:08.291 28442-28659/com.testdes.des I/System.out: RxCachedThreadScheduler-1
04-30 16:49:08.292 28442-28659/com.testdes.des I/System.out: --- accept:三生三世

RxJava2常用操作符

RxJava的操作符非常多,功能也非常强大,这里只能介绍几个常用操作符。

  • Map
    对Observable发射的每一项数据应用一个函数,执行变换操作
    这里写图片描述
    相信很多人都看不懂是什么意思,不懂就对了^_^,看个例子
  public void click(View view) {
    Observable.just(2332182).map(new Function<Integer, String>() {
      @Override public String apply(Integer integer) throws Exception {
        return integer + "@qq.com";
      }
    }).subscribe(new Consumer<String>() {
        @Override public void accept(String s) throws Exception {
          System.out.println("--- accept:" + s);
        }
      }, new Consumer<Throwable>() {
        @Override public void accept(Throwable throwable) throws Exception {
          throwable.printStackTrace();
        }
      });
  }

通过map操作符可以轻松的把Integer转成String,例子中把2332182一串数字转成邮箱地址:2332182@qq.com,运行一下:

04-30 18:46:12.354 1855-1855/com.testdes.des I/System.out: --- accept:2332182@qq.com
  • flatMap
    flatMap将一个发射数据的Observable变换为多个Observables,然后将它们发射的数据合并后放到一个单独的Observable
    这里写图片描述

我们看一个例子,假设需要执行登录操作,登录成功后再获取用户信息,这个过程怎么样通过rxjava来实现?

  public void click(View view) {
    // 登录获取用户信息
    Observable.just(new UserParam("jack", "123"))
        .flatMap(new Function<UserParam, ObservableSource<Integer>>() {
          @Override public ObservableSource<Integer> apply(UserParam userParam) throws Exception {
            // 登录
            int uid = login(userParam);
            return Observable.just(uid);
          }
        }).flatMap(new Function<Integer, ObservableSource<User>>() {
          @Override public ObservableSource<User> apply(Integer uid) throws Exception {
            // 获取用户信息
            User user = getUserInfo(uid);
            return Observable.just(user);
          }
        }).subscribe(new Consumer<User>() {
      @Override public void accept(User user) throws Exception {
        System.out.println(user.toString());
      }
    });

  }

  // 为了演示方便,假设这个login 和 getUserInfo都是调用网络操作
  // 调用登录接口获取uid
  public int login(UserParam param) {
    int uid = 1;
    // get uid from network
    return uid;
  }

  // 获取用户信息
  public User getUserInfo(int uid) {
    User user = new User("jackson", "22", "man");
    // get user info from network
    return user;
  }

flatMap可以把一个Observable转成多个Observables,并且无缝连接在一起,这个是非常巧妙的;由于是模拟的网络操作,所以没有加线程控制也不会有问题,运行一下:

04-30 19:27:17.192 3364-3364/com.testdes.des I/System.out: 用户信息[name:jackson, age:22, sex:man]
  • Debounce
    检测在过了一段指定的时间还没有发射数据时才发射数据
    这里写图片描述

关键字搜索正好符合Debounce使用场景,开发过关键字搜索的童鞋一定知道,每次输入一个字符都会触发EditText的onTextChanged,意味着每输入一个字符就会调用一次搜索接口,而理想的情况应该是当我输入完成的时候再调用接口。输入完成可以定义为当停止输入的时间超过200ms或500ms的时候。
Debounce帮我们解决这个问题,示例如下:
先改下配置,由于rxjava需要和edittext绑定在一起,因此依赖rebinding,rebinding中包含rxjava

compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'

关键字搜索代码实现如下:

public class RxjavaActivity extends AppCompatActivity{

  @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_rxjava);

    EditText mEditText = (EditText) findViewById(R.id.mInput);
    RxTextView.textChanges(mEditText).debounce(200, TimeUnit.MILLISECONDS)
        .subscribeOn(AndroidSchedulers.mainThread())
        .flatMap(new Func1<CharSequence, Observable<List<String>>>() {
          @Override public Observable<List<String>> call(CharSequence charSequence) {
            // do search
            return Observable.just(doSearch(charSequence.toString()));
          }
        }).subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<List<String>>() {
          @Override public void call(List<String> strings) {
            for (String s : strings) {
              System.out.println(s + "\n");
            }
          }
        }, new Action1<Throwable>() {
          @Override public void call(Throwable throwable) {
            throwable.printStackTrace();
          }
        });
  }
  // 搜索接口
  public List<String> doSearch(String key) {
    // 假设调用网络接口获取搜索结果
    List<String> list = new ArrayList<>();
    list.add("abc");
    return list;
  }
}
  • throttleFirst
    防止按钮重复点击可以使用该操作符,代码如下:
    RxView.clicks(button).throttleFirst(1, TimeUnit.SECONDS)
        .subscribe(new Observer<Void>() {
          @Override public void onCompleted() {
          }

          @Override public void onError(Throwable e) {
          }

          @Override public void onNext(Void aVoid) {
            System.out.println("按钮被点击了");
          }
        });
  • merge
    合并操作符,用于合并两个Observable对象
public class RxjavaActivity extends AppCompatActivity{

  @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_rxjava);
    Button button = (Button) findViewById(R.id.button);
    Observable.merge(getCouponFromLocal(), getCouponFromServer())
        .subscribe(new Subscriber<List<String>>() {
          @Override public void onCompleted() {

          }

          @Override public void onError(Throwable e) {

          }

          @Override public void onNext(List<String> strings) {
            for (String s : strings) {
              System.out.println(s + "\n");
            }
          }
        });

  }

  // 本地获取优惠券
  public Observable<List<String>> getCouponFromLocal() {
    List<String> list = new ArrayList<>();
    list.add("满99减10");
    return Observable.just(list);
  }

  // 本地获取优惠券(模拟网络接口调用)
  public Observable<List<String>> getCouponFromServer() {
    List<String> list = new ArrayList<>();
    list.add("满500减100");
    return Observable.just(list);
  }
 }
04-30 23:13:04.249 13568-13568/com.testdes.des I/System.out: 满9910
04-30 23:13:04.249 13568-13568/com.testdes.des I/System.out: 满500100

还有很多其他的操作符,这里就不一一介绍了,详细的可以参考官方wiki: http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Flowable.html#flatMap(io.reactivex.functions.Function)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chengyuan9160

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值