首先要事先声明下这篇博文是转载自如下两篇博客,后续部分也会另外创建一篇博文用于记录自己在RxJava实践中的使用场景:
http://blog.csdn.net/lzyzsd/article/details/50120801
http://blog.csdn.net/theone10211024/article/details/50435325

取数据先检查缓存的场景

取数据,首先检查内存是否有缓存
然后检查文件缓存中是否有
最后才从网络中取
前面任何一个条件满足,就不会执行后面的

final Observable<String> memory = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
if (memoryCache != null) {
subscriber.onNext(memoryCache);
} else {
subscriber.onCompleted();
}
}
});
Observable<String> disk = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
String cachePref = rxPreferences.getString("cache").get();
if (!TextUtils.isEmpty(cachePref)) {
subscriber.onNext(cachePref);
} else {
subscriber.onCompleted();
}
}
});
Observable<String> network = Observable.just("network");
//主要就是靠concat operator来实现
Observable.concat(memory, disk, network)
.first()
.subscribeOn(Schedulers.newThread())
.subscribe(s -> {
memoryCache = "memory";
System.out.println("--------------subscribe: " + s);
});

界面需要等到多个接口并发取完数据,再更新

//拼接两个Observable的输出,不保证顺序,按照事件产生的顺序发送给订阅者
private void testMerge() {
Observable<String> observable1 = DemoUtils.createObservable1().subscribeOn(Schedulers.newThread());
Observable<String> observable2 = DemoUtils.createObservable2().subscribeOn(Schedulers.newThread());

Observable.merge(observable1, observable2)
.subscribeOn(Schedulers.newThread())
.subscribe(System.out::println);
}

一个接口的请求依赖另一个API请求返回的数据

举个例子,我们经常在需要登陆之后,根据拿到的token去获取消息列表。
这里用RxJava主要解决嵌套回调的问题,有一个专有名词叫Callback hell

NetworkService.getToken("username", "password")
.flatMap(s -> NetworkService.getMessage(s))
.subscribe(s -> {
System.out.println("message: " + s);
});

界面按钮需要防止连续点击的情况

RxView.clicks(findViewById(R.id.btn_throttle))
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe(aVoid -> {
System.out.println("click");
});

响应式的界面

比如勾选了某个checkbox,自动更新对应的preference

SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
RxSharedPreferences rxPreferences = RxSharedPreferences.create(preferences);

Preference<Boolean> checked = rxPreferences.getBoolean("checked", true);

CheckBox checkBox = (CheckBox) findViewById(R.id.cb_test);
RxCompoundButton.checkedChanges(checkBox)
.subscribe(checked.asAction());

复杂的数据变换

Observable.just("1", "2", "2", "3", "4", "5")
.map(Integer::parseInt)
.filter(s -> s > 1)
.distinct()
.take(3)
.reduce((integer, integer2) -> integer.intValue() + integer2.intValue())
.subscribe(System.out::println);//9

Scheduler线程切换

这种场景经常会在“后台线程取数据,主线程展示”的模式中看见

Observable.just(1, 2, 3, 4)  
.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer number) {
Log.d(tag, "number:" + number);
}
});

使用debounce做textSearch

用简单的话讲就是当N个结点发生的时间太靠近(即发生的时间差小于设定的值T),debounce就会自动过滤掉前N-1个结点。比如在做百度地址联想的时候,可以使用debounce减少频繁的网络请求。避免每输入(删除)一个字就做一次联想

RxTextView.textChangeEvents(inputEditText)  
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<TextViewTextChangeEvent>() {
@Override
public void onCompleted() {
log.d("onComplete");
}

@Override
public void onError(Throwable e) {
log.d("Error");
}

@Override
public void onNext(TextViewTextChangeEvent onTextChangeEvent) {
log.d(format("Searching for %s", onTextChangeEvent.text().toString()));
}
});

Retrofit结合RxJava做网络请求框架

这里不作详解,具体的介绍可以看扔物线的这篇文章,对RxJava的入门者有很大的启发。其中也讲到了RxJava和Retrofit如何结合来实现更简洁的代码

RxJava代替EventBus进行数据传递:RxBus

注意:RxBus并不是一个库,而是一种模式,是使用了RxJava的思想来达到EventBus的数据传递效果。这篇文章把RxBus讲的比较详细。

使用combineLatest合并最近N个结点

例如:注册的时候所有输入信息(邮箱、密码、电话号码等)合法才点亮注册按钮。

Observable<CharSequence> _emailChangeObservable = RxTextView.textChanges(_email).skip(1);  
Observable<CharSequence> _passwordChangeObservable = RxTextView.textChanges(_password).skip(1);
Observable<CharSequence> _numberChangeObservable = RxTextView.textChanges(_number).skip(1);

Observable.combineLatest(_emailChangeObservable,
_passwordChangeObservable,
_numberChangeObservable,
new Func3<CharSequence, CharSequence, CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence newEmail,
CharSequence newPassword,
CharSequence newNumber) {

Log.d("xiayong",newEmail+" "+newPassword+" "+newNumber);
boolean emailValid = !isEmpty(newEmail) &&
EMAIL_ADDRESS.matcher(newEmail).matches();
if (!emailValid) {
_email.setError("Invalid Email!");
}

boolean passValid = !isEmpty(newPassword) && newPassword.length() > 8;
if (!passValid) {
_password.setError("Invalid Password!");
}

boolean numValid = !isEmpty(newNumber);
if (numValid) {
int num = Integer.parseInt(newNumber.toString());
numValid = num > 0 && num <= 100;
}
if (!numValid) {
_number.setError("Invalid Number!");
}

return emailValid && passValid && numValid;

}
})//
.subscribe(new Observer<Boolean>() {
@Override
public void onCompleted() {
log.d("completed");
}

@Override
public void onError(Throwable e) {
log.d("Error");
}

@Override
public void onNext(Boolean formValid) {
_btnValidIndicator.setEnabled(formValid);
}
});

使用merge合并两个数据源。

例如一组数据来自网络,一组数据来自文件,需要合并两组数据一起展示。

Observable.merge(getDataFromFile(), getDataFromNet())  
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
log.d("done loading all data");
}

@Override
public void onError(Throwable e) {
log.d("error");
}

@Override
public void onNext(String data) {
log.d("all merged data will pass here one by one!")
});

使用timer做定时操作。当有“x秒后执行y操作”类似的需求的时候,想到使用timer

例如:2秒后输出日志“hello world”,然后结束。

Observable.timer(2, TimeUnit.SECONDS)  
.subscribe(new Observer<Long>() {
@Override
public void onCompleted() {
log.d ("completed");
}

@Override
public void onError(Throwable e) {
log.e("error");
}

@Override
public void onNext(Long number) {
log.d ("hello world");
}
});

使用interval做周期性操作。当有“每隔xx秒后执行yy操作”类似的需求的时候,想到使用interval

例如:每隔2秒输出日志“helloworld”。

Observable.interval(2, TimeUnit.SECONDS)  
.subscribe(new Observer<Long>() {
@Override
public void onCompleted() {
log.d ("completed");
}

@Override
public void onError(Throwable e) {
log.e("error");
}

@Override
public void onNext(Long number) {
log.d ("hello world");
}
});

使用schedulePeriodically做轮询请求

Observable.create(new Observable.OnSubscribe<String>() {  
@Override
public void call(final Subscriber<? super String> observer) {

Schedulers.newThread().createWorker()
.schedulePeriodically(new Action0() {
@Override
public void call() {
observer.onNext(doNetworkCallAndGetStringResult());
}
}, INITIAL_DELAY, POLLING_INTERVAL, TimeUnit.MILLISECONDS);
}
}).subscribe(new Action1<String>() {
@Override
public void call(String s) {
log.d("polling….”));
}
})

RxJava进行数组、list的遍历

String[] names = {"Tom", "Lily", "Alisa", "Sheldon", "Bill"};  
Observable
.from(names)
.subscribe(new Action1<String>() {
@Override
public void call(String name) {
log.d(name);
}
});

“在不指定线程的情况下, RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件。如果需要切换线程,就需要用到 Scheduler (调度器)。”
(来自给 Android 开发者的 RxJava 详解)
Scheduler 的作用是用于指定某个代码逻辑应该运行在哪个线程。

RxJava中的5种默认调度器

RxJava默认情况下提供了5种调度器:
.io()

用于读写文件、读写数据库、网络信息交互等IO操作
.io() is backed by an unbounded thread-pool and is the sort of thing you'd use for non-computationally intensive tasks, that is stuff that doesn't put much load on the CPU. So yep interaction with the file system, interaction with databases or services on a different host are good examples.
创建一个用于IO操作的线程的调度器,内部实现机制是通过一个自动增长的线程池来实现的,这种机制可以用于异步操作阻塞IO工作,不要使用这个调度器来完成计算类工作,要完成计算类工作请使用computation()

.computation()

.computation() is backed by a bounded thread-pool with size equal to the number of available processors. If you tried to schedule cpu intensive work in parallel across more than the available processors (say using newThread()) then you are up for thread creation overhead and context switching overhead as threads vie for a processor and it's potentially a big performance hit.
这个调度器一般用于运行CPU密集型的计算任务,这些任务不会被IO等阻塞操作给阻塞,这个 Scheduler 使用大小为 CPU 核数的线程池来完成工作。同上面介绍的IO调度器一样,不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。

.immediate()

Creates and returns a Scheduler that executes work immediately on the current thread.
使用当前线程来完成任务,这是默认的 Scheduler。

.newThread()

Creates and returns a Scheduler that creates a new Thread for each unit of work.
创建新线程,并在新线程执行操作。

.trampoline()

Creates and returns a Scheduler that queues work on the current thread to be executed after the current work completes.
这种方式拥有一个队列,当我们想在当前线程执行一个任务,而是立即,我们可以先将这个任务存入队列,这个调度器将会按顺序运行队列中每一个任务。

为被观察者及订阅者指定线程

要为某个任务指定运行在哪个线程可以使用subscribeOn() 和 observeOn()两个方法:
subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。
observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。
这里大家可能会碰到一个 AndroidSchedulers.mainThread(),顾名思义它就是Android的主线程。

int drawableRes = R.drawable.testImage;
ImageView imageView = (ImageView)findviewById(R.id.imageView);
Observable.create(new OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Drawable>() {
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
});

observeOn() 指定的是它之后的操作所运行的线程。因此如果有多次切换线程的需求,只要在每个想要切换线程的位置调用一次,和observeOn不同的是subscribeOn()只能调用一次。

Observable.from(testEventList) // IO 线程,由 subscribeOn() 指定
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.flatmap(......) // 新线程,由 observeOn() 指定
.observeOn(Schedulers.io())
.map(.......) // IO 线程,由 observeOn() 指定
.observeOn(AndroidSchedulers.mainThread)
.subscribe(subscriber); // 回到主线程

在学习的期间我们会看到还有个比较有意思是方法-onBackpressureBuffer()
它将将告诉Observable发射的数据如果比观察者消费的数据要更快的话,它必须把它们存储在缓存中并提供一个合适的时间给它们。从而不会照成消费者的拥堵。

  • Merege
    Merge 操作能够将有多个事件源合并起来产生一个事件源,也就是多输入单输出系统。最后的结果就和一个事件源发出事件一样,Merge操作符,在使用的时候有两点需要注意:
    和flatMap一样生成的数据源有可能会相互交错,并且当某一个Observable出错而导致发出onError的时候,merge的过程会被终止并将错误分发给订阅者,如果不想要在发生异常的时候中断merge,可以使用MeregeDelayError它在merge结束后再将数据分发出去。
  1. Merege
  2. mergeDelayError
    Observable<Integer> odds = Observable.just(1, 3, 5).subscribeOn(someScheduler);
    Observable<Integer> evens = Observable.just(2, 4, 6);
    Observable.merge(odds, evens)
    .subscribe(new Subscriber<Integer>() {
    @Override
    public void onNext(Integer item) {
    System.out.println("Next: " + item);
    }

    @Override
    public void onError(Throwable error) {
    System.err.println("Error: " + error.getMessage());
    }

    @Override
    public void onCompleted() {
    System.out.println("Sequence complete.");
    }
    });

运行结果:

Next: 1
Next: 3
Next: 5
Next: 2
Next: 4
Next: 6
Sequence complete.
  • Zip

Zip 允许接收多个事件源,并且将对应的事件通过一个方法处理后产生的结果添加到Observable中统一发布出去,这里需要注意的是最终的事件源个数取决于原始数据源中个数最少的那个,看下下面的例子:
事件源observable1 有20个事件,而observable2有10个事件,两个合成起来后最终产生的结果事件为10个。也就是最少的那个:

Observable<Integer> observable1 = Observable.range(1,20);
Observable<Integer> observable2 = Observable.range(1,10);
Observable.zip(observable1, observable2, new Func2<Integer, Integer, Integer>() {
@Override
public Integer call(Integer integer, Integer integer2) {
Log.i("xiaohai.lin", integer.toString() + " + " + integer2.toString() + " = " + (integer.intValue() + integer2.intValue()));
return integer.intValue() + integer2.intValue();
}
}).subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.i("xiaohai.lin", integer.toString() + " =================== ");
}
});
05-22 14:46:16.246 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 1 + 1 = 2
05-22 14:46:16.246 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 2 ===================
05-22 14:46:16.246 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 2 + 2 = 4
05-22 14:46:16.246 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 4 ===================
05-22 14:46:16.246 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 3 + 3 = 6
05-22 14:46:16.246 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 6 ===================
05-22 14:46:16.246 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 4 + 4 = 8
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 8 ===================
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 5 + 5 = 10
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 10 ===================
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 6 + 6 = 12
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 12 ===================
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 7 + 7 = 14
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 14 ===================
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 8 + 8 = 16
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 16 ===================
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 9 + 9 = 18
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 18 ===================
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 10 + 10 = 20
05-22 14:46:16.247 16854-16854/com.idealist.rxandroiddemo I/xiaohai.lin: 20 ===================
  • CombineLatest

    CombinLatest可以将多个数据源的最新(最后产生的数据)给组装起来,然然后发送出去.

    Observable<Long> observable1 = Observable.interval(3, SECONDS);
    Observable<Long> observable2 = Observable.interval(1, SECONDS);
    Observable.combineLatest(observable1, observable2, new Func2<Long, Long, String>() {
    @Override
    public String call(Long aLong, Long aLong2) {
    Log.i("xiaohai.lin", "aLong = " + aLong + " : aLong2 = " + aLong2);
    return (aLong+"====="+aLong2);
    }
    }).subscribe(new Subscriber<String>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onNext(String s) {
    Log.i("xiaohai.lin", s);
    }
    });

    运行结果如下所示:每次某个事件源有事件产生的时候,都会取另一个事件源的最新事件进行组合:

    05-22 15:37:23.431 4781-4839/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 0 : aLong2 = 1
    05-22 15:37:23.431 4781-4839/com.idealist.rxandroiddemo I/xiaohai.lin: 0=====1
    05-22 15:37:23.432 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 0 : aLong2 = 2
    05-22 15:37:23.432 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 0=====2
    05-22 15:37:24.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 0 : aLong2 = 3
    05-22 15:37:24.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 0=====3
    05-22 15:37:25.431 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 0 : aLong2 = 4
    05-22 15:37:25.431 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 0=====4
    05-22 15:37:26.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 0 : aLong2 = 5
    05-22 15:37:26.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 0=====5
    05-22 15:37:26.430 4781-4839/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 1 : aLong2 = 5
    05-22 15:37:26.431 4781-4839/com.idealist.rxandroiddemo I/xiaohai.lin: 1=====5
    05-22 15:37:27.431 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 1 : aLong2 = 6
    05-22 15:37:27.431 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 1=====6
    05-22 15:37:28.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 1 : aLong2 = 7
    05-22 15:37:28.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 1=====7
    05-22 15:37:29.430 4781-4839/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 2 : aLong2 = 7
    05-22 15:37:29.430 4781-4839/com.idealist.rxandroiddemo I/xiaohai.lin: 2=====7
    05-22 15:37:29.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 2 : aLong2 = 8
    05-22 15:37:29.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 2=====8
    05-22 15:37:30.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 2 : aLong2 = 9
    05-22 15:37:30.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 2=====9
    05-22 15:37:31.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 2 : aLong2 = 10
    05-22 15:37:31.430 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 2=====10
    05-22 15:37:32.431 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: aLong = 2 : aLong2 = 11
    05-22 15:37:32.431 4781-4841/com.idealist.rxandroiddemo I/xiaohai.lin: 2=====11

  • StartWith

