RecyclerView und java.lang.IndexOutOfBoundsException: Inkonsistenz festgestellt. Ungültiger Ansichtshalteradapter positionViewHolder in Samsung-Geräten


253

Ich habe eine Recycler-Ansicht, die auf allen Geräten außer Samsung einwandfrei funktioniert. Auf Samsung bekomme ich

java.lang.IndexOutOfBoundsException: Inkonsistenz festgestellt. Ungültiger Ansichtshalteradapter positionViewHolder

wenn ich mit einer anderen Aktivität zum Fragment mit der Recycler-Ansicht zurückkehre.

Adaptercode:

public class FeedRecyclerAdapter extends RecyclerView.Adapter<FeedRecyclerAdapter.MovieViewHolder> {
    public static final String getUserPhoto = APIConstants.BASE_URL + APIConstants.PICTURE_PATH_SMALL;
    Movie[] mMovies = null;
    Context mContext = null;
    Activity mActivity = null;
    LinearLayoutManager mManager = null;
    private Bus uiBus = null;
    int mCountOfLikes = 0;

    //Constructor
    public FeedRecyclerAdapter(Movie[] movies, Context context, Activity activity,
                               LinearLayoutManager manager) {
        mContext = context;
        mActivity = activity;
        mMovies = movies;
        mManager = manager;
        uiBus = BusProvider.getUIBusInstance();
    }

    public void setMoviesAndNotify(Movie[] movies, boolean movieIgnored) {
        mMovies = movies;
        int firstItem = mManager.findFirstVisibleItemPosition();
        View firstItemView = mManager.findViewByPosition(firstItem);
        int topOffset = firstItemView.getTop();
        notifyDataSetChanged();
        if(movieIgnored) {
            mManager.scrollToPositionWithOffset(firstItem - 1, topOffset);
        } else {
            mManager.scrollToPositionWithOffset(firstItem, topOffset);
        }
    }

