我已经制作了一个Android应用程序,该应用程序对Guardian服务器进行HTTP查询,并从其中获取JSON响应,然后显示一个列表,其中包含所有找到的文章的信息。
此列表显示在主要活动(ArticleActivity)中。该应用程序还具有订单首选项,您可以在其中选择是要按日期还是按相关性对文章进行排序。此首选项位于SettingsActivity中,后者是ArticleActivity的子级。
HTTP请求在另一个线程中发生。我已经使用了ASyncTaskLoader。
所以问题在于,在我按下SettingsActivity中的“后退”按钮以返回ArticleActivity之后,我还必须按下ArticleActivity中的“搜索”按钮来刷新列表,并在更改了orderBy参数的情况下重复查询。但这是额外的点击。当我按下SearchActivity中的“后退”按钮时,我希望我的应用以另一种顺序刷新文章列表。
该怎么做?
以下是一些代码:
ArticleActivity.java
public class ArticleActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Article>> {
/** Adapter for the list of earthquakes */
private ArticleAdapter mAdapter;
/** LoaderManager for the ArticleActivity */
private LoaderManager loaderManager;
/** If there are no earthquakes found, this view would show up */
private TextView mEmptyStateTextView;
/** Circle rotating while the app is getting an HTTP response */
private ProgressBar loadingIndicator;
/** Default loader ID (0 — initialize loader, 1 — restart it) */
private int loader_id = 0;
/** By clicking these buttons you go to the previous and next 10 results */
private Button buttonPrevious;
private Button buttonNext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.article_activity);
loadingIndicator = findViewById(R.id.loading_indicator);
buttonPrevious = findViewById(R.id.button_previous);
buttonNext = findViewById(R.id.button_next);
Button searchButton = findViewById(R.id.search_button);
// Set onClickListener on the searchButton
searchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Search field
EditText searchText = findViewById(R.id.search_text);
// Add a search query to the initial HTTP link
QueryUtils.setSearchQuery(QueryUtils.getIniitialGuardianUrl() + searchText.getText().toString().replace("\\s", "+"));
// Create an adapter with an empty list of articles
mAdapter = new ArticleAdapter(ArticleActivity.this,
new ArrayList<Article>());
// Set an adapter on the listView
final ListView listView = findViewById(R.id.list);
listView.setAdapter(mAdapter);
// Set a view that would show up, if there are no
// articles found
mEmptyStateTextView = findViewById(R.id.empty_text_view);
listView.setEmptyView(mEmptyStateTextView);
// Get a reference to the ConnectivityManager to check state of network connectivity
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
// Get details on the currently active default data network
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
// If there is a network connection, fetch data
if (networkInfo != null && networkInfo.isConnected()) {
// Get a reference to the LoaderManager, in order to interact with loaders.
loaderManager = getLoaderManager();
// Initialize the loader if the ID = 0. If it's 1, then restart the loader.
// Pass in the int ID 1 and pass in null for
// the bundle. Pass in this activity for the LoaderCallbacks parameter (which is valid
// because this activity implements the LoaderCallbacks interface).
if (loader_id == 0) {
loader_id++;
loaderManager.initLoader(loader_id, null, ArticleActivity.this);
}
else {
loaderManager.restartLoader(loader_id, null, ArticleActivity.this);
}
// Set onItemClickListener on the listView. Click on the item to
// read the full article
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Article currentArticle = (Article) listView.getItemAtPosition(position);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(currentArticle.getUrl()));
startActivity(intent);
}
});
}
else {
// Otherwise, display error
// First, hide loading indicator so error message will be visible
View loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
// Update empty state with no connection error message
mEmptyStateTextView.setText(getString(R.string.no_internet_connection));
}
}
});
buttonPrevious.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAdapter.clear();
// Assign the previous page number to the start index
QueryUtils.setStartIndex(QueryUtils.getStartIndex() - 1);
loaderManager.restartLoader(loader_id, null, ArticleActivity.this);
}
});
buttonNext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAdapter.clear();
// Assign the next page number to the start index
QueryUtils.setStartIndex(QueryUtils.getStartIndex() + 1);
loaderManager.restartLoader(loader_id, null, ArticleActivity.this);
}
});
}
@NonNull
@Override
public Loader<List<Article>> onCreateLoader(int id, @Nullable Bundle args) {
buttonPrevious.setEnabled(false);
buttonNext.setEnabled(false);
// Get preferences from the default preferences location
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
// Get the "orderBy" preference value
String orderBy = sharedPrefs.getString(
getString(R.string.settings_order_by_key),
getString(R.string.settings_order_by_default)
);
// If there were no articles found last time, delete "no articles found"
// from the screen.
mEmptyStateTextView.setText("");
// Make loading indicator appear because the loading is about to start
loadingIndicator.setVisibility(View.VISIBLE);
// Create a new loader for the given URL
return new ArticleLoader(this, QueryUtils.getSearchQuery(),
QueryUtils.getStartIndex(), orderBy);
}
@Override
public void onLoadFinished(@NonNull Loader<List<Article>> loader, List<Article> articles) {
// Hide loading indicator because the data has been loaded
loadingIndicator.setVisibility(View.GONE);
// Clear the adapter of previous article data
mAdapter.clear();
// If there is a valid list of {@link Article}s, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (articles != null && !articles.isEmpty()) {
mAdapter.addAll(articles);
}
setPageButtonsStates();
// Set empty state text to display "No articles found."
if (!QueryUtils.hasResults)
mEmptyStateTextView.setText(getString(R.string.no_articles_found));
}
@Override
public void onLoaderReset(@NonNull Loader<List<Article>> loader) {
// Loader reset, so we can clear out our existing data.
mAdapter.clear();
}
private void setPageButtonsStates() {
// Check if there are previous and next pages
if (QueryUtils.getStartIndex() >= 2)
{
buttonPrevious.setEnabled(true);
}
else
{
buttonPrevious.setEnabled(false);
}
if (QueryUtils.getTotalArticles() - QueryUtils.getStartIndex() >= 1)
{
buttonNext.setEnabled(true);
}
else
{
buttonNext.setEnabled(false);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
// Go to the SettingsActivity if the settings button is clicked
if (item.getItemId() == R.id.settings_menu)
{
Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivity(settingsIntent);
return true;
}
return super.onOptionsItemSelected(item);
}
}
ArticleLoader.java
public class ArticleLoader extends AsyncTaskLoader<List<Article>> {
/** Query URL */
private String mUrl;
/** Start index for the 10 displayed articles */
private int startIndex;
/** Display order preference value for the query */
private String orderBy;
/**
* Constructs a new {@link ArticleLoader}.
*
* @param context of the activity
* @param url to load data from
*/
public ArticleLoader(Context context, String url,
int startIndex, String orderBy) {
super(context);
mUrl = url;
this.startIndex = startIndex;
this.orderBy = orderBy;
}
@Override
protected void onStartLoading() {
forceLoad();
}
/**
* This is on a background thread.
*/
@Nullable
@Override
public List<Article> loadInBackground() {
if (mUrl == null) {
return null;
}
// List of 10 books from Guardian's JSON response
List<Article> currentArticles;
mUrl = mUrl.replaceFirst("&page=1", "&page=" + startIndex);
mUrl = mUrl.replaceFirst("&order-by=relevance", "&order-by=" + orderBy);
// Perform the network request, parse the response, and extract a list of earthquakes.
currentArticles = QueryUtils.fetchArticlesData(mUrl);
return currentArticles;
}
}
QueryUtils.java
public class QueryUtils {
private static final String INIITIAL_GUARDIAN_URL =
"https://content.guardianapis.com/search?api-key=6ddccecf-47d2-4a2c-99c9-77643e12a2f6&order-by=relevance&show-tags=contributor&show-elements=image&show-fields=all&page=1&q=";
private static String searchQuery;
// Variable for checking if there are articles found
public static boolean hasResults;
// Current page number
private static int startIndex = 1;
// Number of all the articles found
private static int totalArticles = 0;
public static int getTotalArticles() {
return totalArticles;
}
public static int getStartIndex() {
return startIndex;
}
public static void setStartIndex(int startIndex) {
QueryUtils.startIndex = startIndex;
}
private QueryUtils() {
}
public static String getIniitialGuardianUrl() {
return INIITIAL_GUARDIAN_URL;
}
public static void setSearchQuery(String searchQuery) {
QueryUtils.searchQuery = searchQuery;
}
public static String getSearchQuery() {
return searchQuery;
}
/**
* Query the Guardian dataset and return an {@link ArrayList} object to represent a list of articles.
*/
/**
* Return a list of {@link Article} objects that has been built up from
* parsing the given JSON response.
*/
private static List<Article> extractArticlesFromJson(String articleJSON) {
// If the JSON string is empty or null, then return early.
if (TextUtils.isEmpty(articleJSON)) {
Log.e("nojson", "No JSON returned");
return null;
}
// Create an empty ArrayList that we can start adding articles to
List<Article> articles = new ArrayList<>();
// Try to parse the JSON response string. If there's a problem with the way the JSON
// is formatted, a JSONException exception object will be thrown.
// Catch the exception so the app doesn't crash, and print the error message to the logs.
try {
// Create a JSONObject from the JSON response string
JSONObject jsonRoot = new JSONObject(articleJSON);
JSONObject jsonResponse = jsonRoot.getJSONObject("response");
Log.v("success", "JSONObject successfully created!");
totalArticles = jsonResponse.getInt("total");
Log.v("success", "articles found" + totalArticles);
if (totalArticles > 0) {
// Extract the JSONArray associated with the key called "results",
// which represents a list of items (or articles).
JSONArray articleArray = jsonResponse.getJSONArray("results");
for (int i = 0; i < articleArray.length(); i++) {
// Get a single Article at position i within the list of articles
JSONObject currentArticle = articleArray.getJSONObject(i);
// Get an article's section name
String sectionName = currentArticle.getString("sectionName");
// Get an article's url
String articleUrl = currentArticle.getString("webUrl");
//SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy.MMMMM.dd GGG hh:mm aaa");
//String publicationDate = simpleDateFormat.format(currentArticle.getString("webPublicationDate"));
// Get an article's publication date
String publicationDate = currentArticle.getString("webPublicationDate");
// By default, assign "No information" to the authors' string
// in case there's no information about the authors of
// the article
String authorsString = "No information";
// For a given article, extract the JSONObject associated with the
// key called "tags", which represents a list of some extra information
// for that article, such as authors
JSONArray jsonAuthors = currentArticle.getJSONArray("tags");
if (jsonAuthors.length() > 0) {
// If authors are found, delete "No information" phrase
authorsString = "";
// Get an article's authors
for (int j = 0; j < jsonAuthors.length(); j++) {
JSONObject jsonAuthor = jsonAuthors.getJSONObject(j);
authorsString += jsonAuthor.getString("webTitle");
if (j != jsonAuthors.length() - 1) {
authorsString += "\n";
}
}
}
// For a given article, extract the JSONObject associated with the
// key called "fields", which represents a list of some extra information
// for that article, such as headline, description and thumbnail
JSONObject fields = currentArticle.getJSONObject("fields");
// Extract the value for the key called "title"
String headline = fields.getString("headline");
// Extract the value for the key called "trailText" (description of an article)
String description = fields.getString("trailText");
// An article's thumbnail link
String thumbnail;
// An article's thumbnail
Bitmap articleThumbnail = null;
if (fields.has("thumbnail")) {
thumbnail = fields.getString("thumbnail");
// Get an article thumbnail
articleThumbnail = getImage(thumbnail);
// Create a new {@link Article} object with the headline, description, section,
// publication date and thumbnail from the JSON response.
Article article = new Article(headline, description, sectionName,
authorsString, publicationDate, articleUrl);
article.setArticleThumbnail(articleThumbnail);
// Add the new {@link Article} to the list of articles.
articles.add(article);
}
else {
// Create a new {@link Article} object with the headline, description, section,
// publication date and url from the JSON response.
Article article = new Article(headline, description, sectionName,
authorsString, publicationDate, articleUrl);
articles.add(article);
}
}
}
}
catch (JSONException e) {
// If an error is thrown when executing any of the above statements in the "try" block,
// catch the exception here, so the app doesn't crash. Print a log message
// with the message from the exception.
Log.e("QueryUtils", "Problem parsing the article JSON results", e);
}
// Return the list of articles
return articles;
}
/**
* Get an article thumbnail.
*/
/**
* Return an article thumbnail
*/
private static Bitmap getImage(String imageLink)
{
Bitmap image = null;
try {
URL url = new URL(imageLink);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
image = BitmapFactory.decodeStream(input);
} catch (IOException e) {
// Log exception
Log.e("getImage: ", "Image problems", e);
}
return image;
}
/**
* Check if there are articles found
* @param baseJsonResponse — root JSONObject from the Guardian HTTP response
* @return boolean value (true if there are results, false if not)
*/
/*private static boolean hasArticlesFound(JSONObject baseJsonResponse) {
boolean hasArticlesFound = true;
try {
if (baseJsonResponse.getInt("total") == 0) {
hasArticlesFound = false;
}
}
catch (JSONException e)
{
Log.e("hasArticlesFound: ", "JSONException", e);
}
return hasArticlesFound;
}*/
/**
* Query the Guardian dataset and return a list of {@link Article} objects.
*/
public static List<Article> fetchArticlesData(String requestUrl) {
// Create URL object
URL url = createUrl(requestUrl);
// Perform HTTP request to the URL and receive a JSON response back
String jsonResponse = null;
try {
jsonResponse = makeHttpRequest(url);
} catch (IOException e) {
Log.e("", "Problem making the HTTP request.", e);
}
// Extract relevant fields from the JSON response and create a list of {@link Article}s
List<Article> articles = extractArticlesFromJson(jsonResponse);
Log.v("QueryUtils", "fetching data");
// Return the list of {@link article}s
return articles;
}
/**
* Returns new URL object from the given string URL.
*/
private static URL createUrl(String stringUrl) {
URL url = null;
try {
url = new URL(stringUrl);
} catch (MalformedURLException e) {
Log.e("QueryUtils: : ", "Error with creating URL", e);
}
return url;
}
/**
* Make an HTTP request to the given URL and return a String as the response.
*/
private static String makeHttpRequest(URL url) throws IOException {
String jsonResponse = "";
// If the URL is null, then return early.
if (url == null) {
return jsonResponse;
}
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// If the request was successful (response code 200),
// then read the input stream and parse the response.
if (urlConnection.getResponseCode() == 200) {
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
Log.e("", "Error response code: " + urlConnection.getResponseCode());
}
} catch (IOException e) {
Log.e("", "Problem retrieving the earthquake JSON results.", e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
inputStream.close();
}
}
return jsonResponse;
}
/**
* Convert the {@link InputStream} into a String which contains the
* whole JSON response from the server.
*/
private static String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
}
SettingsActivity.java
public class SettingsActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_container);
}
public static class SettingsFragment extends PreferenceFragment
implements Preference.OnPreferenceChangeListener
{
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
// Find the "orderBy" preference and bind preference summary to value
Preference orderBy = findPreference("order_by");
bindPreferenceSummaryToValue(orderBy);
}
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof ListPreference) {
ListPreference listPreference = (ListPreference) preference;
int prefIndex = listPreference.findIndexOfValue(stringValue);
if (prefIndex >= 0) {
CharSequence[] labels = listPreference.getEntries();
preference.setSummary(labels[prefIndex]);
}
} else {
preference.setSummary(stringValue);
}
return true;
}
private void bindPreferenceSummaryToValue(Preference preference) {
preference.setOnPreferenceChangeListener(this);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(preference.getContext());
String preferenceString = preferences.getString(preference.getKey(), "");
onPreferenceChange(preference, preferenceString);
}
}
}