English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android7Utility Class: Detailed Explanation of DiffUtil

1 Overview

DiffUtil is a support-v7:24.2A new utility class in .0, used to compare two datasets and find the old dataset-The minimum change amount of the new dataset.

When it comes to datasets, I believe everyone knows they are related to, which is my favorite, RecyclerView.

From my experience over these few days, its greatest use is to avoid blindly calling mAdapter.notifyDataSetChanged() when refreshing RecyclerView.

Previously, using mAdapter.notifyDataSetChanged() blindly had two drawbacks:

1.Will not trigger RecyclerView animations (delete, add, move, change animations)

2.Performance is lower, as it refreshes the entire RecyclerView without thinking, in extreme cases: if the old and new data sets are exactly the same, the efficiency is the lowest.

After using DiffUtil, change the code to the following:

DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, newDatas), true);
diffResult.dispatchUpdatesTo(mAdapter);

It will automatically calculate the differences between old and new data sets and automatically call the following four methods based on the difference situation.

adapter.notifyItemRangeInserted(position, count);
adapter.notifyItemRangeRemoved(position, count);
adapter.notifyItemMoved(fromPosition, toPosition);
adapter.notifyItemRangeChanged(position, count, payload);

It is obvious that these four methods are all accompanied by RecyclerView animations during execution, and they are all targeted refresh methods, which greatly improve the refresh efficiency.
As usual, let's show the picture first,

Figure 1 shows the effect of using notifyAdapterDataSetChanged() without thinking, you can see that the refresh interaction is very stiff, and the item suddenly appears at a certain position:

Figure 2 shows the effect of using DiffUtils, the most obvious feature is the animation of inserting and moving items:

The GIF is a bit poor, download the Demo at the end to see the effect better.

This article will include and not only include the following content:

1 First, let's introduce the simple usage of DiffUtil to achieve the 'incremental update' effect during refresh. ('Incremental update' is my own term)
2 Advanced usage of DiffUtil: completing partial updates when only the content (data) of an item changes, and the position (position) does not change (officially called Partial bind, partial binding).
3 Learn about the RecyclerView.Adapter's public void onBindViewHolder(VH holder, int position, List<Object> payloads) method and master it.
4 Calculate DiffResult in a sub-thread and refresh RecyclerView in the main thread.
5 How to remove the animation of notifyItemChanged() that causes the item to flash white light for some people who don't like it.
6 Partial translation of the official comments of some classes and methods of DiffUtil

Part 2: Simple Usage of DiffUtil

As mentioned earlier, DiffUtil helps us calculate the differences between old and new data sets when refreshing RecyclerView, and automatically calls the refresh method of RecyclerView.Adapter to complete efficient refresh with item animation effects.

Before we learn it, we need to do some preparation work first, write a simple version of the Demo that uses notifyDataSetChanged() to refresh without thinking.

1 A common JavaBean, but it implements the clone method, which is only used for writing a Demo to simulate refresh. It is not needed in actual projects because the data is pulled from the network during refresh.