    // Create new views (called by layout manager)
    @Override
    public MovieViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.feed_one_recommended_movie_layout, parent, false);

        return new MovieViewHolder(view);
    }

    // Replaced contend of each view (called by layout manager)
    @Override
    public void onBindViewHolder(MovieViewHolder holder, int position) {
        setLikes(holder, position);
        setAddToCollection(holder, position);
        setTitle(holder, position);
        setIgnoreMovieInfo(holder, position);
        setMovieInfo(holder, position);
        setPosterAndTrailer(holder, position);
        setDescription(holder, position);
        setTags(holder, position);
    }

    // returns item count (called by layout manager)
    @Override
    public int getItemCount() {
        return mMovies != null ? mMovies.length : 0;
    }

    private void setLikes(final MovieViewHolder holder, final int position) {
        List<Reason> likes = new ArrayList<>();
        for(Reason reason : mMovies[position].reasons) {
            if(reason.title.equals("Liked this movie")) {
                likes.add(reason);
            }
        }
        mCountOfLikes = likes.size();
        holder.likeButton.setText(mContext.getString(R.string.like)
            + Html.fromHtml(getCountOfLikesString(mCountOfLikes)));
        final MovieRepo repo = MovieRepo.getInstance();
        final int pos = position;
        final MovieViewHolder viewHolder = holder;
        holder.likeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mMovies[pos].isLiked) {
                    repo.unlikeMovie(AuthStore.getInstance()
                        .getAuthToken(), mMovies[pos].id, new Callback<Movie>() {
                        @Override
                        public void success(Movie movie, Response response) {
                            Drawable img = mContext.getResources().getDrawable(R.drawable.ic_like);
                            viewHolder.likeButton
                                .setCompoundDrawablesWithIntrinsicBounds(img, null, null, null);
                            if (--mCountOfLikes <= 0) {
                                viewHolder.likeButton.setText(mContext.getString(R.string.like));
                            } else {
                                viewHolder.likeButton
                                    .setText(Html.fromHtml(mContext.getString(R.string.like)
                                        + getCountOfLikesString(mCountOfLikes)));
                            }
                            mMovies[pos].isLiked = false;
                        }

                        @Override
                        public void failure(RetrofitError error) {
                            Toast.makeText(mContext.getApplicationContext(),
                                mContext.getString(R.string.cannot_like), Toast.LENGTH_LONG)
                                .show();
                        }
                    });
                } else {
                    repo.likeMovie(AuthStore.getInstance()
                        .getAuthToken(), mMovies[pos].id, new Callback<Movie>() {
                        @Override
                        public void success(Movie movie, Response response) {
                            Drawable img = mContext.getResources().getDrawable(R.drawable.ic_liked_green);
                            viewHolder.likeButton
                                .setCompoundDrawablesWithIntrinsicBounds(img, null, null, null);
                            viewHolder.likeButton
                                .setText(Html.fromHtml(mContext.getString(R.string.like)
                                    + getCountOfLikesString(++mCountOfLikes)));
                            mMovies[pos].isLiked = true;
                            setComments(holder, position);
                        }

                        @Override
                        public void failure(RetrofitError error) {
                            Toast.makeText(mContext,
                                mContext.getString(R.string.cannot_like), Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }
        });
    }

    private void setComments(final MovieViewHolder holder, final int position) {
        holder.likeAndSaveButtonLayout.setVisibility(View.GONE);
        holder.commentsLayout.setVisibility(View.VISIBLE);
        holder.sendCommentButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (holder.commentsInputEdit.getText().length() > 0) {
                    CommentRepo repo = CommentRepo.getInstance();
                  repo.sendUserComment(AuthStore.getInstance().getAuthToken(), mMovies[position].id,
                        holder.commentsInputEdit.getText().toString(), new Callback<Void>() {
                            @Override
                            public void success(Void aVoid, Response response) {
                                Toast.makeText(mContext, mContext.getString(R.string.thanks_for_your_comment),
                                    Toast.LENGTH_SHORT).show();
                                hideCommentsLayout(holder);
                            }

                            @Override
                            public void failure(RetrofitError error) {
                                Toast.makeText(mContext, mContext.getString(R.string.cannot_add_comment),
                                    Toast.LENGTH_LONG).show();
                            }
                        });
                } else {
                    hideCommentsLayout(holder);
                }
            }
        });
    }

    private void hideCommentsLayout(MovieViewHolder holder) {
        holder.commentsLayout.setVisibility(View.GONE);
        holder.likeAndSaveButtonLayout.setVisibility(View.VISIBLE);
    }

    private void setAddToCollection(final MovieViewHolder holder, int position) {
        final int pos = position;
        if(mMovies[position].isInWatchlist) {
            holder.saveButton
              .setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_check_green, 0, 0, 0);
        }
        final CollectionRepo repo = CollectionRepo.getInstance();
        holder.saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(!mMovies[pos].isInWatchlist) {
                   repo.addMovieToCollection(AuthStore.getInstance().getAuthToken(), 0, mMovies[pos].id, new Callback<MovieCollection[]>() {
                            @Override
                            public void success(MovieCollection[] movieCollections, Response response) {
                                holder.saveButton
                                    .setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_check_green, 0, 0, 0);

                                mMovies[pos].isInWatchlist = true;
                            }

                            @Override
                            public void failure(RetrofitError error) {
                                Toast.makeText(mContext, mContext.getString(R.string.movie_not_added_to_collection),
                                    Toast.LENGTH_LONG).show();
                            }
                        });
                } else {
                 repo.removeMovieFromCollection(AuthStore.getInstance().getAuthToken(), 0,
                        mMovies[pos].id, new Callback<MovieCollection[]>() {
                        @Override
                        public void success(MovieCollection[] movieCollections, Response response) {
                            holder.saveButton
                                .setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_plus, 0, 0, 0);

                            mMovies[pos].isInWatchlist = false;
                        }

                        @Override
                        public void failure(RetrofitError error) {
                            Toast.makeText(mContext,
                                mContext.getString(R.string.cannot_delete_movie_from_watchlist),
                                Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }
        });
    }

    private String getCountOfLikesString(int countOfLikes) {
        String countOfLikesStr;
        if(countOfLikes == 0) {
            countOfLikesStr = "";
        } else if(countOfLikes > 999) {
            countOfLikesStr = " " + (countOfLikes/1000) + "K";
        } else if (countOfLikes > 999999){
            countOfLikesStr = " " + (countOfLikes/1000000) + "M";
        } else {
            countOfLikesStr = " " + String.valueOf(countOfLikes);
        }
        return "<small>" + countOfLikesStr + "</small>";
    }

    private void setTitle(MovieViewHolder holder, final int position) {
        holder.movieTitleTextView.setText(mMovies[position].title);
        holder.movieTitleTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MovieDetailActivity.openView(mContext, mMovies[position].id, true, false);
            }
        });
    }

    private void setIgnoreMovieInfo(MovieViewHolder holder, final int position) {
        holder.ignoreMovie.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MovieRepo repo = MovieRepo.getInstance();
                repo.hideMovie(AuthStore.getInstance().getAuthToken(), mMovies[position].id,
                    new Callback<Void>() {
                        @Override
                        public void success(Void aVoid, Response response) {
                            Movie[] newMovies = new Movie[mMovies.length - 1];
                            for (int i = 0, j = 0; j < mMovies.length; i++, j++) {
                                if (i != position) {
                                    newMovies[i] = mMovies[j];
                                } else {
                                    if (++j < mMovies.length) {
                                        newMovies[i] = mMovies[j];
                                    }
                                }
                            }
                            uiBus.post(new MoviesChangedEvent(newMovies));
                            setMoviesAndNotify(newMovies, true);
                            Toast.makeText(mContext, mContext.getString(R.string.movie_ignored),
                                Toast.LENGTH_SHORT).show();
                        }

                        @Override
                        public void failure(RetrofitError error) {
                            Toast.makeText(mContext, mContext.getString(R.string.movie_ignored_failed),
                                Toast.LENGTH_LONG).show();
                        }
                    });
            }
        });
    }

    private void setMovieInfo(MovieViewHolder holder, int position) {
        String imdp = "IMDB: ";
        String sources = "", date;
        if(mMovies[position].showtimes != null && mMovies[position].showtimes.length > 0) {
            int countOfSources = mMovies[position].showtimes.length;
            for(int i = 0; i < countOfSources; i++) {
                sources += mMovies[position].showtimes[i].name + ", ";
            }
            sources = sources.trim();
            if(sources.charAt(sources.length() - 1) == ',') {
                if(sources.length() > 1) {
                    sources = sources.substring(0, sources.length() - 2);
                } else {
                    sources = "";
                }
            }
        } else {
            sources = "";
        }
        imdp += mMovies[position].imdbRating + " | ";
        if(sources.isEmpty()) {
            date = mMovies[position].releaseYear;
        } else {
            date = mMovies[position].releaseYear + " | ";
        }

        holder.movieInfoTextView.setText(imdp + date + sources);
    }

    private void setPosterAndTrailer(final MovieViewHolder holder, final int position) {
        if (mMovies[position] != null && mMovies[position].posterPath != null
            && !mMovies[position].posterPath.isEmpty()) {
            Picasso.with(mContext)
                .load(mMovies[position].posterPath)
             .error(mContext.getResources().getDrawable(R.drawable.noposter))
                .into(holder.posterImageView);
        } else {
            holder.posterImageView.setImageResource(R.drawable.noposter);
        }
        holder.posterImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MovieDetailActivity.openView(mActivity, mMovies[position].id, false, false);
            }
        });
        if(mMovies[position] != null && mMovies[position].trailerLink  != null
            && !mMovies[position].trailerLink.isEmpty()) {
            holder.playTrailer.setVisibility(View.VISIBLE);
            holder.playTrailer.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    MovieDetailActivity.openView(mActivity, mMovies[position].id, false, true);
                }
            });
        }
    }

    private void setDescription(MovieViewHolder holder, int position) {
        String text = mMovies[position].overview;
        if(text == null || text.isEmpty()) {
       holder.descriptionText.setText(mContext.getString(R.string.no_description));
        } else if(text.length() > 200) {
            text = text.substring(0, 196) + "...";
            holder.descriptionText.setText(text);
        } else {
            holder.descriptionText.setText(text);
        }
        final int pos = position;
        holder.descriptionText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MovieDetailActivity.openView(mActivity, mMovies[pos].id, false, false);
            }
        });
    }

    private void setTags(MovieViewHolder holder, int position) {
        List<String> tags = Arrays.asList(mMovies[position].tags);
        if(tags.size() > 0) {
            CastAndTagsFeedAdapter adapter = new CastAndTagsFeedAdapter(tags,
                mContext, ((FragmentActivity) mActivity).getSupportFragmentManager());
            holder.tags.setItemMargin(10);
            holder.tags.setAdapter(adapter);
        } else {
            holder.tags.setVisibility(View.GONE);
        }
    }

    // class view holder that provide us a link for each element of list
    public static class MovieViewHolder extends RecyclerView.ViewHolder {
        TextView movieTitleTextView, movieInfoTextView, descriptionText, reasonsCountText;
        TextView reasonText1, reasonAuthor1, reasonText2, reasonAuthor2;
        EditText commentsInputEdit;
        Button likeButton, saveButton, playTrailer, sendCommentButton;
        ImageButton ignoreMovie;
        ImageView posterImageView, userPicture1, userPicture2;
        TwoWayView tags;
        RelativeLayout mainReasonsLayout, firstReasonLayout, secondReasonLayout, reasonsListLayout;
        RelativeLayout commentsLayout;
        LinearLayout likeAndSaveButtonLayout;
        ProgressBar progressBar;

        public MovieViewHolder(View view) {
            super(view);
            movieTitleTextView = (TextView)view.findViewById(R.id.movie_title_text);
            movieInfoTextView = (TextView)view.findViewById(R.id.movie_info_text);
            descriptionText = (TextView)view.findViewById(R.id.text_description);
            reasonsCountText = (TextView)view.findViewById(R.id.reason_count);
            reasonText1 = (TextView)view.findViewById(R.id.reason_text_1);
            reasonAuthor1 = (TextView)view.findViewById(R.id.author_1);
            reasonText2 = (TextView)view.findViewById(R.id.reason_text_2);
            reasonAuthor2 = (TextView)view.findViewById(R.id.author_2);
            commentsInputEdit = (EditText)view.findViewById(R.id.comment_input);
            likeButton = (Button)view.findViewById(R.id.like_button);
            saveButton = (Button)view.findViewById(R.id.save_button);
            playTrailer = (Button)view.findViewById(R.id.play_trailer_button);
            sendCommentButton = (Button)view.findViewById(R.id.send_button);
            ignoreMovie = (ImageButton)view.findViewById(R.id.ignore_movie_imagebutton);
            posterImageView = (ImageView)view.findViewById(R.id.poster_image);
            userPicture1 = (ImageView)view.findViewById(R.id.user_picture_1);
            userPicture2 = (ImageView)view.findViewById(R.id.user_picture_2);
            tags = (TwoWayView)view.findViewById(R.id.list_view_feed_tags);
            mainReasonsLayout = (RelativeLayout)view.findViewById(R.id.reasons_main_layout);
            firstReasonLayout = (RelativeLayout)view.findViewById(R.id.first_reason);
            secondReasonLayout = (RelativeLayout)view.findViewById(R.id.second_reason);
            reasonsListLayout = (RelativeLayout)view.findViewById(R.id.reasons_list);
            commentsLayout = (RelativeLayout)view.findViewById(R.id.comments_layout);
            likeAndSaveButtonLayout = (LinearLayout)view
                .findViewById(R.id.like_and_save_buttons_layout);
            progressBar = (ProgressBar)view.findViewById(R.id.centered_progress_bar);
        }
    }
}

