0

I am getting the following crash report:

Fatal Exception: 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. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(-1, class android.widget.ListPopupWindow$DropDownListView) with Adapter(class com.galleri5.android.adapters.PlaceAutocompleteAdapter)]
       at android.widget.ListView.layoutChildren(ListView.java:1562)
       at android.widget.AbsListView.onLayout(AbsListView.java:2151)
       at android.view.View.layout(View.java:15689)
       at android.view.ViewGroup.layout(ViewGroup.java:5040)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
       at android.view.View.layout(View.java:15689)
       at android.view.ViewGroup.layout(ViewGroup.java:5040)
       at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2116)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1873)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1084)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5990)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
       at android.view.Choreographer.doCallbacks(Choreographer.java:580)
       at android.view.Choreographer.doFrame(Choreographer.java:550)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:135)
       at android.app.ActivityThread.main(ActivityThread.java:5343)
       at java.lang.reflect.Method.invoke(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:372)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)

And here is my PlaceAutoCompleteAdapter:

public class PlaceAutocompleteAdapter
        extends ArrayAdapter<AutocompletePrediction> implements Filterable {
    protected final String TAG = getClass().getSimpleName();
    private static final CharacterStyle STYLE_BOLD = new StyleSpan(Typeface.BOLD);
    private ArrayList<AutocompletePrediction> mResultList;
    private GoogleApiClient mGoogleApiClient;
    private LatLngBounds mBounds;
    private AutocompleteFilter mPlaceFilter;

    public PlaceAutocompleteAdapter(Context context, GoogleApiClient googleApiClient,
                                    LatLngBounds bounds, AutocompleteFilter filter) {
        super(context, android.R.layout.simple_expandable_list_item_2, android.R.id.text1);
        mGoogleApiClient = googleApiClient;
        mBounds = bounds;
        mPlaceFilter = filter;
    }

    public void setBounds(LatLngBounds bounds) {
        mBounds = bounds;
    }

    @Override
    public int getCount() {
        return mResultList != null ? mResultList.size() : 0;
    }

    @Override
    public AutocompletePrediction getItem(int position) {
        return position < mResultList.size() ? mResultList.get(position) : null;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View row = super.getView(position, convertView, parent);

        // Sets the primary and secondary text for a row.
        // Note that getPrimaryText() and getSecondaryText() return a CharSequence that may contain
        // styling based on the given CharacterStyle.

        AutocompletePrediction item = getItem(position);
        if((item != null) && (row != null)) {
            row.setBackgroundColor(Color.parseColor("#707070"));
            TextView textView1 = (TextView) row.findViewById(android.R.id.text1);
            textView1.setTextSize(14);
            TextView textView2 = (TextView) row.findViewById(android.R.id.text2);
            textView2.setTextSize(12);
            textView1.setText(item.getPrimaryText(STYLE_BOLD));
            textView2.setText(item.getSecondaryText(STYLE_BOLD));
        }

        return row;
    }

    /**
     * Returns the filter for the current set of autocomplete results.
     */
    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                // Skip the autocomplete query if no constraints are given.
                if (constraint != null) {
                    // Query the autocomplete API for the (constraint) search string.
                    mResultList = getAutocomplete(constraint);
                    if ((mResultList != null) && (!mResultList.isEmpty())) {
                        // The API successfully returned results.
                        results.values = mResultList;
                        results.count = mResultList.size();
                    }
                }
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if (results != null) {
                    // The API returned at least one result, update the data.
                    if (results.count > 0) {
                        notifyDataSetChanged();
                    }
                } else {
                    // The API did not return any results, invalidate the data set.
                    notifyDataSetInvalidated();
                }
            }

            @Override
            public CharSequence convertResultToString(Object resultValue) {
                // Override this method to display a readable result in the AutocompleteTextView
                // when clicked.
                if (resultValue instanceof AutocompletePrediction) {
                    return ((AutocompletePrediction) resultValue).getFullText(null);
                } else {
                    return super.convertResultToString(resultValue);
                }
            }
        };
    }

    private ArrayList<AutocompletePrediction> getAutocomplete(CharSequence constraint) {
        if (mGoogleApiClient.isConnected()) {

            // Submit the query to the autocomplete API and retrieve a PendingResult that will
            // contain the results when the query completes.
            PendingResult<AutocompletePredictionBuffer> results =
                    Places.GeoDataApi
                            .getAutocompletePredictions(mGoogleApiClient, constraint.toString(),
                                    mBounds, mPlaceFilter);

            // This method should have been called off the main UI thread. Block and wait for at most 60s
            // for a result from the API.
            AutocompletePredictionBuffer autocompletePredictions = results
                    .await(60, TimeUnit.SECONDS);

            // Confirm that the query completed successfully, otherwise return null
            final Status status = autocompletePredictions.getStatus();
            if (!status.isSuccess()) {
                Toast.makeText(getContext(), "Error contacting API: " + status.toString(),
                        Toast.LENGTH_SHORT).show();
                autocompletePredictions.release();
                return null;
            }

            // Freeze the results immutable representation that can be stored safely.
            return DataBufferUtils.freezeAndClose(autocompletePredictions);
        }
        return null;
    }
}

This code is taken from Google's places api autocomplete sample. I modified it for UI changes otherwise, more or less it is same. I can't figure out how this crash happened and what could be reason for it? Any help would be highly appreciated.

Edit : Here are the relevant parts of the Activity where I am setting the adapter:

@Bind(R.id.loc)
    AutoCompleteTextView loc;
protected GoogleApiClient mGoogleApiClient;

    private PlaceAutocompleteAdapter mAdapter;

    private static final LatLngBounds BOUNDS_GREATER_SYDNEY = new LatLngBounds(
            new LatLng(-34.041458, 150.790100), new LatLng(-33.682247, 151.383362));

@Override
onCreate() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this, 0 /* clientId */, this)
                .addApi(Places.GEO_DATA_API)
                .build();

loc.setOnItemClickListener(mAutocompleteClickListener);
        mAdapter = new PlaceAutocompleteAdapter(this, mGoogleApiClient, BOUNDS_GREATER_SYDNEY, null);
        loc.setAdapter(mAdapter);
}
Amit Tiwari
  • 3,684
  • 6
  • 33
  • 75

1 Answers1

1

According to exception, you are changing the list content in another thread. To solve, you must change the content of arraylist in UI thread. From doc performFiltering method is running in different thread. To fix the problem add content to arraylist in publishResults method and call notifydatasetchange.

Other solution is : Update performfiltering as follows :

 protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                // Skip the autocomplete query if no constraints are given.
                if (constraint != null) {
                    final ArrayList<AutocompletePrediction> mResultList1 = getAutocomplete(constraint);

((Activity)mContext).runOnUiThread(new Runnable() {
            public void run() {

                    if ((mResultList1 != null) && (!mResultList1.isEmpty())) {
                        // The API successfully returned results.
                        results.values = mResultList1;
                        results.count = mResultList1.size();
                          mResultList.clear();
                           mResultList.addAll( mResultList1);
                    }
            }
        });
                }
                return results;
            }

For reference

Community
  • 1
  • 1
USKMobility
  • 5,721
  • 2
  • 27
  • 34