我具有以下自定义onBindViewHolder方法-
@Override
protected void onBindViewHolder(@NonNull InstantVideoViewHolder holder, int position, @NonNull Video model) {
boolean isWinner = checkIsWinner(model);
holder.populate(model, this.getRef(position).getKey(), isWinner);
Timber.d("onBindViewHolder: %s", holder.getTag());
// resume active if needed
if (position == getActivePos()) {
markActiveVH(holder);
}
}
在holder.populate()方法中的是我实例化所有Video模型变量的地方,包括回调对象。我的视图持有人类实现了回调接口及其方法,因此在视图持有人内部,我通过应用this
设置了侦听器。
这是我的populate()方法-
public void populate(Video video, String videoUid, boolean isWinner) {
// load models
mVideo = video;
mVideoKey = videoUid;
mContestKey = video.getContestId();
mPresenter.setArgs(videoUid, true, true);
setupDetailsView();
// init viewcounts
mViewStatsFinder = new DBViewStatsFinder(mContestKey, mVideoKey);
mViewStatsFinder.setListener(viewCountsListener);
mViewStatsFinder.findViewsCount();
// info of owner
ImageUtil.loadImageUrl(video.getProfile().getPhotoUrl(), imageViewOwner);
tvOwner.setText(video.getProfile().getName());
// tvPublishedDate.setText(video.getFormattedPublishDate());
// tvContestInfo.setText(getFormattedContest(video));
// video title
mSongNameTv.setText(mVideo.getSongName());
// contest number
mContestNumber.setText( mVideo.getFormattedContestTitle(getContext()).replace("1MD Indian Talent ", ""));
// winner
tvOwner.setTextColor(ContextCompat.getColor(getContext(), isWinner ? R.color.gold_win : R.color.black));
// tvPublishedDate.setTextColor(ContextCompat.getColor(getContext(), isWinner ? R.color.gold_win : R.color.black));
imWinner.setVisibility(isWinner ? View.VISIBLE : View.GONE);
// download
mImageCover.setVisibility(View.VISIBLE);
ImageUtil.loadFirebaseImageUri(video.getThumbUri(), mImageCover);
loadMediaSource();
// subscribe if ready
initDBEvents();
subscribe();
toggleCommentsLoadProgressbar(true);
toggleVotesLoadProgressbar(true);
toggleViewCountsProgressbar(true);
}
我的问题是,对于某些用户,当Internet连接速度慢时,他们在向下滚动列表时会崩溃-单击该视图,然后向服务器发送回调,这可能需要一些时间。在那个时候,用户可能会向下滚动列表,当结果返回时,它变成一个空对象,因为相关的列表项已经被回收,这意味着它现在为空。
我当时正在考虑将整个holder.populate方法移至onCreateViewHolder,但问题是populate()方法需要一些无法在onCreateView()上使用的参数。
任何想法都会很有帮助。
编辑-
这是我的堆栈跟踪-
Fatal Exception: java.lang.NullPointerException
Attempt to invoke interface method 'void com.onemdtalent.app.presenters.sharelanding.ShareLandingPresenter$IVideoLikeSuccess.onLikeSuccess()' on a null object reference
com.onemdtalent.app.presenters.sharelanding.ShareLandingPresenter.completeUpdateVote (ShareLandingPresenter.java:455)
com.onemdtalent.app.presenters.sharelanding.ShareLandingPresenter.lambda$processVoteVideo$4 (ShareLandingPresenter.java:449)
com.onemdtalent.app.presenters.sharelanding.-$$Lambda$ShareLandingPresenter$F7TrCfo8uo_HGe9BlaEqncdfi1w.onComplete (Unknown Source:2)
com.google.android.gms.tasks.zzj.run (Unknown Source:4)
android.os.Handler.handleCallback (Handler.java:873)
android.os.Handler.dispatchMessage (Handler.java:99)
android.os.Looper.loop (Looper.java:193)
android.app.ActivityThread.main (ActivityThread.java:6692)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:858)
这是跟踪的相应代码-
private void processVoteVideo(boolean liked) {
// check vote to see if user has already voted. if so, check to see if user has liked. if liked - return. else - add like. if user has not voted, the like is processed as a vote. *orit*
DataSnapshot snapshot = getExistVote();
if (snapshot != null) {
Vote vote = snapshot.getValue(Vote.class);
if (vote != null) {
// close if already matched
if (vote.isLiked() == liked) {
getView().close();
//toggle like - if liked to unlike and vice versa
} else {
snapshot.getRef().setValue(vote, (DatabaseError databaseError, DatabaseReference databaseReference) -> {
completeUpdateVote(databaseError == null);
});
}
completeUpdateVote(false);
return;
}
}
VotesDBHelper.addVote(getUid(), mProfile, mVideo.getContestId(), mVideo.getContestStageKey(), mVideoKey, liked).addOnCompleteListener(task -> { //todo task was once called multiple times!! check why.
if (task.isSuccessful()) {
Timber.v("Success likeVideo");
} else {
Timber.w(task.getException(), "Error processLikeVideo");
}
completeUpdateVote(task.isSuccessful());
});
}
private void completeUpdateVote(boolean successful) {
if (successful) {
mIVideoLikeSuccessListener.onLikeSuccess(); // --> here is the crash, the listener is null when receving the callback from the server because the list item has been recycled faster than the callback arriving.
} else {
mIVideoLikeSuccessListener.onLikeFail();
}
}
以下代码是我的演示者^。
答案 0 :(得分:0)
我不会为您编写整个代码,但我将向您展示一种更好的方法(imo):
想法是将api调用移出RecyclerView或其适配器。视图模型或演示者应对此负责。所以:
首先将一个scroll listener添加到包含recyclerview的视图模型(或“活动”)中:
recyclerView.addOnScrollListener(...);
在滚动侦听器中,接下来通过方法onScrollStateChanged
检查newState
参数是否等于RecyclerView.SCROLL_STATE_IDLE
如果是,那么您需要通过调用LinearLayoutManager.findFirstCompletelyVisibleItemPosition()
现在您有了可见项目的索引,您可以从列表中的项目中获取所需的信息并加载视频
注意:您需要按照我在第2步中提到的方法检查RecyclerView.SCROLL_STATE_DRAGGING
并分别取消对api的调用。
答案 1 :(得分:0)
我找到了所需的解决方案。
我将relevent API回调的实例传递给ViewHolder的构造函数,然后仅在onViewCreated中创建它,所以这就是我想要的。
感谢大家的帮助。