Ausnahme:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{42319ed8 position=1 id=-1, oldPos=0, pLpos:0 scrap tmpDetached no parent}
 at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4166)
 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4297)
 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4278)
 at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1947)
 at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:434)
 at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1322)
 at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:556)
 at android.support.v7.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:171)
 at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2627)
 at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2971)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:562)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1626)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
 at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
 at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1626)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
 at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
 at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
 at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
 at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
07-30 12:48:22.688    9590-9590/com.Filmgrail.android.debug W/System.err? at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
 at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
 at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2356)
 at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2069)
 at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)
 at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6630)
 at android.view.Choreographer$CallbackRecord.run(Choreographer.java:803)
 at android.view.Choreographer.doCallbacks(Choreographer.java:603)
 at android.view.Choreographer.doFrame(Choreographer.java:573)
 at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:789)
 at android.os.Handler.handleCallback(Handler.java:733)
 at android.os.Handler.dispatchMessage(Handler.java:95)
 at android.os.Looper.loop(Looper.java:136)
 at android.app.ActivityThread.main(ActivityThread.java:5479)
 at java.lang.reflect.Method.invokeNative(Native Method)
 at java.lang.reflect.Method.invoke(Method.java:515)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
 at dalvik.system.NativeStart.main(Native Method)

Wie kann ich das beheben?


