12/08/2018, 16:16

Rx trong Kotlin (P2)

Tiếp theo phần 1 Mình xin đi vào thực hành một ví dụ cho các bạn dễ hình dung Ví dụ đơn giản này sẽ như sau, nhập chuỗi string vào EditText và sau đó tự động response hiển thị lên TextView, response sẽ được gọi bằng một API cứ 1s sau khi nhập text. Đơn giản như sau: Để dễ hiểu hơn, mình sẽ trình ...

Tiếp theo phần 1 Mình xin đi vào thực hành một ví dụ cho các bạn dễ hình dung Ví dụ đơn giản này sẽ như sau, nhập chuỗi string vào EditText và sau đó tự động response hiển thị lên TextView, response sẽ được gọi bằng một API cứ 1s sau khi nhập text.
Đơn giản như sau: Để dễ hiểu hơn, mình sẽ trình bày cả 2 cách reactive và non-reactive

Non-reactive

Mình sử dụng hàm schedule() của Timer để chạy chạy sau cử mỗi 1s, đừng quên dùng runOnUiThread() nhé. Đây là code khi viết bằng Java

//Java
Timer timer = new Timer();

final TextView textView = (TextView) findViewById(R.id.textView);
final EditText editText = (EditText) findViewById(R.id.editText);

editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
                                  int after) {
    }

    @Override
    public void onTextChanged(final CharSequence s, int start, int before,
                              int count) {
        if (timer != null)
            timer.cancel();
    }

    @Override
    public void afterTextChanged(final Editable s) {
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("Output : " + editText.getText());
                    }
                });
            }

        }, 1000);
    }
});

Đây là code khi trình bày bằng Kotlin

//Kotlin
var timer: Timer? = Timer()

val editTextStop = findViewById(R.id.editText) as EditText
editTextStop.addTextChangedListener(object : TextWatcher {
    override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit

    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
            timer?.cancel()
    }

    override fun afterTextChanged(s: Editable) {
        timer = Timer()
        timer!!.schedule(object : TimerTask() {
            override fun run() {
                runOnUiThread { textView.setText("Output : " + editTextStop.getText()) }
            }
        }, 1000)
    }
})

Reactive

Với reactive chúng ta cần làm theo 3 steps: 1-Tạo một observable 2-Sử dụng debounce để set delay 1s 3-Subscribe nó Bạn có thể làm như sau, với Java:

Observable.create(new Observable.OnSubscribe<String>() {
                    @Override
                    public void call(final Subscriber<? super String> subscriber) {
                        editText.addTextChangedListener(new TextWatcher() {
                            @Override
                            public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
                            }
        
                            @Override
                            public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
                                subscriber.onNext(s.toString());
                            }
        
                            @Override
                            public void afterTextChanged(final Editable s) {
                            }
                        });
                    }
                })
                .debounce(1000, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<String>() {
                    @Override
                    public void call(final String s) {
                        textView.setText("Output : " + s);
                    }
                });

Còn khi bạn dùng Kotlin:

Observable.create(Observable.OnSubscribe<String> { subscriber ->
            editText.addTextChangedListener(object : TextWatcher {
                override fun afterTextChanged(s: Editable?) = Unit

                override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit

                override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int)
                        = subscriber.onNext(s.toString())
            })
        }).debounce(1000, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    text ->
                    textView.text = "Output : " + text
                })

RxBinding

Đoạn code trên có quá nhiều các boilerplate, nhìn quá dài dòng, để hạn chế điều này và giúp code của bạn ngắn hơn, loại bỏ được các boilerplate này ta có thể dùng RxBindings với rất nhiều cách binding cho UI widgets. Có thể dùng trong cả Java và Kotlin. Với Java, ta dùng Retrolambda và RxBinding, ví dụ code như sau:

//Java with Retrolambda and RxBinding
RxTextView.afterTextChangeEvents(editText)
              .debounce(1000,TimeUnit.MILLISECONDS)
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe(tvChangeEvent -> {
                 textView.setText("Output : " + tvChangeEvent.view()
                            .getText());
              });

Với Kotlin, ta cũng sử dụng RxBinding

//Kotlin with RxBinding
    RxTextView.afterTextChangeEvents(editText)
              .debounce(1000, TimeUnit.MILLISECONDS)
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe { tvChangeEvent -> 
                        textView.text = "Output : " + tvChangeEvent.view().text 
              }

Không còn nhiều các đoạn code thừa nữa, cũng giúp rất nhiều cho maintainer dễ dàng hiểu được code của bạn. Hy vọng bài này đã giúp các bạn một phần hiểu được cách dùng Rx trong Java và đặc biệt là Kotlin, chúc bạn vui với ngôn ngữ mới này!

0