1. 程式人生 > 其它 >區域性重新整理架構程式碼分析

區域性重新整理架構程式碼分析

技術標籤:Android開發android

頁面區域性重新整理實現中,我們說到了,區域性更新可以使用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
 */