Wenn Sie zurück sind, sind Ihre Daten dieselben wie beim Verlassen der Seite?
Khusrav

Ich habe das gleiche Problem, wie Sie lösen ....
Ashvin Solanki

@ Владимир Hast du die endgültige Antwort gefunden?
Alireza Noorali

In meinem Fall lag es daran, dass ich mit der asynchronen Aufgabe begonnen habe. Wenn eine davon vor der anderen abgeschlossen ist und der Benutzer nach unten scrollt und in der Zwischenzeit ein anderer den Adapter abschließt und aktualisiert, kann der Benutzer eine solche Ausnahme erhalten, da die zweite Aufgabe weniger Datenmenge
zurückgibt

Antworten:


195

Dieses Problem wird durch RecyclerViewDaten verursacht, die in einem anderen Thread geändert wurden. Am besten überprüfen Sie den gesamten Datenzugriff. Und eine Problemumgehung wird abgeschlossen LinearLayoutManager.

Vorherige Antwort

Es gab tatsächlich einen Fehler in RecyclerView und die Unterstützung 23.1.1 wurde immer noch nicht behoben.

Beachten Sie für eine Problemumgehung, dass Backtrace-Stapel Exceptiondiesen Absturz möglicherweise überspringen , wenn wir dies in einer bestimmten Klasse abfangen können. Für mich erstelle ich ein LinearLayoutManagerWrapperund überschreibe das onLayoutChildren:

public class WrapContentLinearLayoutManager extends LinearLayoutManager {
    //... constructor
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
        } catch (IndexOutOfBoundsException e) {
            Log.e("TAG", "meet a IOOBE in RecyclerView");
        }
    }
}

Stellen Sie dann Folgendes ein RecyclerView:

RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);

recyclerView.setLayoutManager(new WrapContentLinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false));

Fangen Sie tatsächlich diese Ausnahme und scheint noch keine Nebenwirkung.

Auch wenn Sie einen Wrapper dafür verwenden GridLayoutManageroder StaggeredGridLayoutManagererstellen müssen.

Hinweis: RecyclerViewMöglicherweise befindet sich das Gerät in einem falschen internen Zustand.


1
wo genau legst du das hin auf Adapter oder Aktivität?
Steve Kamau

verlängern Sie a LinearLayoutManagerund überschreiben Sie dies. Ich werde eine Ergänzung in meiner Antwort.
SakiM

14
code.google.com/p/android/issues/detail?id=158046 Antwort Nr. 12 sagte, tu das nicht.
Robert

ähm, du hast recht. Es scheint schwierig zu sein, alle potenziellen Nicht-UI-Thread-Änderungen in meiner App zu entschärfen. Ich werde dies nur als Problemumgehung beibehalten.
SakiM

1
Für meinen Fall mache ich auf dem gleichen Thread. mDataHolder.get (). removeAll (mHiddenGenre); mAdapter.notifyItemRangeRemoved (mExpandButtonPosition, mHiddenGenre.size ());
JehandadK

73

Dies ist ein Beispiel für das Aktualisieren von Daten mit völlig neuen Inhalten. Sie können es leicht an Ihre Bedürfnisse anpassen. Ich habe dies in meinem Fall gelöst, indem ich angerufen habe:

notifyItemRangeRemoved(0, previousContentSize);

Vor:

notifyItemRangeInserted(0, newContentSize);

Dies ist die richtige Lösung und wird in diesem Beitrag auch von einem AOSP-Projektmitglied erwähnt.


2
Diese Lösung funktioniert für mich Ich habe viele Antworten hier ausprobiert, aber sie funktionieren nicht (ich habe die erste Lösung nicht getestet)
AndroLife

Das Problem besteht darin, dass diese Methoden diese Inkonsistenz erzeugen, selbst wenn sie im selben Thread ausgeführt werden.
JehandadK

Ich benutze notifyItemRangeInsertedund habe dieses Problem nicht mit einigen Samsung-Geräten
Benutzer25

Und hier ziemlich unangebracht. Autor nicht verwendetnotifyItemRangeInserted
user25

1
Danke dir! Das hat mir geholfen.
DmitryKanunnikoff

35

Ich war einmal mit diesem Problem konfrontiert und habe es gelöst, indem ich die LayoutManagerprädiktiven Animationen verpackt und deaktiviert habe.

Hier ein Beispiel:

public class LinearLayoutManagerWrapper extends LinearLayoutManager {

  public LinearLayoutManagerWrapper(Context context) {
    super(context);
  }

  public LinearLayoutManagerWrapper(Context context, int orientation, boolean reverseLayout) {
    super(context, orientation, reverseLayout);
  }

  public LinearLayoutManagerWrapper(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }

  @Override
  public boolean supportsPredictiveItemAnimations() {
    return false;
  }
}

Und setzen Sie es auf RecyclerView:

RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManagerWrapper(context, LinearLayoutManager.VERTICAL, false);

Das scheint für mich zu funktionieren, aber können Sie sagen, warum das funktioniert?
Dennis Anderson

Auch für mich behoben. Wie Sie vorausgesagt haben, dass dies die Ursache für diesen Absturz sein könnte.
Rahul Rastogi

1
Die Basisklasse der LinearLayoutManager-Methode unterstütztPredictiveAnimations () und gibt standardmäßig false zurück. Was bekommen wir, wenn wir die Methode hier überschreiben? public boolean supportsPredictiveItemAnimations() { return false; }
M. Hig

1
@ M.Hig Die Dokumentation für LinearLayoutManagersagt, dass der Standardwert falsch ist, aber diese Anweisung ist falsch :-( Der dekompilierte Code für LinearLayoutManagerhat Folgendes : public boolean supportPredictiveItemAnimations () {return this.mPendingSavedState == null && this.mLastStackFromEnd == this.mStackFromEnd ;}
Clyde

Ich verwende diff utils zum Aktualisieren meines Recycler-Ansichtsadapters und diese Antwort hat einen Absturz behoben. Vielen Dank, lieber Autor!
Eugene P.

29

Neue Antwort: Verwenden Sie DiffUtil für alle RecyclerView-Updates. Dies hilft sowohl bei der Leistung als auch bei dem oben genannten Fehler. Siehe hier

Vorherige Antwort: Das hat bei mir funktioniert. Der Schlüssel ist, notifyDataSetChanged()die richtigen Dinge nicht in der richtigen Reihenfolge zu verwenden und zu tun:

public void setItems(ArrayList<Article> newArticles) {
    //get the current items
    int currentSize = articles.size();
    //remove the current items
    articles.clear();
    //add all the new items
    articles.addAll(newArticles);
    //tell the recycler view that all the old items are gone
    notifyItemRangeRemoved(0, currentSize);
    //tell the recycler view how many new items we added
    notifyItemRangeInserted(0, newArticles.size());
}

1
Dies ist die gründlichste Lösung mit einer guten Erklärung. Vielen Dank!
Sakiboy

Was ist dann der Zweck der Verwendung von notifyitemrangeinserted anstelle von notifydatasetchanged (), @Bolling.
Ankur_009

@FilipLuch Kannst du erklären warum?
Sreekanth Karumanaghat

3
@SreekanthKarumanaghat sicher, keine Ahnung, warum ich den Grund nicht erklärt habe. Grundsätzlich löscht er dann alle Elemente in der Liste neu. Wie in den Suchergebnissen erhalten Sie sehr oft dieselben Elemente, oder wenn die Aktualisierung abgeschlossen ist, erhalten Sie dieselben Elemente, und am Ende wird alles neu erstellt, was eine Verschwendung von Leistung darstellt. Verwenden Sie stattdessen DiffUtils und aktualisieren Sie nur die Änderungen und nicht alle Elemente. Es ist, als würde man jedes Mal von A nach Z gehen, aber man hat dort nur das F geändert.
Filip Luchianenco

2
DiffUtil ist ein versteckter Schatz. Danke für das Teilen!
Sileria

22

Gründe für dieses Problem:

  1. Ein internes Problem in Recycler, wenn Elementanimationen aktiviert sind
  2. Änderung an Recycler-Daten in einem anderen Thread
  3. Aufrufen von Benachrichtigungsmethoden auf falsche Weise

LÖSUNG:

----------------- LÖSUNG 1 ---------------

  • Abfangen der Ausnahme (Nicht empfohlen, insbesondere aus Grund Nr. 3)

Erstellen Sie einen benutzerdefinierten LinearLayoutManager wie folgt und setzen Sie ihn auf ReyclerView

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

            @Override
            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {

                try {

                    super.onLayoutChildren(recycler, state);

                } catch (IndexOutOfBoundsException e) {

                    Log.e(TAG, "Inconsistency detected");
                }

            }
        }

Stellen Sie dann RecyclerVIew Layout Manager wie folgt ein:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- LÖSUNG 2 ---------------

  • Deaktivieren Sie Elementanimationen (behebt das Problem, wenn de Grund 1 hat):

Erstellen Sie erneut einen benutzerdefinierten linearen Layout-Manager wie folgt:

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

             @Override
             public boolean supportsPredictiveItemAnimations() {
                 return false;
             }
        }

Stellen Sie dann RecyclerVIew Layout Manager wie folgt ein:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- LÖSUNG 3 ---------------

  • Diese Lösung behebt das Problem, wenn es durch Grund 3 verursacht wird. Sie müssen sicherstellen, dass Sie die Benachrichtigungsmethoden ordnungsgemäß verwenden. Alternativ können Sie DiffUtil verwenden, um die Änderung auf intelligente, einfache und reibungslose Weise zu handhaben. Verwenden von DiffUtil in Android RecyclerView

----------------- LÖSUNG 4 ---------------

  • Aus Grund Nr. 2 müssen Sie den gesamten Datenzugriff auf die Recycler-Liste überprüfen und sicherstellen, dass an einem anderen Thread keine Änderungen vorgenommen wurden.

Dies hat in meinem Szenario funktioniert. Ich kann DiffUtil nicht verwenden, da ich benutzerdefinierte Komponenten für Recycler und Adapter habe und der Fehler genau in bestimmten bekannten Szenarien auftritt. Ich musste ihn nur patchen, ohne auf Elementanimatoren zurückgreifen zu müssen wickelte es in ein try & catch
RJFares

17