class TestBean implements Cloneable {
 private String name;
 private String desc;
 ....//Get and set methods are omitted
 //Only for DEMO purpose, implement the cloning method
 @Override
 public TestBean clone() throws CloneNotSupportedException {
  TestBean bean = null;
  try {
   bean = (TestBean) super.clone();
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
  }
  return bean;
 }

2 Implement a simple RecyclerView.Adapter.

public class DiffAdapter extends RecyclerView.Adapter<DiffAdapter.DiffVH> {
 private final static String TAG = "zxt";
 private List<TestBean> mDatas;
 private Context mContext;
 private LayoutInflater mInflater;
 public DiffAdapter(Context mContext, List<TestBean> mDatas) {
  this.mContext = mContext;
  this.mDatas = mDatas;
  mInflater = LayoutInflater.from(mContext);
 }
 public void setDatas(List<TestBean> mDatas) {
  this.mDatas = mDatas;
 }
 @Override
 public DiffVH onCreateViewHolder(ViewGroup parent, int viewType) {
  return new DiffVH(mInflater.inflate(R.layout.item_diff, parent, false));
 }
 @Override
 public void onBindViewHolder(final DiffVH holder, final int position) {
  TestBean bean = mDatas.get(position);
  holder.tv1.setText(bean.getName());
  holder.tv2.setText(bean.getDesc());
  holder.iv.setImageResource(bean.getPic());
 }
 @Override
 public int getItemCount() {
  return mDatas != null63; mDatas.size() : 0;
 }
 class DiffVH extends RecyclerView.ViewHolder {
  TextView tv1, tv2;
  ImageView iv;
  public DiffVH(View itemView) {
   super(itemView);
   tv1 = (TextView) itemView.findViewById(R.id.tv1);
   tv2 = (TextView) itemView.findViewById(R.id.tv2);
   iv = (ImageView) itemView.findViewById(R.id.iv);
  }
 }
}

3 Activity code:

public class MainActivity extends AppCompatActivity {
 private List<TestBean> mDatas;
 private RecyclerView mRv;
 private DiffAdapter mAdapter;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  initData();
  mRv = (RecyclerView) findViewById(R.id.rv);
  mRv.setLayoutManager(new LinearLayoutManager(this));
  mAdapter = new DiffAdapter(this, mDatas);
  mRv.setAdapter(mAdapter);
 }
 private void initData() {
  mDatas = new ArrayList<>();
  mDatas.add(new TestBean("Zhang Xutong1"Android", R.drawable.pic1));
  mDatas.add(new TestBean("Zhang Xutong2"Java", R.drawable.pic2));
  mDatas.add(new TestBean("Zhang Xutong3"Back锅", R.drawable.pic3));
  mDatas.add(new TestBean("Zhang Xutong4"Hand撕product", R.drawable.pic4));
  mDatas.add(new TestBean("Zhang Xutong5"Hand撕test", R.drawable.pic5));
 }
 /**
  * Simulate refresh operation
  *
  * @param view
  */
 public void onRefresh(View view) {
  try {
   List<TestBean> newDatas = new ArrayList<>();
   for (TestBean bean : mDatas) {
    newDatas.add(bean.clone());//clone the old data once, simulate refresh operation
   }
   newDatas.add(new TestBean("Zhao Zilong", "handsome", R.drawable.pic6));//Simulate adding new data
   newDatas.get(0).setDesc("Android+");
   newDatas.get(0).setPic(R.drawable.pic7);//Simulate data modification
   TestBean testBean = newDatas.get(1);//Simulate data displacement
   newDatas.remove(testBean);
   newDatas.add(testBean);
   //Don't forget to give the new data to the Adapter
   mDatas = newDatas;
   mAdapter.setDatas(mDatas);
   mAdapter.notifyDataSetChanged();//In most cases before, we could only do it like this
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
  }
 }
}

It's simple, just that when building a new data source newDatas, it traverses the old data source mDatas, calls each data's clone() method to ensure that the new and old data sources are consistent in data but have different memory addresses (pointers), so that when modifying the values in newDatas later, it will not affect the values in mDatas.

4 activity_main.xml Some height and width codes have been removed, leaving only a RecyclerView and a Button to simulate refresh.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
>
 <android.support.v7.widget.RecyclerView
  android:id="@"+id/rv" />
 <Button
  android:id="@"+id/btnRefresh"
  android:layout_alignParentRight="true"
  android:onClick="onRefresh"
  android:text="Simulate Refresh" />
</RelativeLayout>

The above is a demo that a normal youth can easily write, without thinking, using notifyDataSetChanged(), as shown in Figure 1 of the first section.
But we all want to be artistic youth, so

Let's get down to the main topic, simply using DiffUtil, we need to write an additional class.

To become an artistic youth, we need to implement a class inheriting from DiffUtil.Callback and implement its four abstract methods.
Although this class is called Callback, it is more appropriate to understand it as: a class that defines some contracts and rules for comparing whether new and old items are equal.

The abstract class DiffUtil.Callback is as follows:

 public abstract static class Callback {
  public abstract int getOldListSize();//Size of the old dataset
  public abstract int getNewListSize();//Size of the new dataset
  public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);//Is the item at the same position in the new and old data sets an object? (The content may be different, if this returns true, the following method will be called)
  public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);//This method is only called if the above method returns true, my understanding is that only notifyItemRangeChanged() will call it, to determine if the item content has changed
  //This method is used in the advanced usage of DiffUtil, which will not be discussed here
  @Nullable
  public Object getChangePayload(int oldItemPosition, int newItemPosition) {
   return null;
  }
 }

  This demo implements DiffUtil.Callback, with core methods equipped with bilingual comments (in other words, the official English comments have been translated for better understanding).

/**
 * Description: Core class used to determine if new and old items are equal
 * Author: zhangxutong
 * Email: [email protected]
 * Time: 2016/9/12.
 */
public class DiffCallBack extends DiffUtil.Callback {
 private List<TestBean> mOldDatas, mNewDatas;//Look at the name
 public DiffCallBack(List<TestBean> mOldDatas, List<TestBean> mNewDatas) {
  this.mOldDatas = mOldDatas;
  this.mNewDatas = mNewDatas;
 }
 //Size of the old dataset
 @Override
 public int getOldListSize() {
  return mOldDatas != null &63; mOldDatas.size() : 0;
 }
 //Size of the new dataset
 @Override
 public int getNewListSize() {
  return mNewDatas != null &63; mNewDatas.size() : 0;
 }
 /**
  * Called by DiffUtil to decide whether two objects represent the same Item.
  * Called by DiffUtil to determine whether two objects are the same Item.
  * For example, if your items have unique ids, this method should check their id equality.
  * For example, if your Item has a unique id field, this method will judge whether the id is equal.
  * This example determines whether the name field is consistent.
  *
  * @param oldItemPosition The position of the item in the old list
  * @param newItemPosition The position of the item in the new list
  * @return True if the two items represent the same object or false if they are different.
  */
 @Override
 public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
  return mOldDatas.get(oldItemPosition).getName().equals(mNewDatas.get(newItemPosition).getName());
 }
 /**
  * Called by the DiffUtil when it wants to check whether two items have the same data.
  * This is called by DiffUtil to check whether two items contain the same data.
  * DiffUtil uses this information to detect if the content of an item has changed.
  * DiffUtil uses the returned information (true/false) to detect whether the content of the current item has changed.
  * DiffUtil uses this method to check for equality instead of {@link Object#equals(Object)}
  * DiffUtil uses this method to check for equality instead of the equals method.
  * so that you can change its behavior depending on your UI.
  * So you can change its return value according to your UI.
  * For example, if you are using DiffUtil with a
  * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should
  * return whether the visual representations of the items are the same.
  * For example, if you use RecyclerView.Adapter in conjunction with DiffUtil, you need to return whether the visual representation of the items is the same.
  * This method is called only if {@link #areItemsTheSame(int, int)} returns
  * {@code true} for these items.
  * This method is only called when the areItemsTheSame() returns true.
  * @param oldItemPosition The position of the item in the old list
  * @param newItemPosition The position of the item in the new list which replaces the
  *      oldItem
  * @return True if the contents of the items are the same or false if they are different.
  */
 @Override
 public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {}}
  TestBean beanOld = mOldDatas.get(oldItemPosition);
  TestBean beanNew = mNewDatas.get(newItemPosition);
  if (!beanOld.getDesc().equals(beanNew.getDesc())) {
   return false;//If there is different content, return false
  }
  if (beanOld.getPic() != beanNew.getPic()) {
   return false;//If there is different content, return false
  }
  return true; //By default, the two data contents are the same
 }

Detailed comments were written+Simple code, it is believed that it can be understood at a glance.

Then, when using it, comment out the notifyDatasetChanged() method you wrote before, and replace it with the following code:

//The new favorite of artistic youth
//Using the DiffUtil.calculateDiff() method, pass a rule-based DiffUtil.Callback object and a boolean variable indicating whether to detect the movement of items to get the DiffUtil.DiffResult object
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, newDatas), true);
//Using the dispatchUpdatesTo() method of the DiffUtil.DiffResult object, pass it to the RecyclerView.Adapter to easily become an artistic youth
diffResult.dispatchUpdatesTo(mAdapter);
//Don't forget to give the new data to the Adapter
mDatas = newDatas;
mAdapter.setDatas(mDatas);

Explanation:

Step 1

Before setting newDatas to the Adapter, call the DiffUtil.calculateDiff() method first,The minimum update set converted from the old and new data sets is calculated as follows:

The DiffUtil.DiffResult object.
The DiffUtil.calculateDiff() method is defined as follows:
The first parameter is a DiffUtil.Callback object,
The second parameter represents whether to detect the movement of the Item, setting it to false will make the algorithm more efficient, set it as needed, and we set it to true here.

public static DiffResult calculateDiff(Callback cb, boolean detectMoves)

Step two

Then, using the dispatchUpdatesTo() method of the DiffUtil.DiffResult object, pass the RecyclerView's Adapter to replace the mAdapter.notifyDataSetChanged() method commonly used by ordinary young people.

By examining the source code, we can see that this method internally calls the four directional refresh methods of the adapter based on the situation.

 public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) {
   dispatchUpdatesTo(new ListUpdateCallback() {
    @Override
    public void onInserted(int position, int count) {
     adapter.notifyItemRangeInserted(position, count);
    }
    @Override
    public void onRemoved(int position, int count) {
     adapter.notifyItemRangeRemoved(position, count);
    }
    @Override
    public void onMoved(int fromPosition, int toPosition) {
     adapter.notifyItemMoved(fromPosition, toPosition);
    }
    @Override
    public void onChanged(int position, int count, Object payload) {
     adapter.notifyItemRangeChanged(position, count, payload);
    }
   });
  }

Summary:

Therefore, DiffUtil is not only compatible with RecyclerView, but we can also implement the four methods of the ListUpdateCallback interface to do some things. (I'm not responsible for any random thought, can it be used with the nine-square control in my own project? Or optimize the NestFullListView I wrote in my last article? Little recommendation, see the relatively elegant solution for nesting ListView in ListView, RecyclerView, and ScrollView: http:)//blog.csdn.net/zxt0601/article/details/52494665)

Up to now, we have evolved into literary youth, and the running effect is basically consistent with the second figure of the first section,

The only difference is that at this time, adapter.notifyItemRangeChanged() will have an item white flash update animation (the position of the item in this article's Demo is 0). This flash animation is liked by some and disliked by others, but it's not important at all,

Because when we have learned the advanced usage of DiffUtil in the third section, whether you like this ItemChange animation or not, it will all fade away with the wind. (Don't know if it's an official bug)
The effect is as shown in the second figure of the first section, our item0 actually changed both the image and the text, but this change did not come with any animation.

Let's move towards the road of the most literary among the literary youth.

Three advanced usages of DiffUtil

Theory:

Advanced usage only involves two methods,
We need to implement DiffUtil.Callback's
The public Object getChangePayload(int oldItemPosition, int newItemPosition) method,
The returned Object represents what content the item has changed.

in conjunction with RecyclerView.Adapter's
The public void onBindViewHolder(VH holder, int position, List<Object> payloads) method,
Complete directional refresh. (Becoming the most literary among the literary youth, literary youth youth.)
Highlighting the blackboard, this is a new method, note that it has three parameters, the first two are familiar to us, and the third parameter contains the Object we return in getChangePayload().

Alright, let's first take a look at what kind of divine being this method is:

in v7-24.2.0 source code, it looks like this:

 /**
   * Called by RecyclerView to display the data at the specified position. This method
   * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
   * the given position.
   * <p>
   * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
   * again if the position of the item changes in the data set unless the item itself is
   * invalidated or the new position cannot be determined. For this reason, you should only
   * use the <code>position</code> parameter while acquiring the related data item inside
   * this method and should not keep a copy of it. If you need the position of an item later
   * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
   * have the updated adapter position.
   * <p>
   * Partial bind vs full bind:
   * <p>
   * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
   * {@link #notifyItemRangeChanged(int, int, Object)}. If the payloads list is not empty,
   * the ViewHolder is currently bound to old data and the Adapter may run an efficient partial
   * update using the payload info. If the payload is empty, Adapter must run a full bind.
   * Adapter should not assume that the payload passed in notify methods will be received by
   * onBindViewHolder(). For example when the view is not attached to the screen, the
   * payload in notifyItemChange() will be simply dropped.
   *
   * @param holder The ViewHolder which should be updated to represent the contents of the
   *    item at the given position in the data set.
   * @param position The position of the item within the adapter’s data set.
   * @param payloads A non-null list of merged payloads. Can be empty list if requires full
   *     update.
   */
  public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
   else {
  }

原来它内部就仅仅调用了两个参数的onBindViewHolder(holder, position) ,(题外话,哎哟喂,我的NestFullListView 的Adapter也有几分神似这种写法,看来我离Google大神又近了一步)

Only now did I realize that the entry point of onions-bind is actually this method, which is the method corresponding to onions-createViewHolder.
Scrolling down a few lines in the source code reveals a public final void bindViewHolder(VH holder, int position), which internally calls the three-argument onBindViewHolder.
It's not clear in just a few words about RecyclerView.Adapter (in fact, I only understand this much).
Alright, no longer digress, let's get back to our three-argument onBindViewHolder(VH holder, int position, List<Object> payloads) method. The method header has a lot of English comments, and I always think that reading these English comments is very helpful for understanding the method, so I translated them.

Translation:

Called by RecyclerView to display data at the specified position.
This method should update the content of the ItemView in the ViewHolder to reflect the changes in the item at the given position.
Please note that unlike ListView, if the dataset of the item at the given position changes, RecyclerView will not call this method again unless the item itself is invalidated (invalidated) or the new position cannot be determined.
For this reason, in this method, you should only use the position parameter to get the relevant data item, and should not keep a copy of this data item.
If you need the position of this item later, for example, to set a clickListener, you should use ViewHolder.getAdapterPosition(), which can provide the updated position.
(I saw this and found that this is explaining the two-argument onBindViewHolder method)
The unique part of this three-argument method is:)
**partial (partial) binding**vs complete (full) binding
The payloads parameter is a merged list obtained from (notifyItemChanged(int, Object) or notifyItemRangeChanged(int, int, Object)).
If the payloads list is not empty, the ViewHolder that is currently bound to the old data and the Adapter can use the data in the payload for an efficient partial update.
If the payload is empty, the Adapter must perform a complete binding (call the two-argument method).
Adapter should not assume (take for granted) that the payload passed in through the notifyxxxx notification methods will definitely be received in the onBindViewHolder() method. (This sentence is not translated well QAQ, just look at the examples)
For example, when the View is not attached to the screen, this payload from notifyItemChange() can simply be discarded.
The payloads object will not be null, but it may be empty (empty), at this time you need to bind completely (so we only need to judge isEmpty in the method, no need to judge empty repeatedly).
Author's words: This method is an efficient method. I am a low-efficiency translator, I have read40+minutes. Finally understand that the important part is highlighted in bold.

Practice:

So much talk, in fact, it's super simple to use:
Let's first see how to use the getChangePayload() method, along with bilingual comments

  

 /**
  * When {@link #areItemsTheSame(int, int)} returns {@code true} for two items and
  * When {@link #areContentsTheSame(int, int)} returns false for them, DiffUtil
  * calls this method to get a payload about the change.
  * 
  * When {@link #areItemsTheSame(int, int)} returns true and {@link #areContentsTheSame(int, int)} returns false, DiffUtils will call this method,
  * to get the payload (what has changed) of this item.
  * 
  * For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
  * specific field that has changed in the item and your
  * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
  * information to run the correct animation.
  * 
  * For example, if you use RecyclerView with DiffUtils, you can return the fields that this item has changed,
  * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} Can use what information to perform the correct animation
  * 
  * Default implementation returns {@code null}.  * The default implementation returns null
  *
  * @param oldItemPosition The position of the item in the old list
  * @param newItemPosition The position of the item in the new list
  * @return A payload object that represents the change between the two items.
  * Returns a payload object representing the change content between the old and new items.
  */
 @Nullable
 @Override
 public Object getChangePayload(int oldItemPosition, int newItemPosition) {
  //By implementing this method, you can become the most artistic among the artistic youth
  // Partial update in directional refresh
  // The most efficient
  //There is no white flash animation of ItemChange, (I think it's not very important anyway)
  TestBean oldBean = mOldDatas.get(oldItemPosition);
  TestBean newBean = mNewDatas.get(newItemPosition);
  //There is no need to compare the core fields here, they are definitely equal
  Bundle payload = new Bundle();
  if (!oldBean.getDesc().equals(newBean.getDesc())) {
   payload.putString("KEY_DESC", newBean.getDesc());
  }
  if (oldBean.getPic() != newBean.getPic()) {
   payload.putInt("KEY_PIC", newBean.getPic());
  }
  if (payload.size() == 0)//If there are no changes, pass an empty one
   return null;
  return payload;//
 }

In simple terms, this method returns an Object type payload that contains the changed content of a certain item.
We use Bundle to save these changes here.

In the Adapter, rewrite the three-argument onBindViewHolder as follows:

 @Override
 public void onBindViewHolder(DiffVH holder, int position, List<Object> payloads) {
  onBindViewHolder(holder, position);
   else {
  }
   //Literary youth in literary youth
   Bundle payload = (Bundle) payloads.get(0);
   TestBean bean = mDatas.get(position);
   for (String key : payload.keySet()) {
    switch (key) {
     case "KEY_DESC":
      //Here, you can use the data in the payload, or you can also use
      holder.tv2.setText(bean.getDesc());
      break;
     case "KEY_PIC":
      holder.iv.setImageResource(payload.getInt(key));
      break;
     default:
      break;
    }
   }
  }
 }

The payloads passed here is a List, as indicated by the comments, it must not be null, so we judge whether it is empty
If it is empty, call the two-argument function for a full bind
If it is not empty, perform partial bind
By taking out the payload returned in the getChangePayload method through index 0, and then iterating over the keys of the payload, searching according to the key, if there is corresponding change carried in the payload, it is taken out and then updated on the ItemView.
(Here, the data obtained through mDatas is also the latest data source data, so it can be updated with the data of payload or new data)

So far, we have mastered the refreshing method of RecyclerView, the most literary of literary youth.

Four, use DiffUtil in a sub-thread

The source code header comments of DiffUtil introduce relevant information about DiffUtil.
DiffUtil internally uses Eugene W. Myers's difference algorithm, but this algorithm cannot detect the movement of items, so Google improved it to support the detection of moving items based on this algorithm. However, detecting moving items will consume more performance.
In existence1000 items of data,}200 changes, this algorithm takes:
Opened when mobile detection is turned on: average:27.07ms, median:26.92ms.
Closed when mobile detection is turned off: average:13.54ms, median:13.36ms.
Those who are interested can read the comments at the beginning of the source code themselves, which is quite useful for us, especially one section that mentions,
If our list is too large, the time taken to calculate DiffResult is quite long, so we should put the process of obtaining DiffResult in a background thread and update the RecyclerView in the main thread.

Here I use Handler in conjunction with DiffUtil:

The code is as follows:

 private static final int H_CODE_UPDATE = 1;
 private List<TestBean> mNewDatas;//Add a variable to temporarily store newList
 private Handler mHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
   switch (msg.what) {
    case H_CODE_UPDATE:
     //Extract the Result
     DiffUtil.DiffResult diffResult = (DiffUtil.DiffResult) msg.obj;
     diffResult.dispatchUpdatesTo(mAdapter);
     //Don't forget to give the new data to the Adapter
     mDatas = mNewDatas;
     mAdapter.setDatas(mDatas);
     break;
   }
  }
 });
   new Thread(new Runnable() {
    @Override
    public void run() {
     //Calculate DiffResult in a background thread
     DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, mNewDatas), true);
     Message message = mHandler.obtainMessage(H_CODE_UPDATE);
     message.obj = diffResult;//obj stores DiffResult
     message.sendToTarget();
    }
   }).start();

