區域性重新整理架構程式碼分析
阿新 • • 發佈:2021-02-02
在 頁面區域性重新整理實現中,我們說到了,區域性更新可以使用ArrayObjectAdapter類 setItems()方法。
本文主要分析ArrayObjectAdapter類 setItems()方法。程式碼如下所示:
public void setItems(final List itemList, final DiffCallback callback) {
if (callback == null) {
this.mItems.clear();
this.mItems. addAll(itemList);
this.notifyChanged();
} else {
this.mOldItems.clear();
this.mOldItems.addAll(this.mItems);
DiffResult diffResult = DiffUtil.calculateDiff(new Callback() {
public int getOldListSize() {
return ArrayObjectAdapter.this.mOldItems.size();
}
public int getNewListSize() {
return itemList.size();
}
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return callback.areItemsTheSame (ArrayObjectAdapter.this.mOldItems.get(oldItemPosition), itemList.get(newItemPosition));
}
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return callback.areContentsTheSame(ArrayObjectAdapter.this.mOldItems.get(oldItemPosition), itemList.get(newItemPosition));
}
@Nullable
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return callback.getChangePayload(ArrayObjectAdapter.this.mOldItems.get(oldItemPosition), itemList.get(newItemPosition));
}
});
this.mItems.clear();
this.mItems.addAll(itemList);
if (this.mListUpdateCallback == null) {
this.mListUpdateCallback = new ListUpdateCallback() {
public void onInserted(int position, int count) {
if (ArrayObjectAdapter.DEBUG) {
Log.d("ArrayObjectAdapter", "onInserted");
}
ArrayObjectAdapter.this.notifyItemRangeInserted(position, count);
}
public void onRemoved(int position, int count) {
if (ArrayObjectAdapter.DEBUG) {
Log.d("ArrayObjectAdapter", "onRemoved");
}
ArrayObjectAdapter.this.notifyItemRangeRemoved(position, count);
}
public void onMoved(int fromPosition, int toPosition) {
if (ArrayObjectAdapter.DEBUG) {
Log.d("ArrayObjectAdapter", "onMoved");
}
ArrayObjectAdapter.this.notifyItemMoved(fromPosition, toPosition);
}
public void onChanged(int position, int count, Object payload) {
if (ArrayObjectAdapter.DEBUG) {
Log.d("ArrayObjectAdapter", "onChanged");
}
ArrayObjectAdapter.this.notifyItemRangeChanged(position, count, payload);
}
};
}
diffResult.dispatchUpdatesTo(this.mListUpdateCallback);
this.mOldItems.clear();
}
DiffCallback可以為null。這樣會直接呼叫notifyChanged()方法,全刷。
如果DiffCallback不為null,那麼就會呼叫DiffUtil.calculateDiff去構造DiffResult。
最後呼叫DiffResult的dispatchUpdatesTo方法,在ListUpdateCallback中執行回撥,然後Adapter根據回撥執行notifyItemRangeInserted、notifyItemRangeRemoved、notifyItemMoved和notifyItemRangeChanged重新整理區域性介面。
一句話,leanback裡面ArrayObjectAdapter的DiffCallback就是recycleview裡面DiffUtil的Callback的封裝。
看一下DiffUtil的介紹
簡單就是一句話:
DiffUtil 使用了Eugene W. Myers’s difference algorithm這個演算法,我們可以簡單的翻譯成Myers 差分演算法,來計算兩個列表最小的更新運算元,但是還在它的基礎上增加了move操作。
後面有是否開啟move操作的時間,不開啟move操作速度執行會很快,開啟執行稍慢。
為了學習DiffUtil原始碼差分操作,需要先學習 Myers 差分演算法分析
/**
* DiffUtil is a utility class that calculates the difference between two lists and outputs a
* list of update operations that converts the first list into the second one.
* <p>
* It can be used to calculate updates for a RecyclerView Adapter. See {@link ListAdapter} and
* {@link AsyncListDiffer} which can simplify the use of DiffUtil on a background thread.
* <p>
* DiffUtil uses Eugene W. Myers's difference algorithm to calculate the minimal number of updates
* to convert one list into another. Myers's algorithm does not handle items that are moved so
* DiffUtil runs a second pass on the result to detect items that were moved.
* <p>
* Note that DiffUtil, ListAdapter, and AsyncListDiffer require the list to not mutate while in use.
* This generally means that both the lists themselves and their elements (or at least, the
* properties of elements used in diffing) should not be modified directly. Instead, new lists
* should be provided any time content changes. It's common for lists passed to DiffUtil to share
* elements that have not mutated, so it is not strictly required to reload all data to use
* DiffUtil.
* <p>
* If the lists are large, this operation may take significant time so you are advised to run this
* on a background thread, get the {@link DiffResult} then apply it on the RecyclerView on the main
* thread.
* <p>
* This algorithm is optimized for space and uses O(N) space to find the minimal
* number of addition and removal operations between the two lists. It has O(N + D^2) expected time
* performance where D is the length of the edit script.
* <p>
* If move detection is enabled, it takes an additional O(N^2) time where N is the total number of
* added and removed items. If your lists are already sorted by the same constraint (e.g. a created
* timestamp for a list of posts), you can disable move detection to improve performance.
* <p>
* The actual runtime of the algorithm significantly depends on the number of changes in the list
* and the cost of your comparison methods. Below are some average run times for reference:
* (The test list is composed of random UUID Strings and the tests are run on Nexus 5X with M)
* <ul>
* <li>100 items and 10 modifications: avg: 0.39 ms, median: 0.35 ms
* <li>100 items and 100 modifications: 3.82 ms, median: 3.75 ms
* <li>100 items and 100 modifications without moves: 2.09 ms, median: 2.06 ms
* <li>1000 items and 50 modifications: avg: 4.67 ms, median: 4.59 ms
* <li>1000 items and 50 modifications without moves: avg: 3.59 ms, median: 3.50 ms
* <li>1000 items and 200 modifications: 27.07 ms, median: 26.92 ms
* <li>1000 items and 200 modifications without moves: 13.54 ms, median: 13.36 ms
* </ul>
* <p>
* Due to implementation constraints, the max size of the list can be 2^26.
*
* @see ListAdapter
* @see AsyncListDiffer
*/