StartWith操作符会在源事件源的前面插上事件后发送:

Observable<Integer> observable1 = Observable.range(1, 10);
Observable<Integer> observable2 = Observable.range(11, 20);
observable1.startWith(observable2).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer o) {
Log.i("xiaohai.lin", o.toString());
}
});

运行结果:

05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 11
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 12
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 13
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 14
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 15
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 16
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 17
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 18
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 19
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 20
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 21
05-22 15:48:12.555 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 22
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 23
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 24
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 25
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 26
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 27
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 28
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 29
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 30
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 1
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 2
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 3
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 4
05-22 15:48:12.556 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 5
05-22 15:48:12.557 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 6
05-22 15:48:12.557 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 7
05-22 15:48:12.557 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 8
05-22 15:48:12.557 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 9
05-22 15:48:12.557 15979-15979/com.idealist.rxandroiddemo I/xiaohai.lin: 10

前一篇博客我们介绍了如何对原始事件源进行过滤,这一篇我们将给大家介绍如何对事件源进行转换。什么是事件源转换?打个简单的比喻,我们原始事件源发出某个网站的站名,但是我们订阅者需要知道的是网站的地址,
这时候就可以通过事件源的转换来实现。

  • Buffer
    顾名思义,就是对事件源进行缓存,这里的缓存可以按照时间进行缓存,也可以按事件源个数进行缓存,缓存后将数据以数组集合的方式发送出去。

    方式一:
    只是单纯的缓存,这里buffer方法只有一个参数,那就是缓存大小,下面例子中,该转换器集满5个事件后就以列表的形式发出。

    Observable.range(1, 100).buffer(5).subscribe(new Subscriber<List<Integer>>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onNext(List<Integer> integers) {
    Log.i("xiaohai.lin", integers.toString());
    }
    });

    整个运行结果如下所示:

    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [1, 2, 3, 4, 5]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [6, 7, 8, 9, 10]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [11, 12, 13, 14, 15]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [16, 17, 18, 19, 20]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [21, 22, 23, 24, 25]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [26, 27, 28, 29, 30]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [31, 32, 33, 34, 35]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [36, 37, 38, 39, 40]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [41, 42, 43, 44, 45]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [46, 47, 48, 49, 50]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [51, 52, 53, 54, 55]
    05-22 09:12:48.191 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [56, 57, 58, 59, 60]
    05-22 09:12:48.192 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [61, 62, 63, 64, 65]
    05-22 09:12:48.192 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [66, 67, 68, 69, 70]
    05-22 09:12:48.192 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [71, 72, 73, 74, 75]
    05-22 09:12:48.192 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [76, 77, 78, 79, 80]
    05-22 09:12:48.192 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [81, 82, 83, 84, 85]
    05-22 09:12:48.192 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [86, 87, 88, 89, 90]
    05-22 09:12:48.192 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [91, 92, 93, 94, 95]
    05-22 09:12:48.192 25401-25401/com.idealist.rxandroiddemo I/xiaohai.lin: [96, 97, 98, 99, 100]

    方式二:
    每M个事件中抽取N个事件源,以列表的形式发出,比如下面的例子,从1到100,每隔10个事件源,在这10个事件源中发送前5个。

    Observable.range(1, 100).buffer(5, 10).subscribe(new Subscriber<List<Integer>>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onNext(List<Integer> integers) {
    Log.i("xiaohai.lin", integers.toString());
    }
    });

    输出结果:

    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [1, 2, 3, 4, 5]
    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [11, 12, 13, 14, 15]
    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [21, 22, 23, 24, 25]
    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [31, 32, 33, 34, 35]
    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [41, 42, 43, 44, 45]
    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [51, 52, 53, 54, 55]
    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [61, 62, 63, 64, 65]
    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [71, 72, 73, 74, 75]
    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [81, 82, 83, 84, 85]
    05-22 09:11:03.886 23946-23946/com.idealist.rxandroiddemo I/xiaohai.lin: [91, 92, 93, 94, 95]

    方式三:
    在某个时间间隔内,缓存数据,时间一到就把缓存的数据以列表的形式发送出去:
    下面事件源是一个每隔1秒的事件源,缓冲器是一个每隔4秒缓存一次的缓存器,到达4秒后将缓存数据发送出去:

    Observable.interval(1, TimeUnit.SECONDS).buffer(4, TimeUnit.SECONDS).subscribe(new Subscriber<List<Long>>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onNext(List<Long> longs) {
    Log.i("xiaohai.lin", longs.toString());
    }
    });

    下面是输出结果:

    05-22 09:19:01.614 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [0, 1, 2]
    05-22 09:19:05.611 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [3, 4, 5, 6]
    05-22 09:19:09.612 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [7, 8, 9, 10]
    05-22 09:19:13.611 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [11, 12, 13, 14]
    05-22 09:19:17.612 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [15, 16, 17, 18]
    05-22 09:19:21.611 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [19, 20, 21, 22]
    05-22 09:19:25.611 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [23, 24, 25, 26]
    05-22 09:19:29.612 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [27, 28, 29, 30]
    05-22 09:19:33.611 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [31, 32, 33, 34]
    05-22 09:19:37.612 31383-31451/com.idealist.rxandroiddemo I/xiaohai.lin: [35, 36, 37, 38]
  • Window
    Window和上述的buffer类似,不同之处在于window是将数据源以一些小的Observable对象发送出去,这些小的Observable对象封装了每次缓冲的数据。

    方式一:

    Observable.range(1, 10).window(3).subscribe(new Subscriber<Observable<Integer>>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onNext(final Observable<Integer> integerObservable) {
    integerObservable.subscribe(new Subscriber<Integer>() {
    @Override
    public void onCompleted() {
    Log.i("xiaohai.lin", "-------------------------------------------------------------");
    }

    @Override
    public void onError(Throwable e) {
    }

    @Override
    public void onNext(Integer integer) {
    Log.i("xiaohai.lin", "integer " + integer.toString() +" : "+ integerObservable.toString());
    }
    });
    }
    });

    下面是运行的结果:

    05-22 10:50:28.687 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 1 : rx.internal.operators.UnicastSubject@c9c87a
    05-22 10:50:28.688 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 2 : rx.internal.operators.UnicastSubject@c9c87a
    05-22 10:50:28.688 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 3 : rx.internal.operators.UnicastSubject@c9c87a
    05-22 10:50:28.688 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: -------------------------------------------------------------
    05-22 10:50:28.689 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 4 : rx.internal.operators.UnicastSubject@95bb82b
    05-22 10:50:28.689 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 5 : rx.internal.operators.UnicastSubject@95bb82b
    05-22 10:50:28.689 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 6 : rx.internal.operators.UnicastSubject@95bb82b
    05-22 10:50:28.689 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: -------------------------------------------------------------
    05-22 10:50:28.689 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 7 : rx.internal.operators.UnicastSubject@a6cb488
    05-22 10:50:28.689 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 8 : rx.internal.operators.UnicastSubject@a6cb488
    05-22 10:50:28.689 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 9 : rx.internal.operators.UnicastSubject@a6cb488
    05-22 10:50:28.689 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: -------------------------------------------------------------
    05-22 10:50:28.689 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: integer 10 : rx.internal.operators.UnicastSubject@87a7e21
    05-22 10:50:28.701 12664-12664/com.idealist.rxandroiddemo I/xiaohai.lin: -------------------------------------------------------------

    方式二:

    Observable.range(1, 50).window(5,10).subscribe(new Subscriber<Observable<Integer>>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onNext(final Observable<Integer> integerObservable) {
    integerObservable.subscribe(new Subscriber<Integer>() {
    @Override
    public void onCompleted() {
    Log.i("xiaohai.lin", "-------------------------------------------------------------");
    }

    @Override
    public void onError(Throwable e) {
    }

    @Override
    public void onNext(Integer integer) {
    Log.i("xiaohai.lin", "integer " + integer.toString() +" : "+ integerObservable.toString());
    }
    });
    }
    });

    运行结果:

    05-22 10:55:17.413 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 1 : rx.internal.operators.UnicastSubject@c9c87a
    05-22 10:55:17.413 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 2 : rx.internal.operators.UnicastSubject@c9c87a
    05-22 10:55:17.413 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 3 : rx.internal.operators.UnicastSubject@c9c87a
    05-22 10:55:17.413 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 4 : rx.internal.operators.UnicastSubject@c9c87a
    05-22 10:55:17.413 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 5 : rx.internal.operators.UnicastSubject@c9c87a
    05-22 10:55:17.413 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: -------------------------------------------------------------
    05-22 10:55:17.414 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 11 : rx.internal.operators.UnicastSubject@95bb82b
    05-22 10:55:17.414 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 12 : rx.internal.operators.UnicastSubject@95bb82b
    05-22 10:55:17.414 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 13 : rx.internal.operators.UnicastSubject@95bb82b
    05-22 10:55:17.414 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 14 : rx.internal.operators.UnicastSubject@95bb82b
    05-22 10:55:17.414 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 15 : rx.internal.operators.UnicastSubject@95bb82b
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: -------------------------------------------------------------
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 21 : rx.internal.operators.UnicastSubject@a6cb488
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 22 : rx.internal.operators.UnicastSubject@a6cb488
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 23 : rx.internal.operators.UnicastSubject@a6cb488
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 24 : rx.internal.operators.UnicastSubject@a6cb488
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 25 : rx.internal.operators.UnicastSubject@a6cb488
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: -------------------------------------------------------------
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 31 : rx.internal.operators.UnicastSubject@87a7e21
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 32 : rx.internal.operators.UnicastSubject@87a7e21
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 33 : rx.internal.operators.UnicastSubject@87a7e21
    05-22 10:55:17.415 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 34 : rx.internal.operators.UnicastSubject@87a7e21
    05-22 10:55:17.416 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 35 : rx.internal.operators.UnicastSubject@87a7e21
    05-22 10:55:17.416 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: -------------------------------------------------------------
    05-22 10:55:17.416 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 41 : rx.internal.operators.UnicastSubject@d1bf246
    05-22 10:55:17.416 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 42 : rx.internal.operators.UnicastSubject@d1bf246
    05-22 10:55:17.416 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 43 : rx.internal.operators.UnicastSubject@d1bf246
    05-22 10:55:17.416 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 44 : rx.internal.operators.UnicastSubject@d1bf246
    05-22 10:55:17.416 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: integer 45 : rx.internal.operators.UnicastSubject@d1bf246
    05-22 10:55:17.416 19057-19057/com.idealist.rxandroiddemo I/xiaohai.lin: -------------------------------------------------------------

    方式三:

    Observable.interval(1, TimeUnit.SECONDS).window(3, TimeUnit.SECONDS).subscribe(new Subscriber<Observable<Long>>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onNext(Observable<Long> longObservable) {
    longObservable.subscribe(new Subscriber<Long>() {
    @Override
    public void onCompleted() {
    Log.i("xiaohai.lin","=====================================");
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onNext(Long aLong) {
    Log.i("xiaohai.lin","times = "+aLong);
    }
    });
    }
    });

    运行结果:

    05-22 10:58:40.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 0
    05-22 10:58:41.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 1
    05-22 10:58:42.815 22345-22430/com.idealist.rxandroiddemo I/xiaohai.lin: =====================================
    05-22 10:58:42.819 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 2
    05-22 10:58:43.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 3
    05-22 10:58:44.819 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 4
    05-22 10:58:45.815 22345-22430/com.idealist.rxandroiddemo I/xiaohai.lin: =====================================
    05-22 10:58:45.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 5
    05-22 10:58:46.819 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 6
    05-22 10:58:47.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 7
    05-22 10:58:48.816 22345-22430/com.idealist.rxandroiddemo I/xiaohai.lin: =====================================
    05-22 10:58:48.819 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 8
    05-22 10:58:49.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 9
    05-22 10:58:50.819 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 10
    05-22 10:58:51.815 22345-22430/com.idealist.rxandroiddemo I/xiaohai.lin: =====================================
    05-22 10:58:51.819 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 11
    05-22 10:58:52.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 12
    05-22 10:58:53.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 13
    05-22 10:58:54.816 22345-22430/com.idealist.rxandroiddemo I/xiaohai.lin: =====================================
    05-22 10:58:54.819 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 14
    05-22 10:58:55.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 15
    05-22 10:58:56.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 16
    05-22 10:58:57.815 22345-22430/com.idealist.rxandroiddemo I/xiaohai.lin: =====================================
    05-22 10:58:57.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 17
    05-22 10:58:58.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 18
    05-22 10:58:59.818 22345-22431/com.idealist.rxandroiddemo I/xiaohai.lin: times = 19
    05-22 10:59:00.815 22345-22430/com.idealist.rxandroiddemo I/xiaohai.lin: =====================================
  • Map
    Map 转换一般用在将原始数据源的每个事件,一个个进行处理后,发送出去,它对数据的转化是直接进行的和后面将要介绍的FlatMap是有一定的区别的,FlatMap 是通过Observables过渡的会产生中间Observables:
    下面的例子是一个获取手机中的Apk包名然后将其转换为大写的例子,它是一个个直接进行转换的。

    final Intent mainIntent = new Intent(Intent.ACTION_MAIN);
    mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    List<ResolveInfo> activityInfoList = getPackageManager().queryIntentActivities(mainIntent, 0);
    Observable.from(activityInfoList).map(new Func1<ResolveInfo, ResolveInfo>() {
    @Override
    public ResolveInfo call(ResolveInfo resolveInfo) {
    resolveInfo.activityInfo.packageName = resolveInfo.activityInfo.packageName.toUpperCase();
    return resolveInfo;
    }
    }).subscribe(new Subscriber<ResolveInfo>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable throwable) {
    }
    @Override
    public void onNext(ResolveInfo resolveInfo) {
    Log.i("xiaohai.lin", resolveInfo.activityInfo.packageName);
    }
    });

    下面是输出的结果:

    05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.CONTACTS
    05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.DIALER
    05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.MMS
    05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.VENDING
    05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.SETTINGS
    05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.CALENDAR
    05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.MEDIATEK.CAMERA
    05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.CHROME
    05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.DESKCLOCK
    05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.APPS.DOCS
    05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.GM
    05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.TALK
    05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.MUSIC
    05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.APPS.PHOTOS
    05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.YOUTUBE
  • FlatMap

