Search code examples
javaandroidkotlinrx-java2rx-binding

Screen is not responding as I set a result to another edit text when using RxJava


I'm implement an TextInputEditText with RxBinding.

Problem: When I do some math operation on first TextInputEditText and set the result to another TextInputEditText, The screen is not responding. When I start to type another digit, its not displayed in the edit text. I know why this happened, but don't know how to fix. For more detail, please check the code below.

Code:

class NumberSystemFragment : Fragment() {
  override fun onCreateView() { ... }
  override fun onViewCreated() {
    binding?.run {
      // It still ok if just observe a single edit text
      etBinary.observeInput().subscribe {
        val dec = it.fold(0L) { acc, el ->
          (acc * 2) + el.digitToInt(radix = 2)
        }
        val oct = dec.toString(8)
        val hex = dec.toString(16)

        etDecimal.setText(dec.toString())
        etOctal.setText(oct)
        etHexadecimal.setText(hex)
      }

      // But, when I add more EditText, the screen will not responding.
      // I know why this can happen, because when I set the result from operation above,
      // and display it to another EditTexts, then the other EditText will begin to observe its input too.
      // Then the racing condition or whatever it is, will occur.
      etDecimal.observeInput().subscribe {
        val bin = it.toLong().toString(2)
        val oct = it.toLong().toString(8)
        val hex = it.toLong().toString(16)

        etBinary.setText(bin)
        etOctal.setText(oct)
        etHexadecimal.setText(hex)
      }
    }
  }

  private fun <T : EditText> T.observeInput() =
    RxTextView.textChanges(this)
      .skipInitialValue()
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())
      .map(CharSequence::toString)
      .publish()
      .refCount()
}

Sorry, I'm new with RxJava, RxBinding, etc.


Solution

  • Two issues.

    1. You have created a feedback loop by setting text on the other editors, which then trigger updates on the current editor.
    2. If your code crashes, it will stop listening to values.

    First of all, make these extension method changes:

    private fun <T: EditText> T.observeInput() =
        RxTextView.textChanges(this)
            .skipInitialValue()
            .map(CharSequence::toString) // <------------------ order matters
            .distinctUntilChanged() // <----------------------- order matters
            .observeOn(AndroidSchedulers.mainThread())
            .publish()
            .refCount()
    
    private fun TextInputEditText.setText(text: CharSequence, onlyIfChanged: Boolean) {
        if (onlyIfChanged) {
            if (Objects.equals(this.text.toString(), text.toString())) {
                return;
            }
        }
        this.setText(text);
    }
    

    Then, update your onNext handlers:

    // etBinary
    
    // store calculation data
    try {
        Log.d("XYZ-etBinary", t);
        val decResult = t.fold(0L) { acc, element ->
            (acc * 2) + element.digitToInt(2)
        }
        val octResult = decResult.toString(8)
        val hexResult = decResult.toString(16).uppercase()
    
        // and display the results to another edit text which corresponds with their radix representation
        etDecimal.setText(decResult.toString(), true)
        etOctal.setText(octResult, true)
        etHexadecimal.setText(hexResult, true)
    } catch (e: Throwable) {
        Log.e("XYZ-etBinary", e.message.toString())
    }
    
    // etDecimal
    
    // store calculation data
    try {
        Log.d("XYZ-etDecimal", t);
        val binResult = t.toLong().toString(2)
        val octResult = t.toLong().toString(8)
        val hexResult = t.toLong().toString(16).uppercase()
    
        // and display the results to another edit text which corresponds with their radix representation
        etBinary.setText(binResult, true)
        etOctal.setText(octResult, true)
        etHexadecimal.setText(hexResult, true)
    } catch (e: Throwable) {
        Log.e("XYZ-etDecimal", e.message.toString())
    }
    

    Also posted it as a Pull Request on your repo: https://github.com/dansampl/EditTextWithRxJavaSampleApp/pull/1/files