ในที่สุดก็มาถึงตอนที่ 4 แล้ว ไม่น่าเชื่อว่าเจ้าของบล็อกจะเขียนมาถึงขนาดนี้ได้ และนั่นก็หมายความว่า Rx นี่มันน่าดึงดูดถึงขนาดที่ว่าเจ้าของบล็อกต้องหยิบมาเขียนถึง 4 ตอนเลยนะ (และจะมีตามมาอีกหลายตอนแน่นอน)
ซึ่งตอนที่ 4 นี้ เจ้าของบล็อกจะมาเจาะลึกไปกับรายละเอียดการสร้าง Observable ขึ้นมาซักตัวหนึ่ง ว่าจริงๆแล้วมันทำด้วยวิธีไหนได้บ้าง
การสร้าง Observable ที่เจ้าของบล็อกยกตัวอย่างในบทความที่ผ่านมาก็จะมีแค่พวก Just, From หรือ FromCallable ซึ่งนั่นเป็นแค่ส่วนหนึ่งของวิธีสร้าง Observable ทั้งหมดที่มีใน Rx โดยที่คำสั่งต่างๆที่ใช้ในการสร้าง Observable จะเรียกว่า Create Operator ครับ
และ Operator ที่มีใน Rx มีหลายประเภทมาก พูดแล้วก็อยากจะถอนหายใจ เพราะไม่รู้จะเขียนอธิบายได้จนครบทั้งหมดหรือป่าว...
Create Operator : Operator ชุดแรกสุดที่จะต้องรู้จัก
ตามชื่อของมันแหละ มันมีไว้สร้าง Observable เพื่อเรียกใช้งานในภายหลังการสร้าง Observable เพื่อนำไปใช้งานไม่ได้มีอยู่รูปแบบเดียว ดังนั้นใน Rx จึงได้ออกแบบให้สามารถสร้าง Observable ได้หลายวิธี ซึ่งมีทั้งหมดดังนี้
• Just
• From
• Create
• Defer
• Empty
• Never
• Error (Throw)
• Interval
• Timer
• Range
• Repeat
• Start
Just
ถ้าจะให้อธิบายหน้าที่ของ Just แบบง่ายๆ ก็คงจะเป็น "มายังไงก็ไปยังงั้น..." เพราะไม่ว่าผู้ที่หลงเข้ามาอ่านส่งค่าอะไรให้ มันก็จะไปสร้างเป็น Observable แล้วส่งกลับมาให้ทันที เป็นหนึ่งใน Create Operator ที่ใช้งานง่ายที่สุดแล้วล่ะpublic List<LatLng> getCurrentUserLocation() {
...
}
...
Observable<List<LatLng>> observable = Observable.just(getCurrentUserLocation());
observable.subscribe(new Observer<List<LatLng>>() {
@Override
public void onCompleted() {
// TODO Do something
}
@Override
public void onError(Throwable e) {
// TODO Do something
}
@Override
public void onNext(List<LatLng> latLngList) {
// TODO Do something
}
});
สิ่งที่เกิดขึ้นก็คือค่าที่ได้จาก getCurrentUserLocation จะถูกส่งเข้า onNext แล้วตามด้วย onCompleted ทันที
นั่นแหละ เค้าถึงเรียกว่า Just ไง ก็มันทำแค่นั้น (ฮาๆ)
ถึงแม้ว่ามันจะใช้งานได้ง่ายและสะดวก แต่ข้อเสียของการสร้าง Observable ด้วย Just ก็คือมันไม่สามารถจัดการกับ Exception ที่เกิดขึ้นใน getCurrentUserLocation ได้ เพราะสิ่งที่ Just สนก็คือค่าที่ส่งออกมาจาก Method ดังกล่าวเท่านั้น แล้วส่งค่าไปให้ Subscriber ในทันที
เพราะจริงๆแล้วคำสั่ง getCurrentUserLocation นั้นทำงานก่อนที่จะสร้าง Observable และเมื่อทำงานเสร็จแล้วส่งข้อมูลใดๆกลับมา ข้อมูลตัวนั้นก็จะถูกนำไปสร้างเป็น Observable ด้วย Just ทันที นั่นก็หมายความว่าถ้าเกิด Exception ใดๆก็ตามใน getCurrentUserLocation แปลว่ามันเกิดขึ้นก่อนที่ Observable จะสร้างขึ้นมา ดังนั้นอย่าหวังว่ามันจะเข้า onError ให้นะ
From
From ก็ใช้สำหรับการนำค่าอะไรก็ตามไปสร้างเป็น Observable เช่นเดียวกับ Justซึ่ง Just นั้นส่งอะไรมาก็ส่งไปทั้งก้อน ไม่สนใจว่าข้อมูลนั้นจะเป็นอะไรยังไง แต่สำหรับ From นั้นจะเห็นการทำงานที่แตกต่างกับ Just ได้อย่างชัดเจน เมื่อข้อมูลที่ส่งเข้ามามีลักษณะเป็น List หรือ Array เพราะ From จะส่งข้อมูลที่อยู่ใน List ไปให้ Subscriber ทีละตัวแทนที่จะส่งไปทั้งก้อน
เพื่อให้เห็นความแตกต่างระหว่าง Just กับ From มากขึ้น จึงขอหยิบตัวอย่างโค้ดที่ใช้อธิบายการทำงานของ Just มาเปลี่ยนให้เป็น From แทน ก็จะได้ออกมาดังนี้
public List<LatLng> getCurrentUserLocation() {
...
}
...
Observable<LatLng> observable = Observable.from(getCurrentUserLocation());
observable.subscribe(new Observer<LatLng>() {
@Override
public void onCompleted() {
// TODO Do something
}
@Override
public void onError(Throwable e) {
// TODO Do something
}
@Override
public void onNext(LatLng latLng) {
// TODO Do something
}
});
จะเห็นว่า Observable ที่สร้างขึ้นมานั้น เป็น LatLng ไม่ใช่ List<LatLng>
เพราะ From จะทำข้อมูลที่เป็น List หรือ Array ให้กลายเป็นข้อมูลเดียวๆแล้วส่งไปทีละตัวตามลำดับจนครบ นั่นก็หมายความว่า ถ้าคำสั่ง getCurrentUserLocation ส่งข้อมูลออกมาเป็น List ที่มีข้อมูลอยู่ทั้งหมด 3 ตัว ผลที่เกิดขึ้นก็คือ onNext จะถูกเรียก 3 ครั้ง และในแต่ละครั้งก็จะส่งข้อมูลแต่ละตัวที่อยู่ใน List มาให้ด้วย และ onCompleted จะถูกเรียกเมื่อ onNext ถูกเรียกครบทั้งหมด 3 ครั้งแล้ว
และการใช้ From ก็ไม่สามารถจัดการกับ Exception ได้เช่นกันนะ
แต่ From ยังมีคำสั่งพิเศษอีกหนึ่งอย่าง นั่นก็คือ FromCallable ซึ่งเป็นการสร้าง Observable ที่ทำงานอยู่บน Callable ที่สามารถจัดการกับ Exception ได้ ดังนั้นจากเดิมที่ไม่สามารถจัดการกับ Exception ได้ เมื่อเปลี่ยนมาใช้ FromCallable ก็จะสามารถดัก Exception ที่เกิดขึ้นผ่าน onError ได้แล้ว
ดังนั้นเมื่อสร้าง Observable ด้วย FromCallable แล้วกำหนด getCurrentUserLocation ให้ทำงานอยู่ใน Callable เมื่อเกิด Exception ใดๆก็ตามขึ้น ก็จะเข้าสู่ onError ทันที
public LatLng getCurrentUserLocation() {
...
}
...
Observable<LatLng> observable = Observable.fromCallable(new Callable<LatLng>() {
@Override
public LatLng call() throws Exception {
return getCurrentUserLocation();
}
});
observable.subscribe(new Observer<LatLng>() {
@Override
public void onCompleted() {
// TODO Do something
}
@Override
public void onError(Throwable e) {
// TODO Do something
}
@Override
public void onNext(LatLng latLng) {
// TODO Do something
}
});
Create
เป็นการสร้าง Observable แบบพื้นฐานที่สุด เพราะตัวมันไม่จัดการอะไรให้ ผู้ที่หลงเข้ามาอ่านต้อง Manual เองทั้งหมด ทำให้สามารถจัดการ Event ที่จะใช้เกิดขึ้นได้ตามใจชอบเลยObservable<PostDetail> observable = Observable.create(new Observable.OnSubscribe<PostDetail>() {
@Override
public void call(Subscriber<? super PostDetail> subscriber) {
try {
List<PostDetail> postDetailList = getPostDetailFromServer();
for(PostDetail postDetail : postDetailList) {
subscriber.onNext(postDetail);
}
subscriber.onCompleted();
} catch (InterruptedException e) {
subscriber.onError(e);
}
}
});
observable.subscribe(new Observer<PostDetail>() {
@Override
public void onCompleted() {
// TODO Do something
}
@Override
public void onError(Throwable e) {
// TODO Do something
}
@Override
public void onNext(PostDetail postDetail) {
// TODO Do something
}
});
จากโค้ดข้างบนจะเห็นว่าเมื่อใช้ Create ในการสร้าง Observable จะมี Method ที่ชื่อว่า call อยู่ข้างใน (เหมือนกับ FromCallable แต่มีจุดที่แตกต่างกันอยู่) ซึ่ง call จะส่ง Parameter ที่ชื่อว่า subscriber มาให้ ซึ่งตัวนี้เอาไว้ให้ผู้ที่หลงเข้ามาอ่านเรียก onNext, onCompleted หรือ onError ด้วยตัวเอง
ดังนั้นเจ้าของบล็อกจึงยกตัวอย่างคำสั่ง getPostDetailFromServer ขึ้นมาว่าเป็นคำสั่งเอาไว้ดึงข้อมูลจาก Server แล้วส่งกลับมาเป็น Array หรือ List เมื่อได้ข้อมูลแล้วก็จะส่งเข้า onNext ทีละตัวจนครบ แล้วจบท้ายด้วย onCompleted
และถ้าเกิด Exception ใดๆก็ตาม ก็จะกำหนดให้เข้า onError แทน
ซึ่ง Create จะค่อนข้างสะดวกกับกรณีที่ผู้ที่หลงเข้ามาอ่านอยากจะทำคำสั่งบางอย่างให้กลายเป็น Observable แล้วต้องมีการจัดการเพิ่มเติม
หมายเหตุ - ไม่แนะนำให้ใช้ Create ถ้าไม่จำเป็น (ขอบคุณ Kittinun Vantasin ที่ช่วยเพิ่มเติมให้)
Defer
เป็นการสร้าง Observable โดยที่ Observable จะถูกสร้างขึ้นมาเฉยๆแต่ยังไม่ทำงานในทันที จะทำงานก็ต่อเมื่อ Subscribe เริ่มทำงานเท่านั้นซึ่งรูปแบบการใช้งานจะไปครอบอยู่บน Create Operator ตัวอื่นๆแบบนี้
Observable<String> observable = Observable.defer(new Func0<Observable<String>>() {
@Override
public Observable<String> call() {
return Observable.just("Hello");
}
});
จะเห็นว่าข้างใน Defer จะให้ผู้ที่หลงเข้ามาอ่านสร้าง Observable ด้วยวิธีไหนก็ได้ ซึ่งเจ้าของบล็อกยกตัวอย่างง่ายๆเป็น Just และโยน String เข้าไป ซึ่ง Defer ก็จะสร้าง Observable จากตัวนี้อีกทีหนึ่ง ออกมากลายเป็น Observable สำหรับค่า String เหมือนเดิม
เมื่อใช้ Defer แล้วมันต่างจากเดิมยังไง?
เพื่อให้เห็นภาพมากขึ้น ลองยกตัวอย่างว่าเจ้าของบล็อกสร้างคลาสตัวหนึ่งขึ้นมาที่ตัวมันเองมี Getter ที่จะสร้าง Observable แล้วโยนออกมาให้เลยดังนี้
public class Place {
String name;
public void setName(String name) {
this.name = name;
}
public Observable<String> getNameObservable() {
return Observable.just(name);
}
}
จะเกิดอะไรขึ้นถ้าเรียกใช้งานแบบนี้?
Place place = new Place();
Observable<String> observable = place.getNameObservable();
place.setName("Terminal 21");
observable.subscribe(new Action1<String>() {
@Override
public void call(String name) {
Log.d("Rx", "Place is " + name);
}
});
สิ่งที่เกิดขึ้นในโค้ดข้างบนนี้คือ เจ้าของบล็อกสร้าง Instance ของคลาส Place ขึ้นมา จากนั้นก็สร้าง Observable ด้วยคำสั่ง getNameObservable แล้วจึงกำหนดชื่อสถานที่ด้วยคำสั่ง setName โดยกำหนดลงไปว่า Terminal 21 หลังจากนั้นก็ให้ Subscriber เริ่มทำงาน แล้วแสดงค่าที่ได้ผ่าน Log
และที่คือผลลัพธ์ที่เกิดขึ้นใน Logcat
D/Rx: Place is null
จะเห็นว่า String ตัวดังกล่าวกลายเป็น Null เพราะว่า Observable ถูกสร้างขึ้นก่อนที่เจ้าของบล็อกจะไปกำหนดค่าด้วย setName ดังนั้นไม่ว่า Subscriber จะทำงานกี่ครั้ง ค่าที่ได้ก็จะเป็น Null อยู่ดี
ทีนี้เจ้าของบล็อกลองปรับเปลี่ยนนิดหน่อยโดยใช้ Defer ครอบอีกชั้นจากของเดิมแบบนี้
public class Place {
String name;
public void setName(String name) {
this.name = name;
}
public Observable<String> getPlaceNameObservable() {
return Observable.defer(new Func0<Observable<String>>() {
@Override
public Observable<String> call() {
return Observable.just(name);
}
});
}
}
รู้สึกดู Inception เล็กน้อย แต่ช่างมัน... ทีนี้ตอนเรียกใช้งานก็จะยังคงเหมือนเดิมเป๊ะๆ เพราะ Defer ไม่ได้เปลี่ยนแปลงอะไรในการเรียกใช้งาน
Place place = new Place();
Observable<String> observable = place.getNameObservable();
place.setName("Terminal 21");
observable.subscribe(new Action1<String>() {
@Override
public void call(String name) {
Log.d("Rx", "Place is " + name);
}
});
และนี่คือผลลัพธ์ที่เกิดขึ้นจนทำให้คุณต้องอึ้ง!!
D/Rx: Place is Terminal 21
นั่นล่ะฮะ จะเห็นว่าถึงแม้เจ้าของบล็อกจะกำหนดค่าใน setName ทีหลัง แต่ตอนที่ Subscriber เริ่มทำงาน ก็จะได้ผลลัพธ์เป็นค่าที่พึ่งกำหนดไว้ ถึงแม้ว่าจะสร้าง Observable ไว้ก่อนอยู่แล้วก็ตาม
สรุปก็คือ Defer คือการทำให้ Observable ทำงานเฉพาะตอนที่ Subscriber ทำงานนั่นเอง จึงทำให้ค่าใน Observable มีการเปลี่ยนแปลงในภายหลังได้ จนกว่า Subscriber จะเริ่มทำงานจริงๆ
Empty
เป็นการสร้าง Observable แบบไม่มีการส่งข้อมูลอะไรใดๆ มีแค่การส่ง onCompleted ออกมาเท่านั้นObservable observable = Observable.empty();
observable.doOnCompleted(new Action0() {
@Override
public void call() {
// TODO Do something
}
});
เนื่องจาก Empty นั้นจะไม่มี onNext หรือ onError เกิดขึ้น ดังนั้นเวลากำหนด Subscriber ให้กำหนดด้วย doOnCompleted ก็เพียงพอแล้ว
Never
เป็นการสร้าง Observable ขึ้นมา โดยจะไม่มีการส่ง onNext, onCompleted และ onError ออกมา และจะไม่หยุดทำงาน (Terminate) ด้วยตัวเองObservable observable = Observable.never();
observable.doOnSubscribe(new Action0() {
@Override
public void call() {
// TODO Do something
}
});
เนื่องจากตัวมันเองไม่มี Event ใดๆส่งออกมา และจะไม่หยุดทำงาน ดังนั้น Event เกิดขึ้นก็จะมีแค่รู้ว่า Subscriber เริ่มทำงานแล้วหรือยัง จึงสามารถใช้ doOnSubscribe เพื่อคอยเช็คตรงนี้ได้อยู่ และตัวมันก็สามารถ Unscubscribe ได้อยู่นะ
มีไว้เพื่ออะไรเนี่ย!?
Error (Throw)
ตาม Concept ของ Rx จะเรียกว่า Throw แต่เวลาเรียกใช้งานใน RxJava จะเป็น Errorซึ่งมีไว้สร้าง Observable ที่จะส่ง onError เท่านั้น อยากจะส่ง Exception อะไรก็กำหนดได้เลย
NullPointerException exception = new NullPointerException("This value should not be null");
Observable observable = Observable.error(exception);
observable.doOnError(new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
// TODO Do something
}
});
และเนื่องจากมันมีแค่ onError ดังนั้นเจ้าของบล็อกจึงแนะนำให้ใช้กับ doOnError ก็พอแล้ว
Interval
เป็นการสร้าง Observable ที่จะวนการทำงานไปเรื่อยๆโดยมีระยะเวลาในแต่ละครั้งที่กำหนดได้ เหมาะกับการทำงานที่ต้องการวนซ้ำเรื่อยๆ ซึ่งกำหนดระยะเวลาในการทำงานแต่ละครั้งได้Observable<Long> observable = Observable.interval(1, TimeUnit.SECONDS);
observable.subscribe(new Action1<Long>() {
@Override
public void call(Long count) {
// TODO Do something
}
});
ซึ่งการสร้าง Observable ด้วย Interval จะทำให้ได้ Observable ที่ส่งข้อมูลมาให้เป็นค่า Long ซึ่งหมายถึงจำนวนรอบของการทำงาน ซึ่งจะนับจาก 0, 1, 2, ... ไปเรื่อยๆ
ถ้าดูจากเวลาใน LogCat แล้วก็แม่นเหมือนกันนะเนี่ย คลาดเคลื่อนไม่ถึง 10ms เลย
Timer
จะคล้ายๆกับ Interval แต่จะไม่ได้ทำงานวนซ้ำเรื่อยๆ เพราะ Timer จะเป็นการสร้าง Observable ที่ขะยังไม่ทำงานในทันที แต่จะรอจนกว่าจะถึงเวลาที่กำหนดไว้แล้วจึงค่อยทำงาน และทำงานเพียงแค่ครั้งเดียวเท่านั้น แล้วก็จบเลยObservable<Long> observable = Observable.timer(1, TimeUnit.SECONDS);
observable.subscribe(new Observer<Long>() {
@Override
public void onCompleted() {
// TODO DO something
}
@Override
public void onError(Throwable e) {
// TODO DO something
}
@Override
public void onNext(Long count) {
// TODO DO something
}
});
เหมาะกับเวลาที่ไม่ต้องการให้คำสั่งทำงานในทันที แต่อยากให้ Delay ซักครู่ก่อนแล้วค่อยทำงาน
ซึ่ง Timer จะส่ง Event ออกมาเป็น onNext ที่จะส่งค่า Long มาให้เหมือนกับ Interval แต่เนื่องจาก Timer ทำงานแค่ครั้งเดียว ดังนั้นมันก็จะส่งมาครั้งเดียวแล้วตามด้วย onCompleted ในทันที
Range
เป็นการสร้าง Observable ที่คล้ายคลึงกับ Interval แต่จะต่างกันตรงที่ Range ไม่ได้เป็นกำทำงานที่สัมพันธ์กับเวลา แต่จะวนการทำงานซ้ำๆตามค่าที่กำหนดไว้Observable<Integer> observable = Observable.range(5, 3);
observable.subscribe(new Observer<Integer>() {
@Override
public void onCompleted() {
// TODO Do something
}
@Override
public void onError(Throwable e) {
// TODO Do something
}
@Override
public void onNext(Integer index) {
// TODO Do something
}
});
ซึ่งการทำงานของ Range จะถูกกำหนดจาก 2 ค่าด้วยกัน ตัวแรกคือค่าที่ต้องการจะให้ Range เริ่มนับ (ถ้าเป็น Interval หรือ Range จะเริ่มจาก 0 ทุกครั้ง) และตัวที่สองคือค่าที่กำหนดว่าจะให้ Range ทำงานวนซ้ำกี่ครั้ง
ดังนั้นจากตัวอย่างโค้ดข้างบนจะเป็นการกำหนดให้ Range ทำงานโดยเริ่มตันนับเลขตั้งแต่ 5 และทำงานวนซ้ำ 3 ครั้ง
D/RX: onNext (Index : 5)
D/RX: onNext (Index : 6)
D/RX: onNext (Index : 7)
D/RX: onCompleted
สามารถให้เริ่มจากค่าติดลบได้ แต่จำกัดว่าค่าจะเพิ่มทีละ 1 เท่านั้น และจำนวนครั้งห้ามติดลบนะ
Repeat
เป็นการทำให้ Observable ใดๆก็ตามทำงานซ้ำตามจำนวนครั้งที่กำหนดได้ ซึ่ง Repeat ไม่ได้ใช้สร้าง Observable โดยตรง แต่จะใช้กับ Observable ที่ถูกสร้างขึ้นมาด้วย Create Operator ตัวอื่นๆ แล้วเกิดอยากจะให้ทำงานวนซ้ำตามที่ต้องการObservable<String> observable = Observable.just("Sleeping For Less").repeat(2);
observable.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
// TODO Do something
}
@Override
public void onError(Throwable e) {
// TODO Do something
}
@Override
public void onNext(String message) {
// TODO Do something
}
});
จากตัวอย่างจะเห็นว่าเจ้าของบล็อกใช้ Just เพื่อสร้าง Observable อย่างง่ายๆขึ้น โดยโยนคำว่า Sleeping For Less ที่เป็น String เข้าไป จากนั้นจึงใช้ Repeat เพื่อให้ทำงาน 2 ครั้ง
D/RX: onNext (Sleeping For Less)
D/RX: onNext (Sleeping For Less)
D/RX: onCompleted
ถ้าไม่กำหนดจำนวนครั้งให้กับ Repeat ก็จะเป็นการวนการทำงานซ้ำไปเรื่อยๆไม่มีที่สิ้นสุดนั่นเอง
ความแตกต่างระหว่าง Repeat กับ Range จะอยู่ตรงที่ Range จะสร้าง Observable ได้ด้วยตัวเอง และจะเป็น Observable ที่จะส่ง Event เป็นตัวแปร Long ออกมาให้ ในขณะที่ Repeat จะขึ้นอยู่กับ Observable ที่ใช้สร้างก่อนหน้า ดังนั้น Repeat จะไม่มีการส่ง Event เป็นค่าของตัวเอง แต่จะอิงค่าจาก Observable ตัวเก่าแทน
จากตัวอย่าง เจ้าของบล็อกใช้ Just ในการสร้างและกำหนดให้มันส่ง String ดังนั้นเมื่อใช้ Repeat กับ Observable ตัวนี้ ผลที่ได้ก็ยังเป็น Observable ที่ส่ง Event ออกมาเป็นค่า String ออกมาเหมือนเดิมอยู่ดี
Start
เป็นการสร้าง Observable ที่จะทำงานทันทีเมื่อถูกสร้างขึ้นมา แล้วจะเก็บค่านั้นไว้เพื่อ Reuse ทุกครั้งเมื่อเกิด Event ซึ่งจะต่างจาก Create Operator ตัวอื่นๆที่จะทำงานทุกๆครั้งที่ Subscriber ทำงานStart เป็น Observable ที่อยู่ใน RxJava ตั้งแต่แรก แต่ทีมงานได้แยกออกไปเป็น Utility ดังนั้นเวลาจะใช้งาน Create Operator ตัวนี้จะต้องมีการเพิ่ม Dependency เข้ามาอีกตัวหนึ่งด้วย
compile 'io.reactivex:rxjava-async-util:+'
โดยการเรียกใช้งาน Start จะอยู่ในคลาสที่ชื่อว่า Async
Observable<String> observable = Async.start(new Func0<String>() {
@Override
public String call() {
return "Hello World";
}
});
อาจจะดูเหมือนไม่มีอะไร แต่เนื่องจากตัวอย่างโค้ดข้างบนนี้เป็นการใช้ Start สร้าง Observable ที่ให้ผลลัพธ์ไม่ต่างอะไรกับ Operator อย่าง Create ซักเท่าไร
ดังนั้นเพื่อให้เข้าใจมากขึ้น ให้ดูการทำงานของ Create ก่อนนะครับ
public class Place {
String name;
public void setName(String name) {
this.name = name;
}
public Observable<String> getPlaceNameObservable() {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext(name);
}
});
}
}
ถ้ายังจำกันได้ โค้ดนี้เคยยกตัวอย่างไปแล้วในตอนที่อธิบายการทำงานของ Defer แต่คราวนี้เจ้าของบล็อกจะยกตัวอย่างเป็นการใช้ Create แทน
และเรียกใช้งานแบบนี้
Place place = new Place();
Observable<String> observable = fuck.getPlaceNameObservable();
place.setName("Terminal 21");
observable.doOnNext(new Action1<String>() {
@Override
public void call(String name) {
Log.d("Check", "onNext (" + s + ")");
}
});
place.setName("Siam Paragon");
observable.doOnNext(new Action1<String>() {
@Override
public void call(String name) {
Log.d("Check", "onNext (" + s + ")");
}
});
สิ่งที่เกิดขึ้นก็คือ Event จะถูกส่งมาพร้อมกับค่าล่าสุดที่เจ้าของบล็อกกำหนดเข้าไปในคำสั่ง setName
D/RX: onNext (Terminal 21)
D/RX: onNext (Siam Paragon)
แต่ถ้าเจ้าของบล็อกใช้ Start ในการสร้าง Observable ล่ะ?
public class Place {
String name;
public void setName(String name) {
this.name = name;
}
public Observable<String> getPlaceNameObservable() {
return Async.start(new Func0<String>() {
@Override
public String call() {
return name;
}
});
}
}
ส่วนการเรียกใช้งานก็ยังคงเหมือนเดิม
Place place = new Place();
Observable<String> observable = fuck.getPlaceNameObservable();
place.setName("Terminal 21");
observable.doOnNext(new Action1<String>() {
@Override
public void call(String name) {
Log.d("Check", "onNext (" + s + ")");
}
});
place.setName("Siam Paragon");
observable.doOnNext(new Action1<String>() {
@Override
public void call(String name) {
Log.d("Check", "onNext (" + s + ")");
}
});
แต่สิ่งที่เกิดขึ้นจะเป็นแบบนี้แทน
D/RX: onNext (Terminal 21)
D/RX: onNext (Terminal 21)
เพราะ Observable ที่สร้างด้วย Start จะทำงานเพียงแค่ครั้งเดียวเท่านั้น หลังจากนั้นมันก็จะเก็บค่าเดิมไว้ในการส่ง Event ตลอด ไม่ว่าเจ้าของบล็อกจะสร้าง Subscriber ใหม่ก็ตาม
และข้อดีอย่างหนึ่งของ Start ใน RxJava ก็คือตัวมันทำงานเป็น Asynchronous ทันทีโดยไม่ต้องกำหนดอะไร
หมดแล้วววววววววว
น้ำตาแทบจะไหล จะเยอะแยะอะไรนักหนา กว่าจะอธิบายครบนี่เสียเวลาชีวิตไปเยอะมากกกกกกและทั้งหมดนี้ก็คือ Create Operator ของ Rx ที่มีให้ผู้ที่หลงเข้ามาอ่านได้ใช้งาน จะเห็นว่าแต่ละตัวมีรูปแบบการทำงานและจุดประสงค์ในการใช้งานแตกต่างกันออกไป จึงเป็นที่มาว่าทำไม Rx มีรูปแบบการใช้งานที่ยืดหยุ่นมาก
ดังนั้นหยิบไปใช้ให้เหมาะสมกับแต่ละการใช้งานนะครับ
บทความหน้าก็รอดูกันต่อว่าเจ้าของบล็อกจะหยิบ Operator แบบไหนมาอธิบายนะ ตัดสินใจยากมาก แต่ละอันไม่ใช่น้อยๆเลย...