Ich hatte ein ähnliches Problem.

Problem im Fehlercode unten:

int prevSize = messageListHistory.size();
// some insert
adapter.notifyItemRangeInserted(prevSize - 1, messageListHistory.size() -1);

Lösung:

int prevSize = messageListHistory.size();
// some insert
adapter.notifyItemRangeInserted(prevSize, messageListHistory.size() -prevSize);

Das hat bei mir super geklappt! Ich bin mir nicht sicher, warum wir es nicht einfach verwenden können newList.size() - 1.
Waseefakhtar

15

Laut diesem Problem wurde das Problem behoben und wahrscheinlich Anfang 2015 veröffentlicht. Ein Zitat aus demselben Thread :

Es bezieht sich speziell auf den Aufruf von notifyDataSetChanged. [...]

Übrigens rate ich dringend, notifyDataSetChanged nicht zu verwenden, da dadurch Animationen und Leistung beeinträchtigt werden. Auch in diesem Fall kann das Problem durch die Verwendung bestimmter Benachrichtigungsereignisse behoben werden.

Wenn Sie immer noch Probleme mit einer aktuellen Version der Support-Bibliothek haben, würde ich empfehlen, Ihre Anrufe notifyXXX(insbesondere Ihre Verwendung von notifyDataSetChanged) in Ihrem Adapter zu überprüfen , um sicherzustellen, dass Sie den (etwas heiklen / undurchsichtigen) RecyclerView.AdapterVertrag einhalten . Stellen Sie außerdem sicher, dass diese Benachrichtigungen im Hauptthread ausgegeben werden.


16
Nicht wirklich, ich stimme Ihrem Teil bezüglich der Leistung zu, aber notifyDataSetChanged () beendet keine Animationen, um mithilfe von notifyDataSetChanged () zu animieren. a) Rufen Sie setHasStableIds (true) für Ihr RecyclerView.Adapter-Objekt auf und b) überschreiben Sie getItemId in Ihrem Adapter, um a zurückzugeben einzigartiger langer Wert für jede Zeile und
probieren

@PirateApp Sie sollten in Betracht ziehen, Ihren Kommentar als Antwort abzugeben. Ich habe es versucht und es funktioniert gut.
Mr5

Nicht wahr! Sie erhalten weiterhin Berichte von Google Console zu diesem Problem. Und Gerät ist natürlich Samsung -Samsung Galaxy J3(2017) (j3y17lte), Android 8.0
user25

10

Ich hatte das gleiche Problem. Dies wurde verursacht, weil ich die Benachrichtigung des Adapters über das Einfügen von Elementen verzögert habe.

Es wurde jedoch ViewHolderversucht, einige Daten in der Ansicht neu zu zeichnen, und die RecyclerViewMessung und Nachzählung der Anzahl der Kinder wurde gestartet. In diesem Moment stürzte sie ab (die Artikelliste und ihre Größe wurden bereits aktualisiert, der Adapter wurde jedoch noch nicht benachrichtigt).


8

Dies geschieht, wenn Sie die falsche Position für notifyItemChanged, notifyItemRangeInserted usw. angeben. Für mich:

Vorher: (fehlerhaft)

public void addData(List<ChannelItem> list) {
  int initialSize = list.size();
  mChannelItemList.addAll(list);
  notifyItemRangeChanged(initialSize - 1, mChannelItemList.size());
 } 

Nachher: ​​(Richtig)

 public void addData(List<ChannelItem> list) {
  int initialSize = mChannelItemList.size();
  mChannelItemList.addAll(list);
  notifyItemRangeInserted(initialSize, mChannelItemList.size()-1); //Correct position 
 }

1
Warum notifyItemRangeInserted(initialSize, mChannelItemList.size()-1);und nicht notifyItemRangeInserted(initialSize, list.size());?
CoolMind

Unverstanden. Sie verwechselt initialSizeund listGröße. Beide Varianten sind also falsch.
CoolMind

Für mich funktioniert es mit, notifyItemRangeInserted(initialSize, list.size()-1);aber ich verstehe es nicht. Warum muss ich die eingefügte Größe für itemCount um eins reduzieren?
Plexus

7

Ein weiterer Grund für dieses Problem ist, dass Sie diese Methoden mit falschen Indizes aufrufen (Indizes, bei denen es NICHT vorgekommen ist, fügen sie ein oder entfernen sie).

-notifyItemRangeRemoved

-notifyItemRemoved

-notifyItemRangeInserted

-notifyItemInserted

Überprüfen Sie die Indexparameter dieser Methoden und stellen Sie sicher, dass sie präzise und korrekt sind.


2
Das war mein Problem. Eine Ausnahme tritt auf, wenn nichts zur Liste hinzugefügt wird.
Rasel

6

Dieser Fehler ist in 23.1.1 immer noch nicht behoben, aber eine häufige Problemumgehung besteht darin, die Ausnahme abzufangen.


18
Fang es genau wo? Der einzige Code in der Stapelverfolgung ist nativer Android-Code.
Howettl

1
Fangen Sie es genau wie @saki_M Antwort.
Renan Bandeira

Funktioniert das tatsächlich für Sie, obwohl Renan? Haben Sie das Update eine Weile getestet? Der Fehler tritt nur gelegentlich auf, daher werde ich nur sehen, ob dies im Laufe der Zeit funktioniert.
Simon

Dies funktioniert tatsächlich, aber einige untergeordnete Ansichten in meinen bleiben inkonsistent.
david

@ David bleibt uneinheitlich, was ist die Folge davon?
Sreekanth Karumanaghat