和Map一样都是能够将原始数据转换后将其发送出去,但是它是将原始的Observable转换成另外一个Observable后将其以Observable形式发送出去。

下面是Map和Flatmap的区别:[来自网上]
Map是在一个item被发射之后,到达Map处经过转换变成另一个item,然后继续往下走;
flapMap是item被发射之后,到达flatMap 处经过转换变成一个Observable,而这个Observable并不会直接被发射出去,而是会立即被激活,然后把它发射出的每个 item 都传入流中,再继续走下去。
所以 flatMap 和 map 有两个区别:
经过 Observable 的转换,相当于重新开了一个异步的流;
item 被分散了,个数发生了变化。

final Intent mainIntent = new Intent(Intent.ACTION_MAIN);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> activityInfoList = getPackageManager().queryIntentActivities(mainIntent, 0);
Observable.from(activityInfoList).flatMap(new Func1<ResolveInfo, Observable<ResolveInfo>>() {
@Override
public Observable<ResolveInfo> call(ResolveInfo resolveInfo) {
resolveInfo.activityInfo.packageName = resolveInfo.activityInfo.packageName.toUpperCase();
return Observable.just(resolveInfo);
}
}).subscribe(new Subscriber<ResolveInfo>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(ResolveInfo resolveInfo) {
Log.i("xiaohai.lin", resolveInfo.activityInfo.packageName);
}
});

下面是输出的结果:

05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.CONTACTS
05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.DIALER
05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.MMS
05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.VENDING
05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.SETTINGS
05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.CALENDAR
05-22 09:39:14.200 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.MEDIATEK.CAMERA
05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.CHROME
05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.ANDROID.DESKCLOCK
05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.APPS.DOCS
05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.GM
05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.TALK
05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.MUSIC
05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.APPS.PHOTOS
05-22 09:39:14.201 10071-10071/com.idealist.rxandroiddemo I/xiaohai.lin: COM.GOOGLE.ANDROID.YOUTUBE
  • concatMap
    flatMap()与concatMap()大体的行为是一致的,但是flatMap()可能交错的发送事件,最终结果的顺序可能并是不原始Observable发送时的顺序。这有可能会带来严重的影响,比如我们要计算1到100的每10个元素分成一组,计算每组的和,但是如果有交错发生,
    这样每组计算的和和我们期望的将会差别很大,这时候就可以使用与之类似的concatMap()操作符来避免数据的交错发生。

  • SwitchMap
    switchMap()和flatMap()大体的行为是一致的,除了一点:当原始Observable发射一个新的数据(Observable)时,它将取消订阅并停止监视之前那个数据的Observable产生的Observable,并开始监视当前这一个。

  • Cast
    Cast将Observable发射的数据强制转化为另外一种类型,这个用得比较少,在这里先不做介绍:

  • GroupBy

GroupBy操作符将原始事件源的数据按照key来拆分成一些小的Observable,拆封规则在groupBy中实现,然后将这些同组的事件源封装成一个Observable,再将这些事件源一组一组发送出去:
下面的例子将会扫描手机中的apk包名,将其按照报名的前两个前缀分组,并统计每个类别的应用的数量:

final Intent mainIntent = new Intent(Intent.ACTION_MAIN);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List<ResolveInfo> activityInfoList = getPackageManager().queryIntentActivities(mainIntent, 0);
Observable.from(activityInfoList).groupBy(new Func1<ResolveInfo, String>() {
@Override
public String call(ResolveInfo resolveInfo) {
String packageName = resolveInfo.activityInfo.packageName;
return packageName.substring(0,packageName.lastIndexOf("."));
}
}).subscribe(new Subscriber<GroupedObservable<String, ResolveInfo>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(final GroupedObservable<String, ResolveInfo> stringResolveInfoGroupedObservable) {
stringResolveInfoGroupedObservable.count().subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
Log.i("xiaohai.lin", stringResolveInfoGroupedObservable.getKey() + " Contain :" + integer.toString() + " elemnts");
}
});
}
});

运行结果如下:

05-22 10:23:20.672 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com  Contain :4  elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.flysnow Contain :1 elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.qihoo360 Contain :1 elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.everimaging Contain :1 elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.android Contain :9 elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.jianshu Contain :1 elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.sohu.inputmethod Contain :1 elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: io.manong Contain :1 elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.google.android Contain :7 elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.foxit.mobile.pdf Contain :1 elemnts
05-22 10:23:20.674 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.alcatel Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.liangfeizc Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.emoji.keyboard Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.tencent Contain :3 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.baozou.baozou Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.antonioleiva Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.alpha Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.jrdcom Contain :3 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.douban Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.zhihu Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.baidu Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.facebook Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: vStudio.Android Contain :1 elemnts
05-22 10:23:20.675 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.ss.android.article Contain :1 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.android.providers.downloads Contain :1 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: cn.wps Contain :1 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.netease Contain :1 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.ticktick Contain :1 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.codoon Contain :1 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.tct Contain :6 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.google.android.apps Contain :3 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.dajie Contain :1 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.tcl Contain :1 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.idealist Contain :3 elemnts
05-22 10:23:20.676 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.baozoumanhua Contain :1 elemnts
05-22 10:23:20.677 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.hitherejoe Contain :1 elemnts
05-22 10:23:20.677 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.mediatek Contain :1 elemnts
05-22 10:23:20.677 15638-15638/com.idealist.rxandroiddemo I/xiaohai.lin: com.chinamworld Contain :1 elemnts
  • Scan
    Scan操作符用于对一个序列的数据应用一个函数,将函数返回值作为下一个引用这个函数时候的第一个参数使用。
    Observable.range(1, 10).scan(new Func2<Integer, Integer, Integer>() {
    @Override
    public Integer call(Integer integer, Integer integer2) {
    return integer.intValue() + integer2.intValue();
    }
    }).subscribe(new Subscriber<Integer>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onNext(Integer integer) {
    Log.i("xiaohai.lin", "====" + integer.toString());
    }
    });
    下面是运行的结果,这个例子实际上是求1到10的整数和:
    05-22 10:40:26.484 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====1
    05-22 10:40:26.484 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====3
    05-22 10:40:26.484 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====6
    05-22 10:40:26.484 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====10
    05-22 10:40:26.485 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====15
    05-22 10:40:26.485 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====21
    05-22 10:40:26.485 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====28
    05-22 10:40:26.485 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====36
    05-22 10:40:26.485 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====45
    05-22 10:40:26.485 1639-1639/com.idealist.rxandroiddemo I/xiaohai.lin: ====55

上一篇博文我们已经介绍了事件源的生成方法,介绍了8种事件源,但是这些事件源只是一些基本的事件源,一般都是触发我们需要事件的触发器,比如我们需要定时刷新网络,那么我们需要使用到interval作为原始的事件源,
但是我们一般会在这个事件源的基础上作一定的处理,而对事件源的过滤也属于这些过滤中的一种,在本篇博文中我们就来学习下这些事件源的过滤方法:

  • Filter
    filter()方法根据我们传入的判决方法过滤我们不想要的值.

    Observable.timer(10,1, TimeUnit.SECONDS)
    .filter(new Func1<Long, Boolean>() {
    @Override
    public Boolean call(Long aLong) {
    return (aLong % 2 == 0);
    }
    })
    .subscribe(new Subscriber<Long>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable throwable) {
    }
    @Override
    public void onNext(Long aLong) {
    Log.i("xiaohai.lin", "aLong = " + aLong);
    }
    });

    这个方法返回true的事件将会被选中留下。

  • Take
    只接收从原始事件源发出的前N个元素。

    Observable.timer(10,1, TimeUnit.SECONDS).take(10)
    .subscribe(new Subscriber<Long>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable throwable) {
    }
    @Override
    public void onNext(Long aLong) {
    Log.i("xiaohai.lin", "aLong = " + aLong);
    }
    });
  • TakeLast
    只接收从原始事件源发出的最后N个元素

    Observable.range(10,100).takeLast(10)
    .subscribe(new Subscriber<Long>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable throwable) {
    }
    @Override
    public void onNext(Integer integer) {
    Log.i("xiaohai.lin",String.valueOf(integer));
    }
    });
  • Skip
    忽略从原始事件源发出的前N个元素。

    Observable.interval(1, TimeUnit.SECONDS).skip(10).subscribe(new Subscriber<Long>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable throwable) {
    }
    @Override
    public void onNext(Long aLong) {
    Log.i("xiaohai.lin", "aLong = " + aLong);
    }
    });
  • SkipLast
    忽略从原始事件源发出的最后N个元素

Observable.range(1,100).skipLast(10).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(Integer integer) {
Log.i("xiaohai.lin",String.valueOf(integer));
}
});
  • Fist , Last , firstOrDefault , lastOrDefault

first() : 只留下原始数据源的第一个元素

Observable.range(1, 10).first().subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(Integer integer) {
Log.i("xiaohai.lin",String.valueOf(integer));
}
});

last() : 只留下原始数据源的最后一个元素

Observable.range(1, 10).last().subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(Integer integer) {
Log.i("xiaohai.lin",String.valueOf(integer));
}
});

firstOrDefault():当事件源不存在的时候指定发射一个默认的值
lastOrDefault() : 当事件源不存在的时候指定发射一个默认的值

  • ElementAt ElementAtOrDefault

    ElementAt 返回第N个元素。
    ElementAtOrDefault 返回第N个元素如果没有指定的元素将用某个默认值来替代

    Observable.range(1, 100).elementAt(50).subscribe(new Subscriber<Integer>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable throwable) {
    }
    @Override
    public void onNext(Integer integer) {
    Log.i("xiaohai.lin",String.valueOf(integer));
    }
    });

    Observable.range(1, 100).elementAtOrDefault(1000,500).subscribe(new Subscriber<Integer>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable throwable) {
    }
    @Override
    public void onNext(Integer integer) {
    Log.i("xiaohai.lin",String.valueOf(integer));
    }
    });
  • Timeout
    这个更好理解了,它一般用于对时效性有要求的事件。在超过规定时间如果没有完成则会调用onError方法。

    Observable.interval(3,TimeUnit.SECONDS).timeout(1,TimeUnit.SECONDS).subscribe(new Subscriber<Long>() {
    @Override
    public void onCompleted() {
    }
    @Override
    public void onError(Throwable throwable) {
    Log.i("xiaohai.lin", "Time out Error");
    }
    @Override
    public void onNext(Long aLong) {
    Log.i("xiaohai.lin", "aLong = " + aLong);
    }
    });
  • Distinct distinctUntilChanged

    distinct() 根据字面意思很好理解就是对原始事件源过滤处理只剩下不重复的元素
    distinctUntilChanged() 这个可以让我们忽略掉重复的值只有在事件源的值发生改变时才会通过过滤器

  • Sampling
    这个按照字面值也很好理解,就是采样的意思,比如一个事件源是按照每秒发布一次数据,但是我们实际只需要30秒采样一次样本数据,这种情况就可以使用Sampling进行处理。

  • Debounce
    过滤掉由Observable发射的速率过快的数据;如果在一个指定的时间间隔过去了仍旧没有发射一个,那么它将发射最后的那个。

  • 什么是RxJava
    RxJava其实就是一个异步的解决方案,是Android中AsyncTask 以及Thread + Handle的替代方案。
  • 为什么要采用RxJava而不用AsyncTask 以及Thread + Handle?
    因为它随着程序逻辑变得越来越复杂,依然能够保持简洁。而这些正是Thread + Handler 以及AsyncTask 很难做到的,因为当逻辑一多的时候难保证不会出现嵌套。一嵌套整个代码结构就显得很乱。
    而RxJava是基于序列的,最重要的是两个元素Observable被观察对象,Subscriber事件订阅者,Observable是整个事件的源头,产生原始信息,然后再使用各种操作符建立起链式关系,整个过程就和一条流水线一样把原始数据经过层层加工,过滤最后发送给Subscriber。
  • 如何引入到Android studio中:
    只需要在module的build.gradle 中添加如下两个依赖关系语句即可:
    compile 'io.reactivex:rxandroid:1.2.0'
    compile 'io.reactivex:rxjava:1.1.5'
    Hello RxJava

我们首先先建立一个Hello RxJava的例子,让大家有个简单的认识:

 //创建一个事件源,在事件源中调用每个订阅者的对应响应
