01 กันยายน 2559

[Android Code] มารู้จักกับ RxJava และ RxAndroid กันเถอะ [ตอนที่ 4]



        ในที่สุดก็มาถึงตอนที่ 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
    }
});

        หมายเหตุ - ซึ่ง FromCallable จะดีตรงที่ใช้แทน Defer + Just ได้เลย ซึ่งเดี๋ยว Defer เจ้าของบล็อกจะต่อไป (ขอบคุณ Kittinun Vantasin ที่ช่วยเพิ่มเติมให้)

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 แบบไหนมาอธิบายนะ ตัดสินใจยากมาก แต่ละอันไม่ใช่น้อยๆเลย...

แหล่งข้อมูลอ้างอิง

        • Create Operators - ReactiveX Documentation




เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium