Coder Social home page Coder Social logo

Comments (6)

kirillzyusko avatar kirillzyusko commented on June 9, 2024 3

I think this is the most interesting issue I've ever discovered in my life πŸ‘€

So basically crash happens in this code:

public void afterTextChanged(Editable s) {
    if (!ReactEditText.this.mIsSettingTextFromJS && ReactEditText.this.mListeners != null) {
        Iterator var2 = ReactEditText.this.mListeners.iterator();

        while(var2.hasNext()) {
            TextWatcher listener = (TextWatcher)var2.next();
            listener.afterTextChanged(s);
        }
    }

}

On every afterTextChanged input-mask-android removes the listener and adds it back.

Now let's consider oversimplified example (you can run this code in https://www.jdoodle.com/online-java-compiler):

import java.util.ArrayList;
import java.util.Iterator;

public class MyClass {
    public static void main(String args[]) {
        ArrayList<Integer> mListeners = new ArrayList<>();
        mListeners.add(0);
        mListeners.add(1);
        // mListeners.add(2);

        Iterator<Integer> iterator = mListeners.iterator();

        while (iterator.hasNext()) {
            Integer listener = iterator.next();

            // Check if the listener is equal to 1
            // 1 is OnlyChangeIfRequiredMaskedTextChangedListener and we simulate the behavior of this class
            if (listener == 1) {
                int i = mListeners.indexOf(listener);

                if (i >= 0) {
                    mListeners.remove(i);
                }

                // Add the removed element at the end
                mListeners.add(listener);
            }
        }

        // Print the modified list
        System.out.println(mListeners);
    }
}

This code will be executed without any issues, and will produce expected [0, 1] output.

But if we uncomment mListeners.add(2); - we'll get our exception:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1095)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:1049)
	at MyClass.main(MyClass.java:14)

These 3 array elements are real elements in RN app:

  • 0 is EmojiTextWatcher (seems like it's added by OS);
  • 1 is OnlyChangeIfRequiredMaskedTextChangedListener (added by react-native-text-input-mask);
  • 2 is a listener that I'm attaching in RNKC.

So here we get a situation, where react-native code works for 2 listeners, but doesn't work for 3. To make this code working again with 3 listeners we just can change the order - if the order is [2, 0, 1] or [0, 2, 1] it will work, because removal/insertion operation will be the last one and java will not throw an exception.

Or before iterating we can create a copy of array and iterate over this copy (but we'll have to modify react-native source code). For example Android code iterates over mListeners as:

if (mListeners != null) {
    final ArrayList<TextWatcher> list = mListeners;
    final int count = list.size();
    for (int i = 0; i < count; i++) {
        list.get(i).afterTextChanged(text);
    }
}

Oversimplified version with new iteration approach that works may look like this:

import java.util.ArrayList;
import java.util.Iterator;

public class MyClass {
    public static void main(String args[]) {
        ArrayList<Integer> mListeners = new ArrayList<>();
        mListeners.add(0);
        mListeners.add(1);
        mListeners.add(2);
        
        final ArrayList<Integer> list = mListeners;
        final int count = list.size();
        for (int i = 0; i < count; i++) {
            Integer listener = list.get(i);
            if (listener == 1) {
                mListeners.remove(i);


                // Add the removed element at the end
                mListeners.add(listener);
            }
        }

        // Print the modified list
        System.out.println(mListeners);
    }
}

So now we have two options on how to fix this issue:

  • insert RNKC listener before MaskedTextChangedListener;
  • change how react-native iterates over array.

Both solutions are not good enough because:

  • we don't have an access to private variable of ReactEditText and can not insert to the beginning (actually we can use reflection and I already experimented with it - it fixes the problem, but everyone knows that reflection should be used as a last resort);
  • that means that only new version of RN (let's say 0.74) will support RNKC 1.10. And such compatibility range is not good enough for general purpose library.

I will think about other solutions too, but if you @devoren have any ideas - please, share them 😊

from react-native-keyboard-controller.

kirillzyusko avatar kirillzyusko commented on June 9, 2024 2

Just a quick update - the issue is reproducible on my end in 100% cases.

Basically this exception happens when you iterate over and collection gets modified, so this code will trigger this exception:

List<String> myList = new ArrayList<>();
Iterator<String> iterator = myList.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    myList.add("newElement"); // This will throw ConcurrentModificationException
}

There is some issues because in different calls I see different amount of listeners.

3 listeners 2 listeners
image image

I've tried naive things like adding listener after 100ms or 1s, but it will produce crash when you type a symbol πŸ€·β€β™‚οΈ

I'll search for new solutions πŸ‘€

from react-native-keyboard-controller.

kirillzyusko avatar kirillzyusko commented on June 9, 2024 2

I think I will go with reflection approach (it should work pretty reliably) and most likely will submit a PR to react-native repo :)

I think it'll be fixed in 1.10.2 🀞

from react-native-keyboard-controller.

4yki avatar 4yki commented on June 9, 2024 2

Just've tried new version. The issue was fixed for me. Huge thx for your work @kirillzyusko

from react-native-keyboard-controller.

kirillzyusko avatar kirillzyusko commented on June 9, 2024 1

Nice catch @devoren

I'll try to have a look today on it πŸ‘€

from react-native-keyboard-controller.

devoren avatar devoren commented on June 9, 2024

To be honest, I'm not good at the native side. But I understand the problem, so is it because of react-native limitations? Then I don't want to take up too much of your timeπŸ˜…. For now i will use version 1.9.5 without any problems

from react-native-keyboard-controller.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.