Observable<String> stringObservable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("Hello RxJava");
}

});
//创建一个订阅者,实现每个事件节点的响应
Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onCompleted() {
Log.i("xiaohai.lin","onCompleted");
}
@Override
public void onError(Throwable throwable) {
Log.i("xiaohai.lin","onError");
}
@Override
public void onNext(String s) {
Log.i("xiaohai.lin","onNext :=" +s);
}
};
//事件源接受订阅者的订阅
stringObservable.subscribe(subscriber);

输出结果就是:Hello RxJava。但是这里并没有涉及到事件源的处理,在接下来的章节中将陆续对事件源的创建,订阅者,以及事件处理这些部分展开介绍。

RxJava 事件源介绍:

* Create

这个是最基本的方法,它没有任何的特效,但是它也是最灵活的,调用传入的Subscriber的onComplete,onError,onNext 来实现自己想要的事件源:
下面是Create操作符号的时序图:

下面是一个实际的例子:
这些都来自官网:

Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> observer) {
try {
if (!observer.isUnsubscribed()) {
for (int i = 1; i < 5; i++) {
observer.onNext(i);
}
observer.onCompleted();
}
} catch (Exception e) {
observer.onError(e);
}
}
} ).subscribe(new Subscriber<Integer>() {
@Override
public void onNext(Integer item) {
System.out.println("Next: " + item);
}

@Override
public void onError(Throwable error) {
System.err.println("Error: " + error.getMessage());
}

@Override
public void onCompleted() {
System.out.println("Sequence complete.");
}
});

我们来分析下上面的代码:
这里会调用Observable.create并传入OnSubscribe 对象,其中call方法的参数Subscriber其实就是它的订阅者,在有订阅者订阅这个被观察对象的时候,它就会被传入,Observerable首先判断下当前订阅者是否还在订阅自己,如果还继续订阅则向订阅者发送0-5的数字,然后调用onCompleted方法,宣告整个过程完毕,整个过程的核心是Subscriber的onNext/onComplete/onError方法。onNext就是发射处理好的数据给Subscriber, onComplete一般在结束事件源的时候告诉Subscriber所有的数据都已发射完毕;onError是在发生错误的时候发射一个Throwable对象给Subscriber。
一般Observable必须调用所有的Subscriber的onComplete方法,并且一般只能调用一次,onError方法也类似,一般发生出错的时候被调用,调用后一般就停止执行任何其他操作。

* Just


Just可以接受最多9个参数,它将这一系列的对象作为一个整体发送出去,这些对象可以是一个数字、一个字符串、数组、Iterate对象等。

Observable.just(1, 2, 3)
.subscribe(new Subscriber<Integer>() {
@Override
public void onNext(Integer item) {
System.out.println("Next: " + item);
}

@Override
public void onError(Throwable error) {
System.err.println("Error: " + error.getMessage());
}

@Override
public void onCompleted() {
System.out.println("Sequence complete.");
}
});
* From

From操作符用来将传入的参数内容拆封开来,依次将其内容发射出去。它和just的区别在于,但是just将对象整个发射出去。而from会将对象拆封开来一次一次发送,比如说一个含有100个数字的数组,使用from就会发射100次,而使用just则一次将整个数组发送出去。

Integer[] items = { 0, 1, 2, 3, 4, 5 };
Observable myObservable = Observable.from(items);

myObservable.subscribe(
new Action1<Integer>() {
@Override
public void call(Integer item) {
System.out.println(item);
}
},
new Action1<Throwable>() {
@Override
public void call(Throwable error) {
System.out.println("Error encountered: " + error.getMessage());
}
},
new Action0() {
@Override
public void call() {
System.out.println("Sequence complete");
}
}
);
* Defer


Defer操作符每次订阅都会得到一个刚创建的最新的Observable对象,从而确保了每次订阅者订阅到的Observable对象里的数据都是最新的。

Observable.defer(new Func0<Observable<Long>>() {
@Override
public Observable<Long> call() {
return Observable.just(System.currentTimeMillis());
}
}).subscribe(new Subscriber<Long>() {
@Override
public void onCompleted() {}
@Override
public void onError(Throwable e) {}

@Override
public void onNext(Long t) {
Log.i("xiaohai.lin", ""+t);
}
});
* Interval

这个事件源会在一定的时间间隔后产生一个从0开始的数字。

Observable.interval(1, TimeUnit.SECONDS).subscribe(new Subscriber<Long>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(Long aLong) {
Log.i("xiaohai.lin", "Current Time = " + aLong);
}
});

上面的例子会间隔1秒时间发射一个数字。这个数值会传递到订阅者的onNext方法中。

* Range


这个事件源可以产生一定范围的数值。需要传入两个表示范围的参数。

Observable.range(4, 20).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(Integer integer) {
Log.i("xiaohai.lin",String.valueOf(integer));
}
});
* Repeat


这个一般用在某个事件源中,使用repeat后会将原事件源重复一定的次数。

final Intent mainIntent = new Intent(Intent.ACTION_MAIN);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> activityInfoList = getPackageManager().queryIntentActivities(mainIntent, 0);
Observable.from(activityInfoList).repeat(2).subscribe(new Action1<ResolveInfo>() {
@Override
public void call(ResolveInfo resolveInfo) {
Log.i("xiaohai.lin",resolveInfo.activityInfo.name);
}
});
* Timmer


这个是在一定的时间间隔之后产生定时周期的事件源。

Observable.timer(10,1, TimeUnit.SECONDS).subscribe(new Subscriber<Long>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onNext(Long aLong) {
Log.i("xiaohai.lin", "aLong = " + aLong);
}

});

RxAndroid && RxJava Github地址

https://github.com/ReactiveX/RxAndroid  
https://github.com/ReactiveX/RxJava

简单应用实例:

在介绍如何使用Retrofit之前先做点准备。首先要有个开放的API供我们使用网络去获取信息,接下来的例子将使用OpenWeather来练习下,如下是openWeather的地址:
http://openweathermap.org/
和众多的API一样都需要申请一个开发者的key,申请key的过程就不在这里介绍了。

  1. 在AS中引入RxJava + Retrofix
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"

defaultConfig {
applicationId "com.idealist.doumusic"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'

compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
compile 'com.google.code.gson:gson:2.6.2'
}

开始我们先创建一个接口,关键部分先留空,后面我带大家一点一点往上面填:

public interface IDouMusic {
@GET("")
Callback<WheatherResult> getWheatherResult();

}

首先到这一步大家一定都申请了对应的APPID,也就是对应的APIkey,类似f8d8ead8299f963abcded5c40c0bcb81
找不到申请地方的我这里贴个地址:http://openweathermap.org/appid
申请到APPID后怎么使用呢?整个模板是这样的,只要在最后的申请后面添加APIKEY即可
http://api.openweathermap.org/data/2.5/forecast/city?id=524901&APPID={APIKEY}
前面的又是什么呢?大家进入这个页面http://openweathermap.org/api 这里面有很多的查询方案,作为例子我们选个简单的,当前的天气情况:
http://openweathermap.org/current 这里提供了很多的查询方式,可以按照城市名,城市id等信息查询。
你不知道城市id怎么表示?可以到这个地方下载city.list.json 查询 http://bulk.openweathermap.org/sample/
比如我们以广东惠州为例,查询当天天气,那么访问的地址应该是:http://api.openweathermap.org/data/2.5/weather?q=Huizhou&APPID=f8d8ead8299f963abcded5c40c0bcb81
注意这里的APIKey需要换成大家自己申请的。将上述的地址拷贝到浏览器上这时候就会返回下面信息:

{
"coord": {
"lon": 114.4,
"lat": 23.08
},
"weather": [
{
"id": 802,
"main": "Clouds",
"description": "scattered clouds",
"icon": "03n"
}
],
"base": "stations",
"main": {
"temp": 300.93,
"pressure": 995,
"humidity": 49,
"temp_min": 300.93,
"temp_max": 300.93
},
"wind": {
"speed": 1.03,
"gust": 7.71
},
"rain": {},
"clouds": {
"all": 48
},
"dt": 1463923088,
"sys": {
"type": 3,
"id": 187827,
"message": 0.0145,
"country": "CN",
"sunrise": 1463866709,
"sunset": 1463914799
},
"id": 1806776,
"name": "Huizhou",
"cod": 200
}

OK,到这一步我们来实现这个功能:
由于要访问到网络所以需要添加网络访问权限:

<uses-permission android:name="android.permission.INTERNET"/>

紧接着完善刚刚提到的那个接口:
为什么是@GET(“weather”)? 我们上面提到要访问的地址为:

http://api.openweathermap.org/data/2.5/weather?q=Huizhou&APPID=f8d8ead8299f963abcded5c40c0bcb81

去除掉访问参数后的地址为:

http://api.openweathermap.org/data/2.5/weather

我们再看OpenWeather的API,其中http://api.openweathermap.org/data/2.5/ 为公共部分,最后一个是变化的,我们将http://api.openweathermap.org/data/2.5/ 作为baseUri
weather作为地址的区别部分。这里需要注意的是baseuri一定要以’/‘结尾,GET(“weather”)这个不能以/开头,刚开始使用的时候由于这个原因导致了获取不到返回值,具体为什么,我将会在介绍完这个例子后给大家介绍下。

public interface IDouWheather {
@GET("weather")
Call<WheatherResult> getWheatherResult(@Query("q") String cityName, @Query("APPID") String ApiId);
}

关于访问接口还有如下的写法,这里介绍的是Path的用法。

public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(@Path("owner") String owner,@Path("repo") String repo);
}
//baseurl 定义
String baseUrl = "http://api.openweathermap.org/data/2.5/";

//创建Retrofit:
Retrofit retrofit = new Retrofit.Builder()
/**
使用Retrofit会将Json数据直接解析转换成java 对象,因此需要用到json 解析库作为转换器
这里使用GSON解析数据,但是retrofit2还支持如下列表的解析器:
    Gson: com.squareup.retrofit2:converter-gson 
    Jackson: com.squareup.retrofit2:converter-jackson 
    Moshi: com.squareup.retrofit2:converter-moshi 
    Protobuf: com.squareup.retrofit2:converter-protobuf 
    Wire: com.squareup.retrofit2:converter-wire 
    Simple XML:com.squareup.retrofit2:converter-simplexml
    
        */
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(baseUrl)
/*需要注意下BASE_URL这个参数,如果@GET方法里的URl是完整的,则不拼接参数,否则拼接URL*/
.build();
//创建访问服务:
IDouWheather weatherService = retrofit.create(IDouWheather.class);
//将参数传递进去:
Call<WheatherResult> call = weatherService.getWheatherResult("Huizhou", "f8d8ead8299f963abcded5c40c0bcb89");
//异步访问:
call.enqueue(new Callback<WheatherResult>() {
//回调函数发生在主线程,可以进行UI相关的操作
@Override
public void onResponse(Call<WheatherResult> call, Response<WheatherResult> response) {
if(response.isSuccess()) {
//获取返回结果
WheatherResult result = response.body();
Log.i("xiaohai.lin", "getName = " + result.getName());
Log.i("xiaohai.lin", "getTemp = " + result.getMain().getTemp());
Log.i("xiaohai.lin", "getLat = " + result.getCoord().getLat());
Log.i("xiaohai.lin", "getLon = " + result.getCoord().getLon());
Log.i("xiaohai.lin", "getDescription = " + result.getWeather().get(0).getDescription());
}
}
@Override
public void onFailure(Call<WheatherResult> call, Throwable t) {

}
});

下面是返回数据对应的Bean,这个我是使用GSONFormat插件自动生成的:注意这里可以不用实现全部的节点,但是一定要注意,参数名一定要和返回的json数据的key一致:

