Android 改进解析列表中嵌套的 json 响应
Android retrofit parse nested json response in List
我正在创建一个基于新闻 API 的 Android 应用程序,它使用 RecyclerView 加载特定频道的新闻,例如 ABC 新闻到 MainFragment。
我正在 MainFragment 中进行 API 调用,如下所示:
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);
}
}
在执行 articleList = response.body().getArticles()
发生错误,arraylist 仍然是空的。
API 调用不会在其中加载值。
我创建了两个 Retrofit 类:API接口和执行 GET API 调用的 API客户端: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;
}
}
我无法理解我是否在上面两个 类 中进行了正确的 API 调用,因为 JSON 数据没有在我的 arctilesList 列表和我的阵列适配器。
应用程序在 API 调用执行时崩溃。
请注意:API 通话正常。适配器正在成功加载 API 结果。
首先你需要了解的是,Retrofit 的Call#enqueue()
方法是异步的。您的代码从上到下执行。平均时间 enqueue()
方法发起一个异步请求到 API 和 returns 成功响应到 onResponse()
方法如果它是成功的 else onFailure()
方法。
那么,如何解决您的代码问题?
首先,您需要为 API 响应创建 POJO classes(如果您尚未创建),如下所示。
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
}
现在在您的 MainFragment class 中进行以下更改,
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);
}
}
}
不要忽略 onFailure()
方法中的 Throwable 对象。只需记录错误消息,而不是在 Toast 中显示错误消息。
Log.e("TAG", "Error occurred...", t);
通过这种方式,您可以轻松找出执行 API 请求时出现的问题。
我在回答中跳过了您的部分代码,因为它是正确的并且使我的回答有点长。请正确查看我在回答中使用的方法名称和注释。
我正在创建一个基于新闻 API 的 Android 应用程序,它使用 RecyclerView 加载特定频道的新闻,例如 ABC 新闻到 MainFragment。
我正在 MainFragment 中进行 API 调用,如下所示:
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);
}
}
在执行 articleList = response.body().getArticles()
发生错误,arraylist 仍然是空的。
API 调用不会在其中加载值。
我创建了两个 Retrofit 类:API接口和执行 GET API 调用的 API客户端: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;
}
}
我无法理解我是否在上面两个 类 中进行了正确的 API 调用,因为 JSON 数据没有在我的 arctilesList 列表和我的阵列适配器。
应用程序在 API 调用执行时崩溃。
请注意:API 通话正常。适配器正在成功加载 API 结果。
首先你需要了解的是,Retrofit 的Call#enqueue()
方法是异步的。您的代码从上到下执行。平均时间 enqueue()
方法发起一个异步请求到 API 和 returns 成功响应到 onResponse()
方法如果它是成功的 else onFailure()
方法。
那么,如何解决您的代码问题?
首先,您需要为 API 响应创建 POJO classes(如果您尚未创建),如下所示。
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
}
现在在您的 MainFragment class 中进行以下更改,
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);
}
}
}
不要忽略 onFailure()
方法中的 Throwable 对象。只需记录错误消息,而不是在 Toast 中显示错误消息。
Log.e("TAG", "Error occurred...", t);
通过这种方式,您可以轻松找出执行 API 请求时出现的问题。
我在回答中跳过了您的部分代码,因为它是正确的并且使我的回答有点长。请正确查看我在回答中使用的方法名称和注释。