问题
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