public class WheatherResult {
/**
* lon : 114.4
* lat : 23.08
*/

private CoordBean coord;
/**
* coord : {"lon":114.4,"lat":23.08}
* weather : [{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}]
* base : stations
* main : {"temp":302.04,"pressure":994,"humidity":56,"temp_min":302.04,"temp_max":302.04}
* wind : {"speed":2.49,"deg":144}
* rain : {}
* clouds : {"all":68}
* dt : 1464103691
* sys : {"type":3,"id":187827,"message":0.0114,"country":"CN","sunrise":1464039473,"sunset":1464087657}
* id : 1806776
* name : Huizhou
* cod : 200
*/

private String base;
/**
* temp : 302.04
* pressure : 994
* humidity : 56
* temp_min : 302.04
* temp_max : 302.04
*/

private MainBean main;
/**
* speed : 2.49
* deg : 144
*/

private WindBean wind;
private RainBean rain;
/**
* all : 68
*/

private CloudsBean clouds;
private int dt;
/**
* type : 3
* id : 187827
* message : 0.0114
* country : CN
* sunrise : 1464039473
* sunset : 1464087657
*/

private SysBean sys;
private int id;
private String name;
private int cod;
/**
* id : 803
* main : Clouds
* description : broken clouds
* icon : 04n
*/

private List<WeatherBean> weather;

public CoordBean getCoord() {
return coord;
}

public void setCoord(CoordBean coord) {
this.coord = coord;
}

public String getBase() {
return base;
}

public void setBase(String base) {
this.base = base;
}

public MainBean getMain() {
return main;
}

public void setMain(MainBean main) {
this.main = main;
}

public WindBean getWind() {
return wind;
}

public void setWind(WindBean wind) {
this.wind = wind;
}

public RainBean getRain() {
return rain;
}

public void setRain(RainBean rain) {
this.rain = rain;
}

public CloudsBean getClouds() {
return clouds;
}

public void setClouds(CloudsBean clouds) {
this.clouds = clouds;
}

public int getDt() {
return dt;
}

public void setDt(int dt) {
this.dt = dt;
}

public SysBean getSys() {
return sys;
}

public void setSys(SysBean sys) {
this.sys = sys;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getCod() {
return cod;
}

public void setCod(int cod) {
this.cod = cod;
}

public List<WeatherBean> getWeather() {
return weather;
}

public void setWeather(List<WeatherBean> weather) {
this.weather = weather;
}

public static class CoordBean {
private double lon;
private double lat;

public double getLon() {
return lon;
}

public void setLon(double lon) {
this.lon = lon;
}

public double getLat() {
return lat;
}

public void setLat(double lat) {
this.lat = lat;
}
}

public static class MainBean {
private double temp;
private int pressure;
private int humidity;
private double temp_min;
private double temp_max;

public double getTemp() {
return temp;
}

public void setTemp(double temp) {
this.temp = temp;
}

public int getPressure() {
return pressure;
}

public void setPressure(int pressure) {
this.pressure = pressure;
}

public int getHumidity() {
return humidity;
}

public void setHumidity(int humidity) {
this.humidity = humidity;
}

public double getTemp_min() {
return temp_min;
}

public void setTemp_min(double temp_min) {
this.temp_min = temp_min;
}

public double getTemp_max() {
return temp_max;
}

public void setTemp_max(double temp_max) {
this.temp_max = temp_max;
}
}

public static class WindBean {
private double speed;
private int deg;

public double getSpeed() {
return speed;
}

public void setSpeed(double speed) {
this.speed = speed;
}

public int getDeg() {
return deg;
}

public void setDeg(int deg) {
this.deg = deg;
}
}

public static class RainBean {
}

public static class CloudsBean {
private int all;

public int getAll() {
return all;
}

public void setAll(int all) {
this.all = all;
}
}

public static class SysBean {
private int type;
private int id;
private double message;
private String country;
private int sunrise;
private int sunset;

public int getType() {
return type;
}

public void setType(int type) {
this.type = type;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public double getMessage() {
return message;
}

public void setMessage(double message) {
this.message = message;
}

public String getCountry() {
return country;
}

public void setCountry(String country) {
this.country = country;
}

public int getSunrise() {
return sunrise;
}

public void setSunrise(int sunrise) {
this.sunrise = sunrise;
}

public int getSunset() {
return sunset;
}

public void setSunset(int sunset) {
this.sunset = sunset;
}
}

public static class WeatherBean {
private int id;
private String main;
private String description;
private String icon;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getMain() {
return main;
}

public void setMain(String main) {
this.main = main;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getIcon() {
return icon;
}

public void setIcon(String icon) {
this.icon = icon;
}
}
}

点击运行就可以直接获取返回的结果了。

基本用法:

Retrofix && RxJava:

使用RXJava会使得整个流程会显得更加清晰,在原先项目中加入RxJava步骤很简单只需要在原有的基础上作如下改动即可:

在build.gradle中加入:

compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'

这时候接口就不能是Call了,而应该是Observable:

public interface IDouMusic {
@GET("weather")
Observable<WheatherResult> getWheatherResult(@Query("q") String cityName, @Query("APPID") String ApiId);
}
String baseUrl = "http://api.openweathermap.org/data/2.5/";
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
//添加RxJavaCallAdapterFactory
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(baseUrl)
.build();

IDouMusic weatherService = retrofit.create(IDouMusic.class);
Observable<WheatherResult> observable = weatherService.getWheatherResult("Huizhou", "f8d8ead8299f963abcded5c40c0bcb89");
//在IO线程执行网络查询
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
//切换到主线程在主线程更新UI
.subscribe(new Subscriber<WheatherResult>() {
@Override
public void onCompleted() {}
@Override
public void onError(Throwable e) {}
@Override
public void onNext(WheatherResult wheatherResult) {
Log.i("xiaohai.lin", "getName = " + wheatherResult.getName());
Log.i("xiaohai.lin", "getTemp = " + wheatherResult.getMain().getTemp());
Log.i("xiaohai.lin", "getLat = " + wheatherResult.getCoord().getLat());
Log.i("xiaohai.lin", "getLon = " + wheatherResult.getCoord().getLon());
Log.i("xiaohai.lin", "getDescription = " + wheatherResult.getWeather().get(0).getDescription());
}
});

Retrofix && Realm:

Realm 可以与 Retrofit 1.x 和 2.x 无缝配合工作。但注意 Retrofit 不会自动将对象存入 Realm。
你需要通过调用 Realm.copyToRealm() 或 Realm.copyToRealmOrUpdate() 来将它们存入 Realm。

GitHubService service = restAdapter.create(GitHubService.class);
List<Repo> repos = service.listRepos("octocat");

// Copy elements from Retrofit to Realm to persist them.
realm.beginTransaction();
List<Repo> realmRepos = realm.copyToRealmOrUpdate(repos);
realm.commitTransaction();

Retrofix2 新特性:

http://api.openweathermap.org/data/2.5/weather?q=Huizhou&APPID=f8d8ead8299f963abcded5c40c0bcb89
https://gank.io/post/56e80c2c677659311bed9841
https://yq.aliyun.com/articles/26705

http://www.07net01.com/program/2016/02/1307334.html
http://www.jianshu.com/p/c1a3a881a144
https://twiceyuan.com/2015/12/26/retrofit2-0/?utm_source=tuicool&utm_medium=referral
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0915/3460.html
http://blog.csdn.net/liuhongwei123888/article/details/50375283
http://www.cnblogs.com/angeldevil/p/3757335.html
http://www.bkjia.com/Androidjc/995850.html
http://blog.csdn.net/lizelinll/article/details/42361327
http://www.tuicool.com/articles/6zIvQbb
http://www.tuicool.com/articles/fQju2uQ
http://blog.csdn.net/tiankong1206/article/details/46486401
http://blog.csdn.net/caroline_wendy/article/details/50557470

http://www.cxbiao.com/2016/05/14/Retrofit2%E4%B8%8ERxJava%E7%94%A8%E6%B3%95%E8%A7%A3%E6%9E%90/
http://www.jcodecraeer.com/plus/view.php?aid=3460

多线程情况下的Realm

我们可以实时地在不同线程中读取和写入Realm对象,不用担心其它线程会对同一对象进行操作。在使用事务改变对象时,在下一次事件循环中,另一线程中指向同一对象的数据会被即时更新。唯一局限是不能随意跨线程传递 Realm 对象。如果您在另一线程使用同一对象,请在哪个线程使用查询重新获得该对象。请谨记所有的 Realm 对象都会在不同线程中保持更新——Realm 会在数据改变时通知您。

使用JSON数据

对于JSON,我们可以直接将它添加到Realm中,这些 JSON 对象可以是一个 String、一个 JSONObject 或者是一个 InputStream。Realm 会忽略 JSON 中存在但未定义在 Realm 模型类里的字段。
单独对象可以通过 Realm.createObjectFromJson() 添加。对象列表可以通过 Realm.createAllFromJson() 添加。

// A RealmObject that represents a city
public class City extends RealmObject {
private String city;
private int id;
// getters and setters left out ...
}

// Insert from a string
realm.beginTransaction();
realm.createObjectFromJson(City.class, "{ city: \"Copenhagen\", id: 1 }");
realm.commitTransaction();

// Insert multiple items using a InputStream
InputStream is = new FileInputStream(new File("path_to_file"));
realm.beginTransaction();
try {
realm.createAllFromJson(City.class, is);
realm.commitTransaction();
} catch (IOException e) {
realm.cancelTransaction();
}

通知

当后台线程向 Realm 添加数据,UI 线程或者其它线程可以添加一个监听器来获取数据改变的通知。监听器在 Realm 数据改变的时候会被触发。

public class MyActivity extends Activity {
private Realm realm;
private RealmChangeListener realmListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
reamlListener = new RealmChangeListener<Realm>() {
@Override
public void onChange(Realm realm) {
// ... do something with the updates (UI, etc.) ...
}};
realm.addChangeListener(realmListener);
}

@Override
protected void onDestroy() {
super.onDestroy();
// Remove the listener.
realm.removeChangeListener(realmListener);
// Close the realm instance.
realm.close();
}
}

  • 移除所有监听器。
    realm.removeAllChangeListeners();
    除了在 Realm 实例上添加监听器以外,您还可以在 RealmObject 和 RealmResults 实例上添加监听器。
    你可以通过下面的方式来监视对象和查询结果的改变。另外,当监听回调函数被调用时,相应的数据已经被更新,我们不需要去做刷新操作。
    public class MyActivity extends Activity {
    private Realm realm;
    private RealmChangeListener puppiesListener;
    private RealmChangeListener dogListener;

    private RealmResults<Dog> puppies;
    private Dog dog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    realm = Realm.getDefaultInstance();
    puppiesListener = new RealmChangeListener<RealmResults<Dog>>() {
    @Override
    public void onChange(RealmResults<Dog> puppies) {
    // ... do something with the updated puppies instance
    }};

    // Find all the puppies
    puppies = realm.where(Dog.class).lessThanOrEqualTo("age", 2).findAll();
    puppies.addChangeListener(puppiesListener);

    dogListener = new RealmChangeListener<Dog>() {
    @Override
    public void onChange(Dog dog) {
    // ... do something with the updated Dog instance
    }};

    dog = realm.where(Dog.class).equals("name", "Fido").findFirst();
    dog.addChangeListener(dogListener);
    }

    @Override
    protected void onDestroy() {
    super.onDestroy();
    // Remove the listeners
    puppies.removeChangeListener(puppiesListener);
    dog.removeChangeListener(dogListener);
    // Close the realm instance.
    realm.close();
    }
    }
    最后,这些监听器同样会在监听对象的引用对象改变时被触发,请见示例:
    Person person = realm.where(Person.class).findFirst();
    person.getDogs(); // => 2 - Assume there are 2 dogs in the list
    person.addChangeListener(new RealmChangeListener<Person>() {
    @Override
    public void onChange(Person person) {
    // React to the change in the Person instance.
    // This will also get called when any referenced dogs are updated.
    }
    });
    Dog dog = person.getDogs().get(0);
    realm.beginTransaction();
    dog.setAge(5);
    realm.commitTransaction();
    // Person change listener is called on the next iteration of the run loop because
    // a referenced dog object changed.

总结如下:

  • 可以对Realm添加监听
  • 可以对RealmObject,RealmResults添加监听,在这种情况下对应的数据发生变化或者引用发生变化都会出发监听响应事件。

迁移(Migrations)

下面例子使得相应的 migration 代码在有迁移需要的时候被自动执行。

RealmConfiguration config = new RealmConfiguration.Builder(context)
.schemaVersion(2) // Must be bumped when the schema changes
.migration(new MyMigration()) // Migration to run instead of throwing an exception
.build()
// Example migration adding a new class
RealmMigration migration = new RealmMigration() {
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {

// DynamicRealm exposes an editable schema
RealmSchema schema = realm.getSchema();

// Migrate to version 1: Add a new class
// Example:
// public Person extends RealmObject {
// private String name;
// private int age;
// // getters and setters left out for brevity
// }
if (oldVersion == 0) {
schema.create("Person")
.addField("name", String.class)
.addField("age", int.class);
oldVersion++;
}

// Migrate to version 2: Add a primary key + object references
// Example:
// public Person extends RealmObject {
// private String name;
// @PrimaryKey
// private int age;
// private Dog favoriteDog;
// private RealmList<Dog> dogs;
// // getters and setters left out for brevity
// }
if (oldVersion == 1) {
schema.get("Person")
.addField("id", long.class, FieldAttribute.PRIMARY_KEY)
.addRealmObjectField("favoriteDog", schema.get("Dog"))
.addRealmListField("dogs", schema.get("Dog"));
oldVersion++;
}
}
}

如果没有旧 Realm 数据文件存在,那么迁移并不需要,在这种情况下,Realm 会创建一个新的以 .realm 为后缀,基于新的对象模型的数据文件。在开发和调试过程中,假如您需要频繁改变数据模型,并且不介意损失旧数据,您可以直接删除 .realm 文件(这里包含所有的数据!)而不用关心迁移的问题。

RealmConfiguration config = new RealmConfiguration.Builder(context)
.deleteRealmIfMigrationNeeded()
.build()

加密

Realm 文件可以通过传递一个512位(64字节)的密钥参数给 Realm.getInstance().encryptionKey() 来加密存储在磁盘上。

byte[] key = new byte[64];
new SecureRandom().nextBytes(key);
RealmConfiguration config = new RealmConfiguration.Builder(context)
.encryptionKey(key)
.build();

Realm realm = Realm.getInstance(config);

与 Android 相关

  • 适配器(Adapter)

RealmBaseAdapter 可以与 ListView 配合使用。
RealmRecyclerViewAdapter 可以与 RecyclerView 配合使用。

要使用这些适配器需要添加如下的依赖:

dependencies {
compile 'io.realm:android-adapters:1.2.1'
}
  • Intents

因为Realm 中不可以直接通过 intent 传递 RealmObject,所以一般只传递 RealmObject 的标识符。

// Assuming we had a person class with a @PrimaryKey on the 'id' field ...
Intent intent = new Intent(getActivity(), ReceivingService.class);
intent.putExtra("person_id", person.getId());
getActivity().startService(intent);

在接受方(Activty、Service、IntentService、BroadcastReceiver 及其它)从 bundle 中解析出这个主键然后打开 Realm 查询得到这个 RealmObject。

// in onCreate(), onHandleIntent(), etc.
String personId = intent.getStringExtra("person_id");
Realm realm = Realm.getDefaultInstance();
Person person = realm.where(Person.class).equalTo("id", personId).findFirst();
// do something with the person ...
realm.close();

Android Framework 多线程 API 相关

当使用下列 API 时需要格外注意:

  • AsyncTask
  • IntentService
    AsyncTask 的 doInBackground() 方法会运行在一个后台线程。IntentService 的 onHandleIntent(Intent intent) 方法会运行在一个后台工作线程。
    如果需要在这些方法中使用 Realm,在对 Realm 的调用结束后关闭 Realm 实例。

AsyncTask
在 doInBackground 方法中打开并关闭 Realm,如下所示:

private class DownloadOrders extends AsyncTask<Void, Void, Long> {
protected Long doInBackground(Void... voids) {
// Now in a background thread.

// Open the Realm
Realm realm = Realm.getDefaultInstance();
// Work with Realm
realm.createAllFromJson(Order.class, api.getNewOrders());
Order firstOrder = realm.where(Order.class).findFirst();
long orderId = firstOrder.getId(); // Id of order
realm.close();
return orderId;
}

protected void onPostExecute(Long orderId) {
// Back on the Android mainThread
// do something with orderId such as query Realm
// for the order and perform some operation with it.
}
}

IntentService
在 onHandleIntent() 方法中打开并关闭 Realm,如下所示:

public class OrdersIntentService extends IntentService {
public OrdersIntentService(String name) {
super("OrdersIntentService");
}

@Override
protected void onHandleIntent(Intent intent) {
// Now in a background thread.

// Open the Realm
Realm realm = Realm.getDefaultInstance();
// Work with Realm
realm.createAllFromJson(Order.class, api.getNewOrders());
Order firstOrder = realm.where(Order.class).findFirst();
long orderId = firstOrder.getId(); // Id of order
realm.close();
}
}

对其它库的支持