4

Dieses Problem wird durch RecyclerView-Daten verursacht, die in einem anderen Thread geändert wurden

Kann das Threading als ein Problem bestätigen und da ich auf das Problem gestoßen bin und RxJava immer beliebter wird: Stellen Sie sicher, dass Sie es verwenden, .observeOn(AndroidSchedulers.mainThread())wenn Sie anrufennotify[whatever changed]

Codebeispiel vom Adapter:

myAuxDataStructure.getChangeObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<AuxDataStructure>() {

    [...]

    @Override
    public void onNext(AuxDataStructure o) {
        [notify here]
    }
});

Ich bin im Haupt-Thread, während ich DiffUtil.calculateDiff (diffUtilForecastItemChangesAnlayser (this.mWeatherForecatsItemWithMainAndWeathers, weatherForecastItems)) aufrufe. DispatchUpdatesTo (this); Das Protokoll ist klar auf Thread: Thread [main, 5, main]
Mathias Seguy Android2ee

4

In meinem Fall stürzte es jedes Mal ab, wenn ich notifyItemRemoved (0) aufrufte. Es stellte sich heraus, dass ich eingestellt setHasStableIds(true)und getItemIdgerade die Artikelposition zurückgegeben habe. Am Ende habe ich es aktualisiert, um die hashCode()oder die selbst definierte eindeutige ID des Elements zurückzugeben , wodurch das Problem behoben wurde.


4

In meinem Fall trat dieses Problem auf, weil Datenaktualisierungen vom Server abgerufen wurden (ich verwende Firebase Firestore), und während der erste Datensatz von DiffUtil im Hintergrund verarbeitet wird, kommt ein weiterer Satz von Datenaktualisierungen und verursacht ein Parallelitätsproblem durch Starten eines anderen DiffUtil.

Kurz gesagt, wenn Sie DiffUtil in einem Hintergrundthread verwenden, der dann zum Hauptthread zurückkehrt, um die Ergebnisse an RecylerView zu senden, besteht die Möglichkeit, dass dieser Fehler auftritt, wenn mehrere Datenaktualisierungen in kurzer Zeit erfolgen.

Ich habe dieses Problem gelöst, indem ich den Ratschlägen in dieser wunderbaren Erklärung gefolgt bin: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2

Um die Lösung zu erklären, müssen Sie die Updates pushen, während das aktuelle auf einem Deque ausgeführt wird. Die Deque kann dann die ausstehenden Aktualisierungen ausführen, sobald die aktuelle abgeschlossen ist, wodurch alle nachfolgenden Aktualisierungen verarbeitet werden, aber auch Inkonsistenzfehler vermieden werden!

Hoffe das hilft, denn dieser hat mich am Kopf kratzen lassen!


Danke für den Link!
CoolMind

3

Problem trat bei mir nur auf, wenn:

Ich habe den Adapter mit einer leeren Liste erstellt . Dann habe ich Artikel eingefügt und angerufen notifyItemRangeInserted.

Lösung:

Ich habe dieses Problem gelöst, indem ich den Adapter erst erstellt habe, nachdem ich den ersten Datenblock habe und ihn sofort damit initialisiert habe. Der nächste Block konnte dann problemlos eingefügt und notifyItemRangeInsertedaufgerufen werden.


Ich denke nicht, dass es der Grund ist. Ich habe viele Adapter mit leeren Listen, dann Elemente hinzugefügt mit notifyItemRangeInserted, hatte aber dort nie diese Ausnahme.
CoolMind

3

Mein Problem war, dass ich, obwohl ich beide Array-Listen mit dem Datenmodell für die Recycler-Ansicht löschte, den Adapter nicht über diese Änderung benachrichtigte, sodass veraltete Daten aus dem vorherigen Modell vorhanden waren. Was die Verwirrung über die Position des Sichthalters verursachte. Um dies zu beheben, benachrichtigen Sie den Adapter immer, dass der Datensatz geändert wurde, bevor Sie ihn erneut aktualisieren.


oder einfach benachrichtigen, wenn das Element stattdessen entfernt wurde
Remario

Mein Modell verwendet die Referenz des Containers, weshalb
Remario

3

In meinem Fall habe ich die Daten zuvor in einem Thread mit mRecyclerView.post (new Runnable ...) geändert und später die Daten im UI-Thread erneut geändert, was zu Inkonsistenzen führte.


1
Ich habe die gleiche Situation wie Sie. Wie haben Sie das gelöst? danke
baderkhane

2

Der Fehler kann dadurch verursacht werden, dass Ihre Änderungen nicht mit dem übereinstimmen, was Sie benachrichtigen. In meinem Fall:

myList.set(position, newItem);
notifyItemInserted(position);

Was ich natürlich tun musste:

myList.add(position, newItem);
notifyItemInserted(position);

2

In meinem Fall bestand das Problem darin, dass ich notifyDataSetChanged verwendet habe, wenn die Menge der neu geladenen Daten geringer war als die ursprünglichen Daten. Dieser Ansatz hat mir geholfen:

adapter.notifyItemRangeChanged(0, newAmountOfData + 1);
adapter.notifyItemRangeRemoved(newAmountOfData + 1, previousAmountOfData);

Warum notifyDataSetChangedhängt es von neuen Daten ab? Ich dachte, es würde die ganze Liste aktualisieren.
CoolMind

2

Ich bin auf das gleiche Problem gestoßen.

