Android retrofit parse nested json response in List

折月煮酒 提交于 2019-12-12 13:41:44

问题


I am creating a News API based Android application which loads the news of a particular channel say ABC News into MainFragment using a RecyclerView.

I am making an API Call for that in MainFragment as follows:

MainFragment.java

public class MainFragment extends Fragment
{
   protected RecyclerView recyclerView;
   protected NewsAdapter adapter;
   protected String API_KEY;
   String sourceTitle, sourceID;
   List<Articles> articleList;

public MainFragment() {

}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState)
{
    ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_main, null);

    sourceTitle = "ABC News";
    sourceID = "abc-news";

    getActivity().setTitle(sourceTitle);

    API_KEY = getString(R.string.API_KEY);

    recyclerView = root.findViewById(R.id.recyclerview);

    RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.setItemAnimator(new DefaultItemAnimator());

    ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);

    Call<News> call = apiService.getArticles(sourceID, API_KEY);

    call.enqueue(new Callback<News>()
    {
        @Override
        public void onResponse(Call<News> call, Response<News> response)
        {

            if (response != null && response.isSuccessful())
            {
                articleList = response.body().getArticles();
                populateRecycleView();
            }
            else
            {
                Toast.makeText(getActivity(), "Something went wrong..", Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onFailure(Call<News> call, Throwable t)
        {
            Toast.makeText(getActivity(), "Error in API Call", Toast.LENGTH_SHORT).show();
        }
    });

    recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity().getApplicationContext(), recyclerView, new ClickListener() {
        @Override
        public void onClick(View view, int position)
        {
            //onclick code
        }

        @Override
        public void onLongClick(View view, int position) {
        }
    }));
    return root;
}

     private void populateRecycleView()
     {
         if (articleList.isEmpty() || articleList.size() == 0)
         {
            recyclerView.setAdapter(null);
            Toast.makeText(getActivity(), "Error in List", Toast.LENGTH_SHORT).show();
         }
         else
         {
            adapter = new NewsAdapter(articleList, getActivity());
            recyclerView.setAdapter(adapter);
         }
    }

While executing the articleList = response.body().getArticles() an error occurs, and the arraylist is still empty.

The API call doesn't load values inside it.

I have created two Retrofit classes: APIInterface and APIClient that executes the GET API call: https://newsapi.org/v2/top-headlines?sources=abc-news&apiKey=MY_API_KEY.

APIInterface.java

  public interface ApiInterface
 {
   @GET("top-headlines")
   Call<List<News>> getArticles(@Query("sources") String source, @Query("apiKey") String apiKey);
 }

APIClient.java

 public class ApiClient
 {
  public static final String BASE_URL = "https://newsapi.org/v2/";
  private static Retrofit retrofit = null;


  public static Retrofit getClient()
  {
    if (retrofit==null)
    {
        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
    return retrofit;
  }
 }

I am unable to understand whether I am making the right API call in the above two classes, because the JSON data is not getting parsed in my arctilesList List and in my array adapter.

The Application crashes in the API call execution.

Please note: API calls working. The adapter is getting successfully loaded with the API results.


回答1:


First thing you need to understand is, Retrofit's Call#enqueue() method is Asynchronous. Your code executes top to bottom. Mean time enqueue() method initiates an asynchronous request to API and returns success response to onResponse() method if it is successful else onFailure() method.

So, how to fix your code problem?

First thing you need to create POJO classes (if you haven't created yet) for API response like below.

Article.java

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Article {

    @SerializedName("source")
    @Expose
    private Source source;
    @SerializedName("author")
    @Expose
    private String author;
    @SerializedName("title")
    @Expose
    private String title;
    @SerializedName("description")
    @Expose
    private String description;
    @SerializedName("url")
    @Expose
    private String url;
    @SerializedName("urlToImage")
    @Expose
    private Object urlToImage;
    @SerializedName("publishedAt")
    @Expose
    private String publishedAt;
    @SerializedName("content")
    @Expose
    private String content;

    // constructors

    // getters and setter methods

    // use Alt + Insert to generate constructors, getter and setter methods in Android Studio

}

Source.java

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Source {

    @SerializedName("id")
    @Expose
    private String id;
    @SerializedName("name")
    @Expose
    private String name;

    // constructors

    // getters and setter methods
}

News.java

import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class News {

    @SerializedName("status")
    @Expose
    private String status;
    @SerializedName("totalResults")
    @Expose
    private Integer totalResults;
    @SerializedName("articles")
    @Expose
    private List<Article> articles = null;

    // constructors

    // getters and setter methods
}

Now in your MainFragment class do the below changes,

public class MainFragment extends Fragment {

    // other part of the code here

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {

        // other part of the code here

        call.enqueue(new Callback<News>() {
            @Override
            public void onResponse(Call<News> call, Response<News> response) {

                if (response != null && response.isSuccessful()) {

                    articleList = response.body().getArticles();

                    // request is successful just populate data in RecyclerView

                    populateRecyclerView(); 

                } else {

                    Toast.makeText(getActivity(), "Something went wrong...", Toast.LENGTH_SHORT).show();

                }
            }

            @Override
            public void onFailure(Call<News> call, Throwable t) {
                Toast.makeText(getActivity(), "Error in API Call", Toast.LENGTH_SHORT).show();
            }
        });

        // just move ArrayList checking and setting adapter part code into some other method

        // other part of the code here

    }

    private void populateRecyclerView() {

        if (articleList.isEmpty() || articleList.size() == 0) {
            recyclerView.setAdapter(null);
            Toast.makeText(getActivity(), "Error in List", Toast.LENGTH_SHORT).show();
        } else {
            adapter = new NewsAdapter(articleList, getActivity());
            recyclerView.setAdapter(adapter);
        }

    }

}

Do not ignore Throwable object in onFailure() method . Just log the error messages instead of showing error messages in Toast.

Log.e("TAG", "Error occurred...", t);

This way you could easily find out what went wrong while executing API request.

I skipped some part of your code in my answer as it's correct and makes my answer little long. Please look into method name and comments I've used in my answer properly.



来源:https://stackoverflow.com/questions/52456880/android-retrofit-parse-nested-json-response-in-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!