  • GSON
    GSON 是 Google 开发的 JSON 处理库。GSON 与 Realm 可以无缝配合使用。

    // Using the User class
    public class User extends RealmObject {
    private String name;
    private String email;
    // getters and setters left out ...
    }

    Gson gson = new GsonBuilder().create();
    String json = "{ name : 'John', email : 'john@corporation.com' }";
    User user = gson.fromJson(json, User.class);
  • Retrofit

Realm 可以与 Retrofit 1.x 和 2.x 无缝配合工作。但注意 Retrofit 不会自动将对象存入 Realm。
你需要通过调用 Realm.copyToRealm() 或 Realm.copyToRealmOrUpdate() 来将它们存入 Realm。

GitHubService service = restAdapter.create(GitHubService.class);
List<Repo> repos = service.listRepos("octocat");

// Copy elements from Retrofit to Realm to persist them.
realm.beginTransaction();
List<Repo> realmRepos = realm.copyToRealmOrUpdate(repos);
realm.commitTransaction();
  • RxJava

Realm 包含了对 RxJava 的原生支持。如下类可以被暴露为一个 Observable:Realm, RealmResults, RealmObject, DynamicRealm and DynamicRealmObject。

// Combining Realm, Retrofit and RxJava (Using Retrolambda syntax for brevity)
// Load all persons and merge them with their latest stats from GitHub (if they have any)
Realm realm = Realm.getDefaultInstance();
GitHubService api = retrofit.create(GitHubService.class);
realm.where(Person.class).isNotNull("username").findAllAsync().asObservable()
.filter(persons.isLoaded)
.flatMap(persons -> Observable.from(persons))
.flatMap(person -> api.user(person.getGithubUserName())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(user -> showUser(user));

请注意异步查询不会阻塞当前线程,如上代码会立即返回一个 RealmResults 实例。如果您想确定该 RealmResults 已经加载完成请使用 filter operator 和 RealmResults.isLoaded() 方法。通过判断 RealmResults 是否已经加载可以得知查询是否已经完成。

最佳实践

  • 防止出现 ANR

一般来说 Realm 的读写是足够快的,甚至在 UI 线程中读写也不是问题。但是,写事务是互相阻塞的,所以为了避免 ANR 的出现,我们建议你在后台线程中执行写操作。

  • 控制 Realm 实例的生命周期

RealmObjects 和 RealmResults 在访问其引用数据时都是懒加载的。因为这个原因,请不要关闭你的 Realm 实例如果你仍然需要访问其中的 Realm 对象或者查询结果。为了避免不必要的 Realm 数据连接的打开和关闭,
Realm 内部有一个基于引用计数的缓存。这表示在同一线程内调用 Realm.getDefaultInstance() 多次是基本没有开销的,并且底层资源会在所有实例都关闭的时候才被释放。
以 UI 线程举例,最简单安全的途径是,在你所有的 Activity 和 Fragment 初始化时取得 Realm 实例,并在它们销毁时关闭 Realm 实例。

// Setup Realm in your Application
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(realmConfiguration);
}
}

// onCreate()/onDestroy() overlap when switching between activities so onCreate()
// on Activity 2 will be called before onDestroy() on Activity 1.
public class MyActivity extends Activity {
private Realm realm;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
}

@Override
protected void onDestroy() {
super.onDestroy();
realm.close();
}
}

// Use onStart()/onStop() for Fragments as onDestroy() might not be called.
public class MyFragment extends Fragment {
private Realm realm;

@Override
public void onStart() {
super.onStart();
realm = Realm.getDefaultInstance();
}

@Override
public void onStop() {
super.onStop();
realm.close();
}
}
  • 重用 RealmResults 和 RealmObjects
    在 UI 线程和其它拥有 Looper 的线程中,RealmObject 和 RealmResults 都会在 Realm 数据改变时自动刷新。这意味着你不需要在 RealmChangeListener 中重新获取这些对象。它们已经被更新并且准备好被重绘在屏幕上了。
    public class MyActivity extends Activity {

    private Realm realm;
    private RealmResults<Person> allPersons;
    private RealmChangeListener realmListener = new RealmChangeListener<Realm>() {
    @Override
    public void onChange(Realm realm) {
    // Just redraw the views. `allPersons` already contain the
    // latest data.
    invalidateView();
    }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    realm = Realm.getDefaultInstance();
    realm.addRealmChangeListener(listener);
    allPerson = realm.where(Person.class).findAll(); // Create the "live" query result
    setupViews(); // Initial setup of views
    invalidateView(); // Redraw views with data
    }

    // ...
    }

对于Realm的学习主要分成篇博文,这篇如果只是想要单纯简单应用的话只要学会本篇博文的内容即可上手在项目中使用,如果对于Realm想要进一步深入了解它的高级用法可以查看Android 进阶之第三方库的介绍 Realm [二] 高级用法:
注意本篇博文大多数的内容都来自Realm官网,只不过是通过自己的认知规律重新组织下,来加深这些内容的理解。读大学那会儿老师说过看的东西永远是别人的,所以需要自己动手实践,哪怕梳理下内容也会转化成自己的。觉得非常有道理。

官网地址:https://realm.io

为什么选用Realm?

官网给出了如下的原因

  • 易用
    Ream 不是在SQLite基础上的ORM,它有自己的数据查询引擎。并且十分容易使用。
  • 快速
    由于它是完全重新开始开发的数据库实现,所以它比任何的ORM速度都快很多,甚至比SLite速度都要快。
  • 跨平台
    Realm 支持 iOS & OS X (Objective‑C & Swift) & Android. 我们可以在这些平台上共享Realm数据库文件,并且上层逻辑可以不用任何改动的情况下实现移植。
  • 高级
    Ream支持加密,格式化查询,易于移植,支持JSON,流式api,数据变更通知等高级特性
  • 可信赖的
    这个就不用解释了吧,每个库都这么说。
  • 社区支持

有什么限制?

目前Realm还不支持 Android 以外的 Java 环境;
Android Studio >= 1.5.1 ;
较新的 Android SDK 版本;
JDK 版本 >=7;
支持 Android API 9 以上的所有版本(Android 2.3 Gingerbread 及以上)。


1. 添加Realm到工程

在module的build.gradle文件中添加realm依赖:

compile 'io.realm:realm-android:0.84.1'

2. 创建一个Realm数据库

  • 创建一个持久化Realm数据库
    和SQLite数据库类似,每个Realm对应一个数据库文件。但是它的数据库后缀不是.db而是.realm
    可以调用Realm.getInstance方法来创建一个Realm数据库:
    Realm musicRealmDB = Realm.getInstance(context);
    注意这里只是传入一个context,并没有传入数据库文件的文件名,这时候会创建一个叫做 default.realm的Realm文件,一般来说,这个文件位于/data/data//files/,可以通过realm.getPath()来获得该Realm的绝对路径。
    需要注意的是Realm的实例是线程单例化的,也就是说,在同一个线程内多次调用静态方法获得针对同路径的Realm,会返回同一个Realm实例。
    但是如果在这个基础上还想创建一个新的数据库,或者你不想用default.realm这个名字,那么就需要使用如下的方式创建了:
Realm musicRealmDB =
Realm.getInstance(
new RealmConfiguration.Builder(context)
.name("musicRealmDB.realm")
.build()
);

除了上述的方法还可以使用:

Realm realm = Realm.getDefaultInstance();

来获得默认的Realm实例

默认RealmConfiguration:
RealmConfiguration可以保存为默认配置。通过在自定义的Application设置默认的Realm配置,可以使您在代码中的其他地方更加方便地创建针对该默认配置的Realm。

public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// The realm file will be located in Context.getFilesDir() with name "default.realm"
RealmConfiguration config = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(config);
}
}

public class MyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Realm realm = Realm.getDefaultInstance();
// ... Do something ...
realm.close();
}
}
  • 创建非持久化的Realm

    RealmConfiguration myConfig = new RealmConfiguration.Builder(context)
    .name("myrealm.realm")
    .inMemory()
    .build();

    这样就可以创建一个存在于“内存中的” Realm。“内存中的”Realm 在内存紧张的情况下仍有可能使用到磁盘存储,但是这些磁盘空间都会在Realm实例完全关闭的时候被释放。
    请注意使用同样的名称同时创建“内存中的”Realm 和常规的(持久化)Realm 是不允许的。
    当某个“内存中的”Realm 的所有实例引用都被释放,该Realm 下的数据也同时会被清除。建议在您的应用生命周期中保持对“内存中的” Realm 实例的引用以避免非期望的数据丢失。

  • 关闭Realm实例
    Realm 实现了 Closeable 接口以便与释放 native 内存和文件描述符,请务必在使用完毕后关闭 Realm 实例。
    Realm 实例是基于引用计数的, 也就是说假设您在同一个线程中调用了 getInstance() 两次,您需要同样调用 close() 两次以关闭该实例。
    需要实现 Runnable,简单地在函数开始的时候调用 getInstance(),在函数结束的时候调用 close() 即可!
    对于UI线程,您可以选择在 onDestroy() 方法内调用 realm.close()。
    对于 AsyncTask,可以参照如下方式,在退出的时候关闭

    protected Void doInBackground(Void... params) {
    Realm realm = null;
    try {
    realm = Realm.getDefaultInstance();
    // ... Use the Realm instance ...
    } finally {
    if (realm != null) {
    realm.close();
    }
    }
    return null;
    }

    如果需要创建一个包含 Looper 的线程,可以参考如下的用法:

    public class MyThread extends Thread {

    private Realm realm;
    public void run() {
    Looper.prepare();
    try {
    realm = Realm.getDefaultInstance();
    //... Setup the handlers using the Realm instance ...
    Lopper.loop();
    } finally {
    if (realm != null) {
    realm.close();
    }
    }
    }
    }

    如果minSdkVersion >= 19,可以使用try-with-resources,这种情况下就不需要手动来关闭了

    try (Realm realm = Realm.getDefaultInstance()) {
    // No need to close the Realm instance manually
    }

    3. 数据模型定义

    Define you model class by extending the RealmObject

要想将某个JavaBean对象存储到Ream数据库中必须继承RealmObject类,但是需要注意的是Realm 数据模型不可以继承自除了 RealmObject 以外的其它对象

public class MusicItemBean extends RealmObject {

private String title;
private int path;
//................
public MusicItemBean() { }

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public int getPath() {
return path;
}

public void setPath(int path) {
this.path = path;
}

}
  • Realm 数据模型不仅仅支持 private 成员变量,您还可以使用 public、protected 以及自定义的成员方法。目前不支持 final、transient 和 volatile 修饰的成员变量

  • Realm 支持boolean、byte、short、int、long、float、double、String、Date和byte [] 等类型字段。还可以使用 RealmObject 的子类和 RealmList<? extends RealmObject> 来表示模型关系。
    Realm 对象中还可以声明包装类型(boxed type)属性,包括:Boolean、Byte、Short、Integer、Long、Float和Double。

  • Realm常用的注释:

    • @PrimaryKey 属性:
      如果想让RealmObject的一个成员变量作为主键,可以使用@PrimaryKey注解。需要注意的是该字段类型必须为字符串(String)或整数(short、int 或 long)以及它们的包装类型(Short、Int 或 Long)。

    • @Required 属性:
      @Required 表示该字段不能为null,只有 Boolean,Byte,Short,Integer,Long,Float,Double,String,byte[] 以及 Date 可以被 @Required 修饰。
      在其它类型属性上使用 @Required 修饰会导致编译失败。基本数据类型(primitive types)不需要使用注解 @Required,因为他们本身就不可为空。RealmObject 属性永远可以为空。
      主键的存在意味着可以使用 createOrUpdate() 方法,它会用此主键尝试寻找一个已存在的对象,如果对象存在,就更新该对象;反之,它会创建一个新的对象。
      使用主键会对性能产生影响。创建和更新对象将会慢一点,而查询则会变快。很难量化这些性能的差异,因为性能的改变跟您数据库的大小息息相关。

    • @Ignore 属性:
      注解 @Ignore 意味着一个字段不应该被保存到 Realm。

    • @Index 属性:
      注解 @Index 会为字段增加搜索索引。这会导致插入速度变慢,同时数据文件体积有所增加,但能加速查询。因此建议仅在需要加速查询时才添加索引。目前仅支持索引的属性类型包括:String、byte、short、int、long、boolean和Date。

4. 存储数据项目:

Realm强制所有的写操作(添加、修改和删除对象)都在一个事务中执行从而确保数据的一致性。
要开始一个事务可以使用beginTransaction方法。反之要结束当前事务,可以使用commitTransaction方法。要取消事务可以调用cancelTransaction()方法:如下所示:
写入事务可以提交或取消。在提交期间,所有更改都将被写入磁盘,并且,只有当所有更改可以被持久化时,提交才会成功。通过取消一个写入事务,所有更改将被丢弃。

musicRealmDB.beginTransaction();
MusicItemBean music = musicRealmDB.createObject(MusicItemBean.class);
music.setTitle("何以爱情");
music.setPath("/storage/sdcard0/test01.mp3");
musicRealmDB.commitTransaction();

注意这里并没有调用MusicItemBean构造方法创建新的对象,如果你真的需要使用构造方法也可以,下面是使用构造方法的示例,它调用copyToRealm写入,Realm对象支持多个构造函数,只要其中之一是公共无参数构造函数即可。

MusicItemBean music = new MusicItemBean();
music.setTitle("何以爱情");
music.setPath("/storage/sdcard0/test01.mp3");

musicRealmDB.beginTransaction();
MusicItemBean musicbean = musicRealmDB.copyToRealm(music);
musicRealmDB.commitTransaction();
MyObject obj = new MyObject();
obj.setId(42);
obj.setName("Fish");
realm.beginTransaction();
// This will create a new one in Realm
// realm.copyToRealm(obj);
// This will update a existing one with the same id or create a new one instead
realm.copyToRealmOrUpdate(obj);
realm.commitTransaction();

需要注意的是,写入事务之间会互相阻塞,如果一个写入事务正在进行,那么其他的线程的写入事务就会阻塞它们所在的线程。同时在 UI线程和后台线程使用写入事务有可能导致ANR问题。可以使用异步事务(async transactions)以避免阻塞UI线程。
当正在进行一个写入事务时读取操作并不会被阻塞,这意味着,除非需要从多个线程进行并发写入操作,否则,可以尽量使用更大的写入事务来做更多的事情而不是使用多个更小的写入事务。
当写入事务被提交到 Realm 时,该Realm的所有其他实例都将被通知,读入隐式事务将自动刷新您每个Realm对象。

