title: Android Retrofit how to load data and load image independent to recycler view
body: <p>I'm using Retrofit to load data from web api and display data to recyclerview. Here are my layout:</p>
  
  <p>Item of recycler view:</p>
  
  <pre><code> &lt;LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      android:weightSum="100"
      &gt;
  
      &lt;LinearLayout
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_marginBottom="10dp"
          android:layout_weight="30"
  
          &gt;
          &lt;RelativeLayout
              android:layout_width="match_parent"
              android:layout_height="match_parent"&gt;
              &lt;android.support.v7.widget.CardView
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  xmlns:cardview="http://schemas.android.com/apk/res-auto"
                  cardview:cardCornerRadius="@dimen/cardview_corner_radius"
                  &gt;
                  &lt;ImageView
                      android:id="@+id/imgExhibit"
                      android:layout_width="match_parent"
                      android:layout_height="match_parent"
                      android:scaleType="fitXY"
  
                      android:src="@drawable/img_no_image" /&gt;
              &lt;/android.support.v7.widget.CardView&gt;
              &lt;ProgressBar
                  android:id="@+id/viewProgressBar"
                  android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:layout_centerInParent="true"
                  android:indeterminateDrawable="@drawable/my_progress"
                  android:visibility="gone"/&gt;
          &lt;/RelativeLayout&gt;
  
  
  
      &lt;/LinearLayout&gt;
  
  
      &lt;LinearLayout
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_weight="70"
          android:orientation="vertical"
          android:weightSum="100"&gt;
  
          &lt;TextView
              android:id="@+id/tvExhibitName"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
  
              android:layout_weight="10"
              android:text="TRỐNG NHẠC"
              android:textAppearance="@style/ExhibitNameMainscreen" /&gt;
  
          &lt;TextView
              android:id="@+id/tvExhibitDescription"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:textAppearance="@style/ExhibitDescriptionMainscreen"
              android:layout_weight="30"
              android:ellipsize="end"
              android:maxLines="2"
              android:text="Trống nhạc là hiện vật do Toà thánh Ngọc Sắc trao tặng"
               /&gt;
  
      &lt;/LinearLayout&gt;
  
  
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>Model is auto parse by <a href="http://www.jsonschema2pojo.org/" rel="nofollow noreferrer">http://www.jsonschema2pojo.org/</a> from api:
  <a href="http://demo.museum.vebrary.vn/api/Exhibit/GetPaging?pageindex=1&amp;pagesize=10" rel="nofollow noreferrer">http://demo.museum.vebrary.vn/api/Exhibit/GetPaging?pageindex=1&amp;pagesize=10</a></p>
  
  <pre><code>public class ExhibitMainScreenModel {
  
  @SerializedName("EXHID")
  @Expose
  private Integer eXHID;
  @SerializedName("EXHIBITNAME")
  @Expose
  private String eXHIBITNAME;
  @SerializedName("DESCRIPTION")
  @Expose
  private String dESCRIPTION;
  
  public Integer getEXHID() {
      return eXHID;
  }
  
  public void setEXHID(Integer eXHID) {
      this.eXHID = eXHID;
  }
  
  public String getEXHIBITNAME() {
      return eXHIBITNAME;
  }
  
  public void setEXHIBITNAME(String eXHIBITNAME) {
      this.eXHIBITNAME = eXHIBITNAME;
  }
  
  public String getDESCRIPTION() {
      return dESCRIPTION;
  }
  
  public void setDESCRIPTION(String dESCRIPTION) {
      this.dESCRIPTION = dESCRIPTION;
  }
  </code></pre>
  
  <p>}</p>
  
  <p>and api <a href="http://demo.museum.vebrary.vn/api/Exhibit/GetImages?id=4" rel="nofollow noreferrer">http://demo.museum.vebrary.vn/api/Exhibit/GetImages?id=4</a> return one String of image by id</p>
  
  <p>Previously, I used to add attribute String Image to Model because the previous api return both data and image. And I can load it easy to recycler view. But it toke many time loading.
  And then, I have 2 api get data and get image independent.
  I want to display data before, and UI main don't block by loading image to image view, I want to can scroll the recycler view while the image view loading (be like facebook app) 
  Here are Main activity class:</p>
  
  <pre><code>public class MainActivityNew extends AppCompatActivity implements View.OnClickListener {
  Toolbar toolbar;
  RecyclerView recyclerView;
  NavigationView navigationView;
  DrawerLayout drawerLayout;
  TextView tvTitleToolbar, txtTitleCategory;
  
  //RecyclerView api
  private ExhibitMainscreenRecyclerViewAdapter mAdapter;
  private ApiService mService;
  //ProgressDialog
  private ProgressBar viewProgressBar;
  private CustomProgressDialogTwo customProgressDialogTwo;
  private LoadMoreProgressDialog loadMoreProgressDialog;
  //load more
  private int indexPage=1;
  private int size=10;
  
  private EndlessRecyclerViewScrollListener scrollListener;
  //
  int id;
  
  
  
  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main_new);
      //set transparent stt bar
      //StatusBarUtil.setTransparent(this);
      addControl();
      actionBar();
      showDataToRecyclerView();
      showProgressDialog();
      loadAnswers(indexPage,size);
      showIntroMenu();
      setPositionTextViewTittleCategogy();
      addEvent();
  
  
  
  
  }
  
  
  
  public void showErrorMessage() {
      Toast.makeText(MainActivityNew.this, "Error loading posts", Toast.LENGTH_SHORT).show();
  }
  
  private void showProgressDialog() {
      viewProgressBar.setVisibility(View.GONE);
      customProgressDialogTwo.show();
  }
  private void showLoadMoreProgressDialog(){
      viewProgressBar.setVisibility(View.GONE);
      loadMoreProgressDialog.show();
  }
  private void loadAnswers(int indexPage,int size) {
      new Thread(new Runnable() {
          @Override
          public void run() {
              mService.getExhibitByPage(indexPage,size).enqueue(new Callback&lt;AllExhibitJsonResponse&gt;() {
                  @Override
                  public void onResponse(Call&lt;AllExhibitJsonResponse&gt; call, Response&lt;AllExhibitJsonResponse&gt; response) {
  
                      if(response.isSuccessful()) {
                          customProgressDialogTwo.dismiss();
                          mAdapter.updateAnswers(response.body().getExhibitModels());
                          Log.d("AnswersPresenter", "posts loaded from API");
                      }else {
                          int statusCode  = response.code();
                          Toast.makeText(MainActivityNew.this, "Error"+statusCode+response.message(), Toast.LENGTH_SHORT).show();
                      }
                  }
  
                  @Override
                  public void onFailure(Call&lt;AllExhibitJsonResponse&gt; call, Throwable t) {
                      showErrorMessage();
                      Log.d("AnswersPresenter", "error loading from API");
  
                  }
              });
          }
      }).start();
  
  }
  private void showDataToRecyclerView() {
      mAdapter = new ExhibitMainscreenRecyclerViewAdapter(this, new ArrayList&lt;ExhibitMainScreenModel&gt;(0), new ExhibitMainscreenRecyclerViewAdapter.PostItemListener() {
  
  
          @Override
          public void onPostClick(long id) {
              startDetailActivity((int)id);
  
          }
      });
      /*RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
      recyclerView.setLayoutManager(layoutManager);*/
      GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
      recyclerView.setLayoutManager(layoutManager);
      recyclerView.setAdapter(mAdapter);
      recyclerView.setHasFixedSize(true);
      EndlessRecyclerViewScrollListener scrollListener = new EndlessRecyclerViewScrollListener(layoutManager) {
  
          @Override
          public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
              showLoadMoreProgressDialog();
              loadMoreAnswers(page,size);
  
  
          }
      };
      recyclerView.addOnScrollListener(scrollListener);
  }
  
  private void loadMoreAnswers(int i, int size) {
      new Thread(new Runnable() {
          @Override
          public void run() {
              mService.getExhibitByPage(i+1,size).enqueue(new Callback&lt;AllExhibitJsonResponse&gt;() {
                  @Override
                  public void onResponse(Call&lt;AllExhibitJsonResponse&gt; call, Response&lt;AllExhibitJsonResponse&gt; response) {
  
                      if(response.isSuccessful()) {
                          loadMoreProgressDialog.dismiss();
                          mAdapter.updateMoreAnswers(response.body().getExhibitModels());
  
                          Log.d("AnswersPresenter", "posts loaded from API");
                      }else {
                          int statusCode  = response.code();
                          Toast.makeText(MainActivityNew.this, "Error"+statusCode+response.message(), Toast.LENGTH_SHORT).show();
                      }
                  }
  
                  @Override
                  public void onFailure(Call&lt;AllExhibitJsonResponse&gt; call, Throwable t) {
                      showErrorMessage();
                      Log.d("AnswersPresenter", "error loading from API");
  
                  }
              });
          }
      }).start();
  
  
  
  }
  </code></pre>
  
  <p>And the adapter:</p>
  
  <pre><code>public class ExhibitMainscreenRecyclerViewAdapter extends RecyclerView.Adapter&lt;ExhibitMainscreenRecyclerViewAdapter.ViewHolder&gt; {
  
  private List&lt;ExhibitMainScreenModel&gt; ExhibitList;
  private Context mContext;
  private PostItemListener mItemListener;
  private int id;
  //web api
  private ApiService mService;
  
  
  public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
  
      public TextView tvName,tvDescription;
      public ImageView imvExhibit;
      PostItemListener mItemListener;
  
      public ViewHolder(View itemView, PostItemListener postItemListener) {
          super(itemView);
          tvName = itemView.findViewById(R.id.tvExhibitName);
          tvDescription = itemView.findViewById(R.id.tvExhibitDescription);
          imvExhibit=itemView.findViewById(R.id.imgExhibit);
  
          this.mItemListener = postItemListener;
          itemView.setOnClickListener(this);
      }
  
      @Override
      public void onClick(View view) {
          ExhibitMainScreenModel item = getItem(getAdapterPosition());
          this.mItemListener.onPostClick(item.getEXHID());
  
          notifyDataSetChanged();
      }
  }
  
  public ExhibitMainscreenRecyclerViewAdapter(Context context, List&lt;ExhibitMainScreenModel&gt; posts, PostItemListener itemListener) {
      ExhibitList = posts;
      mContext = context;
      mItemListener = itemListener;
  }
  
  @Override
  public ExhibitMainscreenRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  
      Context context = parent.getContext();
      LayoutInflater inflater = LayoutInflater.from(context);
  
      View postView = inflater.inflate(R.layout.item_recyclerview_mainscreen, parent, false);
  
      ViewHolder viewHolder = new ViewHolder(postView, this.mItemListener);
      //web api
      mService = ApiUtils.getSOService();
      return viewHolder;
  }
  
  @Override
  public void onBindViewHolder(ExhibitMainscreenRecyclerViewAdapter.ViewHolder holder, int position) {
  
      ExhibitMainScreenModel item = ExhibitList.get(position);
      TextView tvName = holder.tvName;
      tvName.setText(item.getEXHIBITNAME());
      TextView tvDesc = holder.tvDescription;
      tvDesc.setText(item.getDESCRIPTION());
      //
      id = item.getEXHID();
      loadImage(id,holder);
  
  
  
  
  }
  
  private void loadImage(int id, ViewHolder holder) {
      new Thread(new Runnable() {
          @Override
          public void run() {
                  //get image default
                  mService.getExhibitImageById(id, true).enqueue(new Callback&lt;String&gt;() {
                      @Override
                      public void onResponse(Call&lt;String&gt; call, Response&lt;String&gt; response) {
  
                          if (response.isSuccessful()) {
                              try{
                                  showImage(response.body(),holder);
                                  Log.d("AnswersPresenter", "Image loaded!!!!");
                              }
                              catch (Exception e)
                              {
                                  e.printStackTrace();
                              }
  
                          } else {
                              int statusCode = response.code();
                              Toast.makeText(mContext, "Error" + statusCode + response.message(), Toast.LENGTH_SHORT).show();
                          }
  
                      }
  
  
                      @Override
                      public void onFailure(Call&lt;String&gt; call, Throwable t) {
                          showErrorMessage();
                          Log.d("AnswersPresenter", "error loading image!!!");
  
                      }
                  });
  
          }
      }).run();
  }
  
  @Override
  public int getItemCount() {
      return ExhibitList.size();
  }
  
  public void updateAnswers(List&lt;ExhibitMainScreenModel&gt; items) {
      ExhibitList = items;
      notifyDataSetChanged();
  }
  public void updateMoreAnswers(List&lt;ExhibitMainScreenModel&gt; items) {
      ExhibitList.addAll(items);
      notifyDataSetChanged();
  }
  
  private ExhibitMainScreenModel getItem(int adapterPosition) {
      return ExhibitList.get(adapterPosition);
  }
  
  public interface PostItemListener {
      void onPostClick(long id);
  }
  private void showImage(String imageString, ViewHolder holder) {
      Bitmap bmp = Util.StringToBitMap(imageString);
      ImageView imv = holder.imvExhibit;
      imv.setImageBitmap(bmp);
  
  }
  
  public void showErrorMessage() {
      Toast.makeText(mContext, "Error loading posts", Toast.LENGTH_SHORT).show();
  }
  </code></pre>
  
  <p>}</p>
  
  <p>And here is result with my code. 
  <a href="https://i.stack.imgur.com/7NKkS.jpg" rel="nofollow noreferrer">result</a></p>
  
  <p>but the image is display intricate
  So, can you help me?
  Ah, It usually have error "android application doing too much work on its main thread" 
  Can you help me solve it? Thank you very much!!</p>