This is a simple usage of Handler, no further elaboration is needed.

Five summaries and others

1 In fact, the code in this article is very little, you can download the Demo to view it, a total of four classes.
But unconsciously, it has been written so long, mainly involving the translation of some source code comments, which is convenient for everyone to better understand.

2 DiffUtil is very suitable for scenarios such as pull-to-refresh,
The update efficiency has improved, and there are animations, and ~ you don't even have to think about it.
However, if it is just to do a delete like like, there is no need to use DiffUtils. Just remember the position, judge whether the position is in the screen, and call the several directional refresh methods.

3 In fact, DiffUtil is not only used in combination with RecyclerView.Adapter,
We can implement the ListUpdateCallback interface ourselves and use DiffUtil to help us find the minimum difference set between the new and old data sets to do more things.

4 Note that when writing DEMO, the new and old data sets used for comparison, not only ArrayList, but also each data inside, must be different. Otherwise, changed cannot be triggered.
It is not encountered in actual projects because the new data is often obtained from the network.

5 Today is the last day of the Mid-Autumn Festival, and my company actually started working!!! Feeling angry, I wrote an article about DiffUtil, I don't even need to use DiffUtil, and I can easily compare the differences between our company and other companies. QQA, and today's state is not good, I actually wrote8It took an hour to finish. I thought this article could be selected into the micro essay collection, but I didn't expect it to be so long. Those without patience can download the DEMO to take a look, the amount of code is not much, and it is still very easy to use.

GitHub Gateway:
https://github.com/mcxtzhang/DiffUtils

The above is the introduction to Android7This is the material collection of the utility class DiffUtil in .0, and will continue to supplement relevant materials, thank you all for your support to this site!

You May Also Like