5 Transaction 事务

除了上述的beginTransectiion,commitTransection外还有事务执行块以及异步事务这两种:

  • 事务执行块(Transaction blocks)

异步执行块会自动处理写入事物的开始和提交,并在错误发生时取消写入事物。

realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User user = realm.createObject(User.class);
user.setName("John");
user.setEmail("john@corporation.com");
}
});
  • 异步事务(Asynchronous Transactions)
    事务会相互阻塞其所在的线程,在后台线程中开启事务进行写入操作可以有效避免 UI 线程被阻塞。通过使用异步事务,Realm 会在后台线程中进行写入操作,并在事务完成时将结果传回调用线程。
    realm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm bgRealm) {
    User user = bgRealm.createObject(User.class);
    user.setName("John");
    user.setEmail("john@corporation.com");
    }
    }, new Realm.Transaction.OnSuccess() {
    @Override
    public void onSuccess() {
    // Transaction was a success.
    }
    }, new Realm.Transaction.OnError() {
    @Override
    public void onError(Throwable error) {
    // Transaction failed and was automatically canceled.
    }
    });
    OnSuccess 和 OnError 并不是必须重载的,重载了的回调函数会在事务成功或者失败时在被调用发生的线程执行。回调函数是通过 Looper 被执行的,所以在非 Looper 线程中只有空(null)回调函数被允许使用。
    RealmAsyncTask transaction = realm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm bgRealm) {
    User user = bgRealm.createObject(User.class);
    user.setName("John");
    user.setEmail("john@corporation.com");
    }
    }, null);

异步事务调用会返回一个 RealmAsyncTask 对象。当你退出 Activity 或者 Fragment 时可以使用该对象取消异步事务。如果你在回调函数中更新UI,那么忘记取消异步事务可能会造成你的应用崩溃。

public void onStop () {
if (transaction != null && !transaction.isCancelled()) {
transaction.cancel();
}
}

6. 查询数据条目:

要创建一个查询,使用相关Realm对象的where方法并传入相关的类。创建完查询之后,将会返回一个RealmResults。可以通过RealmResults对象的findAll方法获取所有的结果。
当查询没有任何匹配时,返回的 RealmResults 对象将不会为 null,取而代之的是它的 size() 方法将返回 0。

RealmResults<MusicItemBean> results1 =
musicRealmDB.where(MusicItemBean.class).findAll();
for(MusicItemBean music:results1) {
Log.d("xiaohai.lin", music.setTitle());
}
  • Realm 支持如下的查询条件:
between()、greaterThan()、lessThan()、greaterThanOrEqualTo() 和 lessThanOrEqualTo()
equalTo() 和 notEqualTo()
contains()、beginsWith() 和 endsWith()
RealmResults<MusicItemBean> qury2 =
musicRealmDB.where(MusicItemBean.class)
.beginsWith("path", “/storage/sdcard0/”)
.findAll();
  • 查询条件的组合:

字符串查询条件可以通过使用 Case.INSENSITIVE 修饰符来忽略字母 A-Z 和 a-z 的大小写。
并且可以使用 beginGroup()(相当于左括号)和 endGroup()(相当于右括号)来将查询条件组合起来:
此外,也可以用 not() 否定一个条件。该 not() 运算符可以与 beginGroup()/endGroup() 一起使用来否定子条件。

RealmResults<User> r = realm.where(User.class)
.greaterThan("age", 10) //implicit AND
.beginGroup()
.equalTo("name", "Peter")
.or()
.contains("name", "Jo")
.endGroup()
.findAll();


  • 对查询结果进行排序:

如果想要查询的结果按照某项进行排序,那么可以使用findAllSorted,并传入一个boolean指定归类顺序,true表示升序排序,false表示降序排序。

RealmResults<MusicItemBean> qury2 =
musicRealmDB.where(Country.class)
.findAllSorted("title", true);

还可以通过sort来进行排序:

RealmResults<User> result = realm.where(User.class).findAll();
result = result.sort("age"); // Sort ascending
result = result.sort("age", Sort.DESCENDING);
  • 为某个查询添加监听事件:

下面是来自官网的,它将会监控age小于2的查询结果,如果我们往这里面添加了数据那么这个接口将会被回调:

final RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.addChangeListener(new RealmChangeListener<RealmResults<Dog>>() {
@Override
public void onChange(RealmResults<Dog> results) {
// Queries are updated in real time
puppies.size(); // => 1
}
});
  • 聚合
    RealmResult 自带一些聚合方法:
    RealmResults<User> results = realm.where(User.class).findAll();
    long sum = results.sum("age").longValue();
    long min = results.min("age").longValue();
    long max = results.max("age").longValue();
    double average = results.average("age");
    long matches = results.size();
  • 遍历查询结果:
    RealmResults<User> results = realm.where(User.class).findAll();
    for (User u : results) {
    // ... do something with the object ...
    }
    RealmResults<User> results = realm.where(User.class).findAll();
    for (int i = 0; i < results.size(); i++) {
    User u = results.get(i);
    // ... do something with the object ...
    }
  • 异步查询

声明并注册回调方法:

private RealmChangeListener callback = new RealmChangeListener<RealmResults<User>>() {
@Override
public void onChange(RealmResults<User> results) {
// called once the query complete and on every update
}
};

public void onStart() {
RealmResults<User> result = realm.where(User.class).findAllAsync();
result.addChangeListener(callback);
}

在退出 Activity 或者 Fragment 时移除监听器的注册以避免内存泄漏。

public void onStop () {
result.removeChangeListener(callback); // remove a particular listener
// or
result.removeChangeListeners(); // remove all registered listeners
}

主动检查查询是否完成

RealmResults<User> result = realm.where(User.class).findAllAsync();
if (result.isLoaded()) {
// Results are now available
}

注意同步查询返回的 RealmResults 实例的 isLoaded 方法会永远返回 true。可以在 Looper线程中使用异步查询异步查询需要使用Handler来传递查询结果在没有Looper的线程中使用异步查询会导致IllegalStateException 异常被抛出。

7. 自动更新数据条目:

在Realm中只要调用set方法就会自动更新对应的条目,而不用像其他的数据库那样调用update方法:

realm.beginTransaction();
Dog myDog = realm.createObject(Dog.class);
myDog.setName("Fido");
myDog.setAge(1);
realm.commitTransaction();

Dog myPuppy = realm.where(Dog.class).equalTo("age", 1).findFirst();
realm.beginTransaction();
myPuppy.setAge(2);
realm.commitTransaction();

8. 异步查询和更新数据

下面的示例是通过异步方式查询小狗的年龄等于1的狗并将其年龄设置为3.如果设置成功onSuccess方法将会被调用:

realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// begin & end transcation calls are done for you
Dog theDog = realm.where(Dog.class).equals("age", 1).findFirst();
theDog.setAge(3);
}
}, new Realm.Transaction.Callback() {
@Override
public void onSuccess() {
// Original Queries and Realm objects are automatically updated.
puppies.size(); // => 0 because there are no more puppies (less than 2 years old)
dog.getAge(); // => 3 the dogs age is updated
}
});

9. 删除数据

// obtain the results of a query
RealmResults<Dog> results = realm.where(Dog.class).findAll();
// All changes to data must happen in a transaction
realm.beginTransaction();
// remove single match
results.deleteFromRealm(0);
results.deleteLastFromRealm();
// remove a single object
Dog dog = results.get(5);
dog.deleteFromRealm();
// Delete all matches
results.deleteAllFromRealm();
realm.commitTransaction();

10. 多表关系

任意两个 RealmObject 可以相互关联。
下面的例子中Contact与Email建立了关联:

public class Email extends RealmObject {
private String address;
private boolean active;
// ... setters and getters left out
}

public class Contact extends RealmObject {
private String name;
private Email email;
// ... setters and getters left out
}
  • 多对一 关系

要实现多对一的关联只需要简单地声明一个 Realm 模型类的属性即可:

public class Contact extends RealmObject {
private Email email;
// Other fields…
}

设置一个类型为 RealmObject 的属性为空值(null)会清除该属性的引用,但并不会删除对应的 RealmObject。

  • 多对多 关系

要实现多对多的关联可以通过使用 RealmList 为一个对象关联0或多个其它对象。

public class Contact extends RealmObject {
private RealmList<Email> emails;
// Other fields…
}

RealmList 是 Realm 模型对象的容器,其行为与 Java 的普通 List 近乎一样。
同一个 Realm 模型对象可以存在于多个 RealmList 中。
同一个 Realm 模型对象可以在同一个 RealmList 中存在多次。

使用 Contact 和 Email 类

public class Email extends RealmObject {
private String address;
private boolean active;
// ... setters and getters left out
}

public class Contact extends RealmObject {
private String name;
private Email email;
// ... setters and getters left out
}

您可以通过标准的 getter 和 setter 来访问 RealmList.

realm.beginTransaction();
Contact contact = realm.createObject(Contact.class);
contact.setName("John Doe");

Email email1 = realm.createObject(Email.class);
email1.setAddress("john@example.com");
email1.setActive(true);
contact.getEmails().add(email1);

Email email2 = realm.createObject(Email.class);
email2.setNumber("jd@example.com");
email2.setActive(false);
contact.getEmails().add(email2);

realm.commitTransaction();

Realm支持关联查询。以如下模型举例:

public class Person extends RealmObject {
private String id;
private String name;
private RealmList<Dog> dogs;
// getters and setters
}

public class Dog extends RealmObject {
private String id;
private String name;
private String color;
// getters and setters
}

每个 User 对象都与多个 Dog 对象相关联,如下图所示:

// users => [U1,U2]
RealmResults<User> users = realm.where(User.class)
.equalTo("dogs.color", "Brown")
.findAll();

以上的查询含义为“所有至少含有一个 color 为 Brown 的 User”。请务必注意,这里的返回的 User 中,有可能包含 color 不为 Brown 的 Dog 对象,因为在其 RealmList 列表中,其它的 Dog 对象满足查询条件:

// r1 => [U1,U2]
RealmResults<User> r1 = realm.where(User.class)
.equalTo("dogs.name", "Fluffy")
.findAll();
// r2 => [U1,U2]
RealmResults<User> r2 = r1.where()
.equalTo("dogs.color", "Brown")
.findAll();

请注意第一个查询返回两个 User 对象,因为它们都满足查询条件。每个 User 对象都包含一个 Dog 对象列表——列表中至少有一个 Dog 对象满足查询条件。谨记我们是在寻找其拥有的 Dog 对象满足条件(name 和 color)的 User,不是在针对 Dog 对象进行查询。
因此第二个查询建立在第一个的 User 结果(r1)以及 r1 的每个 User 的 Dog 列表之上。两个 User 仍然满足第二个查询,但这次是 color 满足查询条件。

我们再深入了解下这个概念,请看以下代码:

// r1 => [u1,u2]
realmresults<user> r1 = realm.where(user.class)
.equalto("dogs.name", "fluffy")
.equalto("dogs.color", "brown")
.findall();

// r2 => [u2]
realmresults<user> r2 = realm.where(user.class)
.equalto("dogs.name", "fluffy")
.findall()
.where()
.equalto("dogs.color", "brown")
.findall();
.where()
.equalto("dogs.color", "yellow")
.findall();

第一个查询表示找到所有的User他至少有一个Dog的名字为fluffy并且找到所有User他至少有一个Dog的颜色是brown 然后返回这两个结果的交集。
第二个查询表示找到所有的User他至少有一个Dog的名字为fluffy;然后在这个结果之上找到所有的User他至少有一个Dog的颜色为brown;
最后在之前的结果之上找到所有的 User 他至少有一个 Dog 的颜色为 yellow。

我们来解释一下第一个查询以深入了解下这个行为。两个条件分别是equalto(“dogs.name”, “fluffy”) 和 equalto(“dogs.color”, “brown”)。
u1和u2 完全满足第一个条件 ——我们称其c1集合。u1和u2也同时完全满足第二个条件——我们称其 c2 集合。查询中的逻辑与即是c1与c2的交集c1与c2的交集就是u1和u2。因此r1就包含u1和u2。

第二个查询不一样。我们来分别讲解。该查询第一部分看起来是这样的:realmresults r2a = realm.where(user.class).equalto(“dogs.name”, “fluffy”).findall()。
它的结果包含 u1 和 u2。然后 r2b = r2a.where().equalto(“dogs.color”, “brown”).findall();
的结果仍然包含 u1 和 u2 (两个 User 都有颜色为 brown 的 Dog)。最后的查询 r2 = r2b.where().equalto(“dogs.color”, “yellow”).findall();
结果只包含 u2,因为只有 u2 同时有一个颜色为 brown 的 Dog 和一个颜色为 yellow 的 Dog。

11 自动更新(Auto-Refresh)

如果 Realm 实例存在于一个带有 Looper 的线程,那么这个 Realm 实例即具有自动更新的功能。这意味这如果发生了 Realm 数据库的变化,那么该 Realm 实例会在下一个事件循环(event loop)中自动更新。这个便捷的功能使您不必花费太多的精力就能保证的UI与数据的实时同步。
如果 Realm 的实例所在线程没有绑定 Looper,那么该实例不会被更新直到您手动调用 waitForChange() 方法。请注意,不更新 Realm 以保持对旧数据的引用会造成而外的磁盘和内存开销。这也是为什么要在线程结束时调用 close() 关闭 Realm 实例的一个重要原因。
如果您想确定当前 Realm 实例是否有自动更新功能,可以通过调用 isAutoRefresh() 方法查询。

12 实际例子

下面是官网给出的一些学习Demo建议大家通过这些代码来学习会比较快点:
introExample 包含了如何使用当前的API的简单例子。
gridViewExample 用来展示如何使用 Realm 作为 GridView 的后端存储。它同时也展示了如何用 JSON 来填充数据库。另外还有怎么通过 ABI splits 来缩小 APK 体积。
threadExample 展示了如何在多线程环境中使用 Realm。
adapterExample 展示了如何以一个非常便捷的方式使用 RealmBaseAdapter 绑定 RealmResults 到安卓的 ListView。
jsonExample 展示了 Realm 与 JSON 相关的功能。
encryptionExample 向您展示如何使用加密的 Realm。
rxJavaExamples 展示了如何与 RxJava 结合使用 Realm。
unitTestExample 展示了如何写与 Realm 相关的单元测试。