Meine App verwendet Navigationskomponenten mit einem Fragment, das meine recyclerView enthält. Meine Liste wurde beim ersten Laden des Fragments gut angezeigt ... aber beim Navigieren und Zurückkommen trat dieser Fehler auf.

Beim Wegnavigieren durchlief der Fragmentlebenszyklus nur onDestroyView und startete bei der Rückkehr bei onCreateView. Mein Adapter wurde jedoch in onCreate des Fragments initialisiert und bei der Rückkehr nicht neu initialisiert.

Das Update bestand darin, den Adapter in onCreateView zu initialisieren.

Hoffe das kann jemandem helfen.


0

Ich habe diesen Fehler erhalten, weil ich fälschlicherweise eine Methode aufgerufen habe, um eine bestimmte Zeile mehrmals aus meiner Recycling-Ansicht zu entfernen. Ich hatte eine Methode wie:

void removeFriends() {
    final int loc = data.indexOf(friendsView);
    data.remove(friendsView);
    notifyItemRemoved(loc);
}

Ich habe diese Methode versehentlich dreimal statt einmal aufgerufen, daher war das zweite Mal loc-1 und der Fehler wurde angegeben, als versucht wurde, sie zu entfernen. Die beiden Korrekturen sollten sicherstellen, dass die Methode nur einmal aufgerufen wurde, und auch eine Überprüfung der Integrität wie folgt hinzufügen:

void removeFriends() {
    final int loc = data.indexOf(friendsView);
    if (loc > -1) {
        data.remove(friendsView);
        notifyItemRemoved(loc);
    }
}

0

Ich habe das gleiche Problem und habe gelesen, dass dies nur bei Samsung-Handys passiert ist ... Aber die Realität hat gezeigt, dass dies bei vielen Marken passiert.

Nach dem Testen wurde mir klar, dass dies nur passiert, wenn Sie schnell durch die RecyclerView scrollen und dann entweder mit der Zurück-Taste oder der Aufwärts-Taste zurückgehen. Also habe ich den Up-Button hineingesteckt und onBackpressed das folgende Snippet:

someList = new ArrayList<>();
mainRecyclerViewAdapter = new MainRecyclerViewAdapter(this, someList, this);
recyclerViewMain.setAdapter(mainRecyclerViewAdapter);
finish();

Mit dieser Lösung laden Sie einfach eine neue Arraylist in den Adapter und einen neuen Adapter in recyclerView und beenden dann die Aktivität.

Hoffe es hilft jemandem


0

Ich habe diesen Fehler erhalten, weil ich versehentlich zweimal "notifyItemInserted" aufgerufen habe.


0

In meinem Fall hatte ich mehr als 5000 Artikel in der Liste. Mein Problem war, dass beim Scrollen in der Recycler-Ansicht manchmal der "onBindViewHolder" aufgerufen wird, während die "myCustomAddItems" -Methode die Liste ändert.

Meine Lösung bestand darin, allen Methoden, die die Datenliste ändern, "synchronized (syncObject) {}" hinzuzufügen. Auf diese Weise kann zu jedem Zeitpunkt nur eine Methode diese Liste lesen.


0

In meinem Fall haben sich die Adapterdaten geändert. Und ich habe fälschlicherweise notifyItemInserted () für diese Änderungen verwendet. Wenn ich notifyItemChanged verwende, ist der Fehler verschwunden.


0

Ich bin auf dasselbe Problem gestoßen, als ich Elemente in der Liste entfernt und aktualisiert habe ... Nach Tagen der Untersuchung denke ich, dass ich endlich eine Lösung gefunden habe.

Was Sie tun müssen, ist zuerst die gesamte notifyItemChangedListe und erst dann alle notifyItemRemoved in absteigender Reihenfolge

Ich hoffe, dies wird Menschen helfen, die auf dasselbe Problem stoßen ...


0

Ich verwende einen Cursor, daher kann ich die DiffUtils nicht wie in den gängigen Antworten vorgeschlagen verwenden. Damit es für mich funktioniert, deaktiviere ich Animationen, wenn die Liste nicht inaktiv ist. Dies ist die Erweiterung, die dieses Problem behebt:

 fun RecyclerView.executeSafely(func : () -> Unit) {
        if (scrollState != RecyclerView.SCROLL_STATE_IDLE) {
            val animator = itemAnimator
            itemAnimator = null
            func()
            itemAnimator = animator
        } else {
            func()
        }
    }

Dann können Sie Ihren Adapter so aktualisieren

list.executeSafely {
  adapter.updateICursor(newCursor)
}

0

Wenn das Problem nach Multi-Touch auftritt, können Sie Multi-Touch mit deaktivieren

android:splitMotionEvents="false" 

in der Layoutdatei.


-1

Wenn sich Ihre Daten stark ändern, können Sie verwenden

 mAdapter.notifyItemRangeChanged(0, yourData.size());

oder einige einzelne Elemente in Ihrem Datensatz können geändert werden

 mAdapter.notifyItemChanged(pos);

Für eine detaillierte Verwendung der Methoden können Sie auf das Dokument verweisen, indem Sie versuchen, es nicht direkt zu verwenden mAdapter.notifyDataSetChanged().


2
Die Verwendung notifyItemRangeChangederzeugt auch den gleichen Absturz.
Lionelmessi

Das passt zu einer Situation. Möglicherweise haben Sie Ihr Dataset sowohl im Hintergrundthread als auch im UI-Thread aktualisiert. Dies führt auch zu Inkonsistenzen. Wenn Sie das Dataset nur im UI-Thread aktualisieren, funktioniert es.
Arron Cao
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.