0

I've been receiving a few Play Store reports of my android app crashing due to IllegalStateException in my AutoCompleteTextView. However, I've never been able to recreate the issue myself. The AutoCompleteTextView uses custom filtering, and I more or less followed the codes here: Custom filtering in AutoCompleteTextView not working

I've done some research on the problem, found this as one of the results: How to resolve "The content of the adapter has changed but ListView did not receive a notification” exception but still can't work out what's wrong with my code. I can't even figure out what's causing the exception to occur, it just occurs randomly, though most of the time it doesn't occur. What's more frustrating, the exception stack trace doesn't point out which line of my code that is causing the exception.

Here is the exception stack trace:

java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(-1, class android.widget.AutoCompleteTextView$DropDownListView) with Adapter(class transponders.transmob.JourneyPlanner$LocationAdapter)]
at android.widget.ListView.layoutChildren(ListView.java:1541)
at android.widget.AbsListView.onLayout(AbsListView.java:1428)
at android.view.View.layout(View.java:7228)
at android.widget.FrameLayout.onLayout(FrameLayout.java:338)
at android.view.View.layout(View.java:7228)
at android.view.ViewRoot.performTraversals(ViewRoot.java:1148)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1868)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3691)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:907)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:665)
at dalvik.system.NativeStart.main(Native Method)

And here is my custom filter code in the custom adapter class:

        @Override
        public Filter getFilter() 
        {
            Filter filter = new Filter(){
                  @Override
                  protected FilterResults performFiltering(CharSequence constraint)
                  {
                      locations.clear();
                      FilterResults results = new FilterResults();
                      String typedText = ownerACTV.getText().toString();

                      try
                      {
                          Log.d("performFiltering", "TYPED TEXT LENGTH: " + typedText.length());
                          if(!typedText.equals("Current location"))
                          {
                              String url = "some URL";
                              request = new JSONRequest();
                              request.setListener(JourneyPlanner.this);
                              request.execute(url);

                              String result = request.get();

                              Object obj = JSONValue.parse(result);
                              JSONArray array = (JSONArray)((JSONObject)obj).get("Locations");

                              for(int i = 0; i < array.size(); i++)
                              {
                                  JSONObject location = (JSONObject) array.get(i);
                                  String desc = (String) location.get("Description");
                                  String id = (String) location.get("Id");

                                  Log.d("filterResults", "Description: " + desc);
                                  Log.d("filterResults", "ID: " + id);

                                  String[] splitted = desc.split(" \\(");
                                  if (splitted.length > 1)
                                      desc = splitted[0];

                                  locationsToID.put(desc, id);
                                  locations.add(desc);
                              }
                          }

                          results.values = locations;
                          results.count = locations.size();

                      }
                      catch (Exception e)
                      {
                          e.printStackTrace();
                      }

                      Log.d("performFiltering", "return FilterResults");
                      return results;
                  }

                  @Override
                  protected void publishResults(CharSequence arg0, FilterResults results) 
                  {  
                      if (results != null && results.count > 0)
                      {
                         notifyDataSetChanged();
                      }
                      else
                      {
                          notifyDataSetInvalidated();
                      }
                  }
            };

            return filter;
        }

I can give you other parts of the code as well if necessary. I have to make a call to the web in order to populate the autocomplete drop down list, I'm guessing that it may be the one causing the exception?

halfer
  • 19,824
  • 17
  • 99
  • 186
ABVincita
  • 8,676
  • 2
  • 18
  • 16
  • Ah, any suggestions on how to replicate the crash itself will be appreciated as well, since I can't seem to find a way to replicate the condition that causes the crash. Thanks! – ABVincita Dec 11 '13 at 13:02

2 Answers2

1

the problem is your "locations" field that you modify in non ui thread (perfornFiltering method), you should use FilterResults which is passed to publishResults

pskink
  • 23,874
  • 6
  • 66
  • 77
  • and also you use it in performFiltering which is called from non ui thread, you cannot modify "locations" in non ui thread – pskink Dec 08 '13 at 13:27
  • Can you elaborate more on this? The 'locations' object is an List of String, and it's used in the LocationAdapter class (which extends ArrayAdapter). The 'locations' list is used in the getItem and getCount method of the LocationAdapter class. I read somewhere that we should override those two methods when we use custom filter... – ABVincita Dec 08 '13 at 13:29
  • and btw why are you writing a new Filter instead of using existing standard ones? – pskink Dec 08 '13 at 13:31
  • 1
    see my answer here http://stackoverflow.com/questions/19858843/how-to-dynamically-add-suggestions-to-autocompletetextview-with-preserving-chara – pskink Dec 08 '13 at 13:36
  • 1
    Ummm I wrote that custom getFilter() method because that seems to be the suggested way based on what I found... I haven't seen your approach using FilterQueryProvider and SimpleCursorAdapter until just now. Is there any advantages of using FilterQueryProvider compared to writing custom getFilter() method? – ABVincita Dec 08 '13 at 13:44
  • If I want to keep using the custom getFilter() method, then I should not modify the 'locations' list inside performFiltering but in the publishResults instead, is that correct? – ABVincita Dec 08 '13 at 13:49
  • yes dont modify locations in non ui thread, but again why reinventing the wheel ? – pskink Dec 08 '13 at 13:53
  • Thanks, I will move the 'locations' list into the publishResults method, and try out your solution as well. Can you give suggestions on how to test the code for stability as well? Because I can't seem to replicate the crash myself... – ABVincita Dec 11 '13 at 13:02
  • my solution with FilterQueryProvider is so simple that i cannot see any points where it could fail... – pskink Dec 11 '13 at 13:07
  • Super late reply, but I guess better late than never. So I implemented @pskink answer and now seems like the autocomplete is not throwing the crash anymore and it's even faster than my previous implementation. So, accepted pskink's answer :) Thanks! – ABVincita Nov 22 '14 at 13:57
1

Here is the getFilter method:which is working fine

 @Override
 public Filter getFilter() {
 Filter filter = new Filter() {
    @Override
    protected FilterResults performFiltering(final CharSequence  constraint) {
        final FilterResults filterResults = new FilterResults();

       activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                resultList = autocomplete(constraint.toString());

                // Assign the data to the FilterResults
                filterResults.values = resultList;
                filterResults.count = resultList.size();

            }
        });




        return filterResults;
    }

    @Override
    protected void publishResults(CharSequence constraint, final     FilterResults results) {
        try {
           activity.runOnUiThread(new Runnable() {
               @Override
               public void run() {
                   if (results != null && results.count > 0) {

                      // resultList=(ArrayList<String>)results.values;
                       notifyDataSetChanged();
                   } else {
                       notifyDataSetInvalidated();
khushbu
  • 382
  • 1
  • 3
  • 15