下面是API文档链接:
Realm API

概述

做Android开发的每位工程师想必都会接触到Json,它是客户端与服务器进行数据交互时使用的一种数据交换格式。当然大家也可能只接触过XML,但是这种可能性不是很大,因为涉及到客户端和服务端交互这种事情,一般都不是自己能够说得算的,还要取决于服务端开发工程师的习惯,一般大家都比较推崇JSON格式,因为XML方式解析较为麻烦,并且XML形式的交换方式数据较大,不如JSON来得轻量。Java中最常用的JSON解析库有:JSON-Java、Gson、Jackson、FastJson等,这些类库就我个人来说并没都使用过,网上有其他人做过对比,这些解析库中来自阿里的FastJson性能最好。这个我自己没有亲自做过这方面的对比所以也不肯定,大家如果有兴趣找找对应的文章看看。

JSON语法简介

如果大家对JSON还不了解,可以到如下网站上了解下。它其实也是一种key/value的形式的数据交换方式.
http://www.json.org.cn/
http://www.w3school.com.cn/json/json_syntax.asp
下面是最核心的语法,大家如果不想花太多时间在这部分的话,可以简单下看下下面的语法,其实Json语法很简单花一两分钟就可以完全掌握了。

  • 总体语法:

    • 数据在名称/值对中
    • 两个数据之间由逗号分隔
    • 花括号保存对象
    • 方括号保存数组
  • 名值对定义:
    JSON 名称/值对 定义如下

    "firstName" : "John"

    变量名和数值使用冒号隔开。

    JSON 值可以是:

    • 数字(整数或浮点数)
    • 字符串(在双引号中)
    • 逻辑值(true 或 false)
    • 数组(在方括号中)
    • 对象(在花括号中)
    • null
  • JSON 对象
    JSON 对象在花括号中书写:
    对象可以包含多个名称/值对:

    { "firstName":"John" , "lastName":"Doe" }
  • JSON 数组
    JSON 数组在方括号中书写:
    数组可包含多个对象:

    {
    "employees": [
    { "firstName":"John" , "lastName":"Doe" },
    { "firstName":"Anna" , "lastName":"Smith" },
    { "firstName":"Peter" , "lastName":"Jones" }
    ]
    }

    下面是一个比较简单的例子:

    {
    "firstName": "John",
    "lastName": "Smith",
    "sex": "female",
    "age": 28,
    "address":
    {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021"
    },
    "phoneNumber":
    [
    {
    "type": "home",
    "number": "212 555-1234"
    },
    {
    "type": "fax",
    "number": "646 555-4567"
    }
    ]
    }

往Android Studio中添加 GSON:

下面是GitHub的地址,上面给出了GSON的源码,文档地址等
https://github.com/google/gson
下面是一个推荐的GSON教程供进一步深入学习。
http://www.studytrails.com/java/json/java-google-json-introduction.jsp

要在Android studio中导入GSON,需要在gradle.build中添加如下依赖关系:

compile 'com.google.code.gson:gson:2.6.2'

基本使用:

Gson中提供了fromJson() 和toJson() 两个直接用于解析和生成的方法,前者可以将数据从JSON中取出这个过程称为反序列化,后者用于将数据转化为JSON格式,称为序列化。

  • 序列化

    Gson gson = new Gson();
    gson.toJson(100); //int
    gson.toJson("jimmy"); //String
    gson.toJson(new Long(1000000000)); //Long
    gson.toJson(true); //boolean
    gson.toJson(new Date()); //Date
    int[] values = {1, 2, 3,4,5,6};
    gson.toJson(values); //array
  • 反序列化

    int one = gson.fromJson("100", int.class);
    Integer oneInteger = gson.fromJson("100", Integer.class);
    Long oneLong = gson.fromJson("1000", Long.class);
    Boolean falseBoolean = gson.fromJson("false", Boolean.class);
    String str = gson.fromJson("\"abc\"", String.class);
  • POJO类的序列化和反序列化:
    上面介绍的是最基本类的序列化和反序列化,但是上面的使用范围十分有限,更为常见的是POJO的序列化和反序列化:

    • JavaBean 序列化
      定义 Bean对象:

      class User {
      private int id = 000001;
      private String name = "jimmy";
      private int age = 28;
      public User() {
      }
      }

      序列化 JavaBean

      Gson gson = new Gson();
      String customjson = gson.toJson(new User());
    • JavaBean 反序列化:

      String jsonRes = "{\"id\":000001,\"name\":\"jimmy\",\"age\":25}"
      User user = gson.fromJson(jsonRes, User.class);

GSON 流式序列化反序列化

  • Gson的流式反序列化
    String gsonString = "{\"name\":\"jimmy\",\"age\":\"28\"}";
    User user = new User();
    JsonReader gsonReader = new JsonReader(new StringReader(gsonString));
    gsonReader.beginObject();
    while (gsonReader.hasNext()) {
    String tag = gsonReader.nextName();
    switch (tag) {
    case "name":
    user.name = gsonReader.nextString();
    break;
    case "age":
    user.age = gsonReader.nextInt();
    break;
    }
    }
    gsonReader.endObject();
  • Gson的流式序列化
    JsonWriter gsonwriter = new JsonWriter(new OutputStreamWriter(System.out));
    gsonwriter.beginObject()
    .name("name").value("jimmy")
    .name("age").value(28)
    .endObject();
    gsonwriter.flush();

Gson中使用泛型

要在GSON中使用泛型必须使用TypeToken,当我们希望将数据解析为List时可以写成:

Gson gson = new Gson();
String jsonArray = "[\"Android\",\"IOS\",\"WINDPHONE\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());

使用泛型

public class ResponeResult<T> {
public int code;
public String message;
public T data;
}

如何使用:

Type user = new TypeToken<ResponeResult<User>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);
User user = userResult.data;

Type userList = new TypeToken<ResponeResult<List<User>>>(){}.getType();
ResponeResult<List<User>> userListResult = gson.fromJson(json,userList);
List<User> users = userListResult.data;

默认GSON 的定制化:

一般情况下上面介绍的GSON已经够大家用了,但是还是有些常用的配置需要我们在序列化和反序列化的时候对其进行序列化和反序列化规则的配置:

Gson gson = new GsonBuilder()
//序列化null,默认的情况下null值是不序列化的
.serializeNulls()
// 设置日期时间格式,在序列化和反序化时均生效
.setDateFormat("yyyy-MM-dd")
//禁此序列化内部类
.disableInnerClassSerialization()
//生成不可执行的Json(多了 )]}' 这4个字符)
.generateNonExecutableJson()
//禁止转义html标签
.disableHtmlEscaping()
//格式化输出
.setPrettyPrinting()
.create();

常用的注释标志

  • 属性重命名 @SerializedName 注解的使用

    从上面POJO的生成与解析可以看出在默认情况下GSON要求Bean的成员变量名和json字段名以及变量类型都应该一致,但是这个是很难保证的,一般客户端的开发和服务端的开发是分开来的,不可能服务端一修改我们这边就跟着改,特别是两个开发语言命名的规则不同的时候,一般都会存在纷争,GSON为了克服这个问题引入了@SerializedName。

@SerializedName("office_tel")
public String officeTel;

这样GSON就会将来自服务端的json中的office_tel与Bean中的officeTel关联起来:
还有种比较难搞定的情况,Bean 中的某个字段对应对应服务器端的多个变量名的时候,可以使用如下方式为
为POJO字段提供备选属性名:

@SerializedName(value = "officeTel", alternate = {"tel", "office_tel"})
public String officeTel;
  • 选择性导出

    • @Expose

有的时候我们需要对POJO中的某些字段进行有选择性的导出,这时候我们就可以通过在要导出的字段上加上@Expose注解,不导出的字段不加的方式来达到这个目的:

@Expose(deserialize = true,serialize = true) 	//序列化和反序列化都生效
@Expose(deserialize = true,serialize = false) //反序列化时生效
@Expose(deserialize = false,serialize = true) //序列化时生效
@Expose(deserialize = false,serialize = false) //序列化和反序列化都不生效
这种情况下就不能使用默认的GSON了,而必须使用上面介绍的GsonBuild进行定制:
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
* @Since @Until基于版本号导出:
有时候我们的某些字段在某个版本是不需要导出的,这种情形就可以通过@Since 和 @Until,并结合GsonBuilder.setVersion(Double)来实现。这种情况个人是没有用到过,但是以后的开发中应该会有这种需求。

* 基于访问修饰符:
Gson gson = new GsonBuilder()
.excludeFieldsWithModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PRIVATE)
.create();
* 自定义排除规则:

要自定义排除规则可以通过在创建GsonBuilder的时候通过addSerializationExclusionStrategy 和addDeserializationExclusionStrategy方法分别添加序列化和反序列化的排除规则。

Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
// 这里作判断,决定要不要排除该字段,return true为排除
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
// 直接排除某个类 ,return true为排除
}
})
.create();

某种类型序列化和反序列化过程的自定义:

  • TypeAdapter
    首先定义一个User类型的TypeAdapter,覆写里面的write以及read方法。这两个方法会在后续操作User.class的时候被调用。

    public class UserTypeAdapter extends TypeAdapter<User> {
    @Override
    public void write(JsonWriter out, User value) throws IOException {
    out.beginObject();
    out.name("name").value(value.name);
    out.name("age").value(value.age);
    out.endObject();
    }

    @Override
    public User read(JsonReader in) throws IOException {
    User user = new User();
    in.beginObject();
    while (in.hasNext()) {
    switch (in.nextName()) {
    case "name":
    user.name = in.nextString();
    break;
    case "age":
    user.age = in.nextInt();
    break;
    }
    }
    in.endObject();
    return user;
    }
    }

    实现TypeAdapter后还需要通过registerTypeAdapter注册给GsonBuilder,在注册的时候需要指定要关联的对象以及对应的TypeAdapter类型。

    User user = new User("jimmy", 28);
    Gson gson = new GsonBuilder()
    //为User注册TypeAdapter
    .registerTypeAdapter(User.class, new UserTypeAdapter())
    .create();

    再考虑如下情况,如果有多个类继承自同一个父类,并且我们对这一系列的子类都使用同一种方式序列化以及反序列化,那么这种情况有两种做法,一种是使用registerTypeAdapter对一个个子类进行注册,另一种是使用registerTypeHierarchyAdapter注册父类即可。但是一定要注意registerTypeHierarchyAdapter不支持泛型。

  • JsonSerializer与JsonDeserializer
    上面的registerTypeAdapter必须同步指定序列化和反序列化的实现,如果并不是想序列化和反序列化两个过程都重新覆写,只是想实现其中之一那么可以使用JsonSerializer与JsonDeserializer来代替TypeAdapter
    下面是来自http://www.studytrails.com/java/json/java-google-json-custom-serializer-deserializer.jsp 的JsonDeserializer用法的例子

    public class DogDeserialiser implements JsonDeserializer<Dog> {
    @Override
    public Dog deserialihttp://www.studytrails.com/java/json/java-google-json-custom-serializer-deserializer.jspze(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    String name = json.getAsJsonObject().get("name").getAsString();
    name = name.replace(" ", "_");
    Dog dog = new Dog(name);
    return dog;
    }
    public static void main(String[] args) {
    String json = "{\"animal\":{\"name\":\"I am a dog\"}}";
    Gson gson = new GsonBuilder().registerTypeAdapter(Dog.class,
    new DogDeserialiser()).create();
    Type animalType = new TypeToken<Animal<Dog>>() {
    }.getType();
    Animal<Dog> animal = gson.fromJson(json, animalType);
    System.out.println(animal.get().getName());
    }
    }

    JsonSerializer

    public class DogSerializer implements JsonSerializer<dog> {
    @Override
    public JsonElement serialize(Dog src, Type typeOfSrc,JsonSerializationContext context) {
    // This method gets involved whenever the parser encounters the Dog
    // object (for which this serializer is registered)
    JsonObject object = new JsonObject();
    String name = src.getName().replaceAll(" ", "_");
    object.addProperty("name", name);
    // we create the json object for the dog and send it back to the
    // Gson serializer
    return object;
    }

    public static void main(String[] args) {
    Animall<Dog> animal = new Animall<Dog>();
    Dog dog = new Dog("I am a dog");
    animal.setAnimal(dog);
    // Create the GsonBuilder and register a serializer for the Dog class.
    // Whenever the Dog class is encountered Gson calls the DogSerializer
    // we set pretty printing own to format the json
    Gson gson = new GsonBuilder().registerTypeAdapter(Dog.class, new DogSerializer()).setPrettyPrinting().create();
    // Since Animal contains generic type create the type using TypeToken
    // class.
    Type animalType = new TypeToken<Animal<Dog>>() {
    }.getType();
    System.out.println(gson.toJson(animal, animalType));
    }
    }

    需要注意的是registerTypeAdapter必须使用包装类型不能使用int.class,long.class,float.class
    如果一个被序列化的对象本身就带有泛型,且注册了相应的TypeAdapter,那么必须调用Gson.toJson(Object,Type),明确告诉Gson对象的类型。

    Type type = new TypeToken<List<User>>() {}.getType();
    TypeAdapter typeAdapter = new TypeAdapter<List<User>>() {
    };
    Gson gson = new GsonBuilder()
    .registerTypeAdapter(type, typeAdapter)
    .create();
    List<User> list = new ArrayList<>();
    list.add(new User("jimmy",28));
    list.add(new User("kitty",22));
    String result = gson.toJson(list, type);
  • TypeAdapterFactory
    除了用上述的方式注册TypeAdapter外还可以使用TypeAdapterFactory,可以根据传入的类型来查看目前是否有现有的TypeAdapter,如果有的话就返回对应的TypeAdapter并注册,如果没有就返回null。注册工厂的方法为GsonBuilder.registerTypeAdapterFactory。

  • @JsonAdapter
    使用@JsonAdapter注解可以替代registerTypeAdapterFactory以及registerTypeAdapter方法,这样可以避免在每次进行序列化和反序列化的时候都要进行注册。它的参数必须是且必须是TypeAdpater,JsonSerializer/JsonDeserializer或者TypeAdapterFactory。
    用法如下:

    @JsonAdapter(DogTypeAdapter.class)
    public class Dog {

    }