一. assertNotInLayoutOrScroll引起的IllegalStateException
通過(guò)調(diào)用 notifyxxx 對(duì)適配器進(jìn)行更新時(shí),會(huì)調(diào)用到RecyclerView的assertNotInLayoutOrScroll,該方法可能會(huì)拋出IllegalStateException
異常拋出點(diǎn):
void assertNotInLayoutOrScroll(String message) {
if (this.isComputingLayout()) {
if (message == null) {
throw new IllegalStateException("Cannot call this method while RecyclerView is computing a layout or scrolling" + this.exceptionLabel());
} else {
throw new IllegalStateException(message);
}
} else {
if (this.mDispatchScrollCounter > 0) {
Log.w("RecyclerView", "Cannot call this method in a scroll callback. Scroll callbacks mightbe run during a measure & layout pass where you cannot change theRecyclerView data. Any method call that might change the structureof the RecyclerView or the adapter contents should be postponed tothe next frame.", new IllegalStateException("" + this.exceptionLabel()));
}
}
}
問(wèn)題:
1. Cannot call this method while RecyclerView is computing a layout or scrolling
該問(wèn)題直接原因是RecyclerView的isComputingLayout()為true導(dǎo)致。
字面意思就是不要在RecyclerView正在layout的時(shí)候,去嘗試更新適配器內(nèi)容。
解決方法:
public final void notifyDataSetChangedSafely() {
if (mRecyclerView != null && mRecyclerView.isComputingLayout()) {
mRecyclerView.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
} else {
notifyDataSetChanged();
}
}
即在調(diào)用notifyDataSetChanged等方法時(shí),判斷RecyclerView是否在ComputingLayout,如果是的話,在下一條消息中notifyDataSetChanged等方法。
2. Cannot call this method in a scroll callback
該問(wèn)題直接原因是RecyclerView的mDispatchScrollCounter>0導(dǎo)致。
字面意思就是不要在RecyclerView執(zhí)行Scroll動(dòng)作的回調(diào)方法的時(shí)候去嘗試直接更新適配器內(nèi)容。
其值變化所在位置:
void dispatchOnScrolled(int hresult, int vresult) {
++this.mDispatchScrollCounter;
int scrollX = this.getScrollX();
int scrollY = this.getScrollY();
this.onScrollChanged(scrollX, scrollY, scrollX, scrollY);
this.onScrolled(hresult, vresult);
if (this.mScrollListener != null) {
this.mScrollListener.onScrolled(this, hresult, vresult);
}
if (this.mScrollListeners != null) {
for(int i = this.mScrollListeners.size() - 1; i >= 0; --i) {
((RecyclerView.OnScrollListener)this.mScrollListeners.get(i)).onScrolled(this, hresult, vresult);
}
}
--this.mDispatchScrollCounter;
}
代碼一目了然,即不要在RecyclerView回調(diào)onScrollChanged,onScrolled的時(shí)候去嘗試直接更新適配器內(nèi)容,
也不要在RecyclerView的OnScrollListener回調(diào)onScrolled的時(shí)候去嘗試直接更新適配器內(nèi)容。
比如我們通常會(huì)在RecyclerView滑動(dòng)到底部的時(shí)候去加載更多數(shù)據(jù)來(lái)填充列表:
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(final RecyclerView recyclerView, int dx, int dy) {
if (isLastViewMoreView(recyclerView)) {
onLoadMore();
}
}
});
分兩種情況:
a. 我們的onLoadMore的數(shù)據(jù)來(lái)源是網(wǎng)絡(luò),因?yàn)榫W(wǎng)絡(luò)請(qǐng)求是異步返回,就不會(huì)出現(xiàn)問(wèn)題。
b. 我們的onLoadMore的數(shù)據(jù)來(lái)源是本地,即請(qǐng)求可能是同步返回,這個(gè)時(shí)候如果直接更新適配器,那么這個(gè)更新動(dòng)作就是在onScrolled方法的執(zhí)行序列中進(jìn)行了,這就會(huì)導(dǎo)致call this method in a scroll callback
解決方法:
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(final RecyclerView recyclerView, int dx, int dy) {
if (isLastViewMoreView(recyclerView)) {
new Handler().post(new Runnable() {
@Override
public void run() {
onLoadMore();
}
});
}
}
});
即在調(diào)用onScrolled方法時(shí),在下一條消息中去調(diào)用onLoadMore方法(更新適配器動(dòng)作在onScrolled等方法的執(zhí)行序列中的方法)