title: How to prevent BottomNavigationView animation from stuttering
body: <p>(First of all: no, it's not a duplicate of mentioned question :P read, then push the button.)</p>
  
  <p>I'm using the BottomNavigationView in one of my apps, loading Fragments with lists, which get their data from a ViewModel/LiveData/Dao. When selecting a Fragment via the BNV, it seems its animation somehow fights for UI-Thread time with the Fragment loading, causing it only to finish completely after the lists are displayed - which confuses me. I was under the impression, that LiveData calls are being handled async by default?</p>
  
  <p><a href="https://drive.google.com/open?id=1gwOuolk6LsDa8MKeL2AQ8aE2X7-vSw6J" rel="nofollow noreferrer">Stuttering gif</a></p>
  
  <p>Is this a known thing?</p>
  
  <p>ViewModel</p>
  
  <pre><code>public class ScheduleViewModel extends ViewModel {
      private final LiveData&lt;List&lt;ScheduleInfo&gt;&gt; arrivals;
      private final LiveData&lt;List&lt;ScheduleInfo&gt;&gt; departures;
  
      public ScheduleViewModel() {
          arrivals = SigmoDb.schedule().getArrivals();
          departures = SigmoDb.schedule().getDepartures();
      }
  
      public LiveData&lt;List&lt;ScheduleInfo&gt;&gt; getArrivals() {
          return arrivals;
      }
  
      public LiveData&lt;List&lt;ScheduleInfo&gt;&gt; getDepartures() {
          return departures;
      }
  }
  </code></pre>
  
  <p>Fragment</p>
  
  <pre><code>public class ArrivalsFragment extends MainFragment {
      private ScheduleDetailsAdapter adapter;
      private ScheduleViewModel viewModel;
  
      private final Observer&lt;List&lt;ScheduleInfo&gt;&gt; arrivalsObserver = new Observer&lt;List&lt;ScheduleInfo&gt;&gt;() {
          @Override
          public void onChanged(@Nullable List&lt;ScheduleInfo&gt; infoList) {
              adapter.setData(infoList);
          }
      };
  
      public static ArrivalsFragment newInstance() {
          return new ArrivalsFragment();
      }
  
      @Override
      public void onCreate(@Nullable Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          adapter = new ScheduleDetailsAdapter(getActivity());
          // using the parent fragment as LifeCycleOwner, since both its
          // child Fragments use the same ViewModel
          Fragment parent = getParentFragment();
          if (parent == null) {
              parent = this;
          }
          viewModel = ViewModelProviders.of(parent).get(ScheduleViewModel.class);
      }
  
      @Override
      public void onActivityCreated(@Nullable Bundle savedInstanceState) {
          super.onActivityCreated(savedInstanceState);
          reObserveViewModel();
      }
  
      // remove and re-add observer until Google fixes the multiple observer issue
      // TODO: remove when Google fixes the issue
      // https://github.com/googlesamples/android-architecture-components/issues/47
      private void reObserveViewModel() {
          viewModel.getArrivals().removeObservers(this);
          viewModel.getArrivals().observe(this, arrivalsObserver);
      }
  
      @Nullable
      @Override
      public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
          View view = inflater.inflate(R.layout.fragment_arrivals_departures, container, false);
  
          RecyclerView recyclerView = view.findViewById(R.id.rv_schedule_details);
          LinearLayoutManager llm = new LinearLayoutManager(this.getContext());
          recyclerView.setLayoutManager(llm);
          recyclerView.setAdapter(adapter);
          return view;
      }
  }
  </code></pre>
  
  <p>For info: I timestamped the ViewModel's constructor start and end (to rule out those calls somehow being on the UI thread - takes 1 millisecond).</p>
  
  <p><strong>Narrowed down the issue</strong></p>
  
  <p>After Robin Davies' answer, I tried the Android Profiler and although I get some GC events now and then, I don't get them all the time with the stuttering being there every single time. However, delaying setting of the adapter data in the observer by 100ms seems to let the BNV animation complete when switching to the ArrivalsFragment:</p>
  
  <p><a href="https://drive.google.com/open?id=1wXOTVKm6F-wCfaULKo6jAROPBq0z6TbQ" rel="nofollow noreferrer">No stuttering gif</a> </p>
  
  <p>All I did was changing</p>
  
  <pre><code>private final Observer&lt;List&lt;ScheduleInfo&gt;&gt; arrivalsObserver = new Observer&lt;List&lt;ScheduleInfo&gt;&gt;() {
      @Override
      public void onChanged(@Nullable List&lt;ScheduleInfo&gt; infoList) {
          adapter.setData(infoList);
      }
  };
  </code></pre>
  
  <p>to</p>
  
  <pre><code>private final Observer&lt;List&lt;ScheduleInfo&gt;&gt; arrivalsObserver = new Observer&lt;List&lt;ScheduleInfo&gt;&gt;() {
      @Override
      public void onChanged(@Nullable final List&lt;ScheduleInfo&gt; infoList) {
          new Handler().postDelayed(new Runnable() {
              @Override
              public void run() {
                  adapter.setData(infoList);
              }
          }, 100);
      }
  };
  </code></pre>
  
  <p>So it seems that this part of your answer</p>
  
  <blockquote>
    <p>Also if you post list results back to the foreground thread and
    populate the adapter while the animation is running, that will force a
    layout pass that will interfere with animation.</p>
  </blockquote>
  
  <p>is the one I was struggling with in my particular case. While I'm a bit disappointed having to revert to using delays to make the animation fluent, I'm happy to have found the culprit and thank you very much for your help :)</p>