Last active
September 4, 2018 12:28
-
-
Save mtali/392d25dc08a5acb36914c032f7995987 to your computer and use it in GitHub Desktop.
My implementation of AsyncTaskLoader to avoid possible Memory Leak
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2016 The Android Open Source Project | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.example.android.sunshine; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.net.Uri; | |
import android.os.Bundle; | |
import android.support.v4.app.LoaderManager; | |
import android.support.v4.content.AsyncTaskLoader; | |
import android.support.v4.content.Loader; | |
import android.support.v7.app.AppCompatActivity; | |
import android.support.v7.widget.LinearLayoutManager; | |
import android.support.v7.widget.RecyclerView; | |
import android.util.Log; | |
import android.view.Menu; | |
import android.view.MenuInflater; | |
import android.view.MenuItem; | |
import android.view.View; | |
import android.widget.ProgressBar; | |
import android.widget.TextView; | |
import android.widget.Toast; | |
import com.example.android.sunshine.ForecastAdapter.ForecastAdapterOnClickHandler; | |
import com.example.android.sunshine.data.SunshinePreferences; | |
import com.example.android.sunshine.utilities.NetworkUtils; | |
import com.example.android.sunshine.utilities.OpenWeatherJsonUtils; | |
import java.lang.ref.WeakReference; | |
import java.net.URL; | |
public class MainActivity extends AppCompatActivity implements ForecastAdapterOnClickHandler, | |
LoaderManager.LoaderCallbacks<String[]> { | |
private static final String TAG = MainActivity.class.getSimpleName(); | |
private RecyclerView mRecyclerView; | |
private ForecastAdapter mForecastAdapter; | |
private TextView mErrorMessageDisplay; | |
private ProgressBar mLoadingIndicator; | |
private static final int WEATHER_LOADER_ID = 1; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_forecast); | |
/* | |
* Using findViewById, we get a reference to our RecyclerView from xml. This allows us to | |
* do things like set the adapter of the RecyclerView and toggle the visibility. | |
*/ | |
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview_forecast); | |
/* This TextView is used to display errors and will be hidden if there are no errors */ | |
mErrorMessageDisplay = (TextView) findViewById(R.id.tv_error_message_display); | |
/* | |
* LinearLayoutManager can support HORIZONTAL or VERTICAL orientations. The reverse layout | |
* parameter is useful mostly for HORIZONTAL layouts that should reverse for right to left | |
* languages. | |
*/ | |
LinearLayoutManager layoutManager | |
= new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); | |
mRecyclerView.setLayoutManager(layoutManager); | |
/* | |
* Use this setting to improve performance if you know that changes in content do not | |
* change the child layout size in the RecyclerView | |
*/ | |
mRecyclerView.setHasFixedSize(true); | |
/* | |
* The ForecastAdapter is responsible for linking our weather data with the Views that | |
* will end up displaying our weather data. | |
*/ | |
mForecastAdapter = new ForecastAdapter(this); | |
/* Setting the adapter attaches it to the RecyclerView in our layout. */ | |
mRecyclerView.setAdapter(mForecastAdapter); | |
/* | |
* The ProgressBar that will indicate to the user that we are loading data. It will be | |
* hidden when no data is loading. | |
* | |
* Please note: This so called "ProgressBar" isn't a bar by default. It is more of a | |
* circle. We didn't make the rules (or the names of Views), we just follow them. | |
*/ | |
mLoadingIndicator = (ProgressBar) findViewById(R.id.pb_loading_indicator); | |
getSupportLoaderManager().initLoader(WEATHER_LOADER_ID, null, this); | |
} | |
/** | |
* This method will get the user's preferred location for weather, and then tell some | |
* background method to get the weather data in the background. | |
*/ | |
/** | |
* This method is overridden by our MainActivity class in order to handle RecyclerView item | |
* clicks. | |
* | |
* @param weatherForDay The weather for the day that was clicked | |
*/ | |
@Override | |
public void onClick(String weatherForDay) { | |
Context context = this; | |
Class destinationClass = DetailActivity.class; | |
Intent intentToStartDetailActivity = new Intent(context, destinationClass); | |
intentToStartDetailActivity.putExtra(Intent.EXTRA_TEXT, weatherForDay); | |
startActivity(intentToStartDetailActivity); | |
} | |
/** | |
* This method will make the View for the weather data visible and | |
* hide the error message. | |
* <p> | |
* Since it is okay to redundantly set the visibility of a View, we don't | |
* need to check whether each view is currently visible or invisible. | |
*/ | |
private void showWeatherDataView() { | |
/* First, make sure the error is invisible */ | |
mErrorMessageDisplay.setVisibility(View.INVISIBLE); | |
/* Then, make sure the weather data is visible */ | |
mRecyclerView.setVisibility(View.VISIBLE); | |
} | |
/** | |
* This method will make the error message visible and hide the weather | |
* View. | |
* <p> | |
* Since it is okay to redundantly set the visibility of a View, we don't | |
* need to check whether each view is currently visible or invisible. | |
*/ | |
private void showErrorMessage() { | |
/* First, hide the currently visible data */ | |
mRecyclerView.setVisibility(View.INVISIBLE); | |
/* Then, show the error */ | |
mErrorMessageDisplay.setVisibility(View.VISIBLE); | |
} | |
/** | |
* This method uses the URI scheme for showing a location found on a | |
* map. This super-handy intent is detailed in the "Common Intents" | |
* page of Android's developer site: | |
* | |
* @see <a"http://developer.android.com/guide/components/intents-common.html#Maps"> | |
* <p> | |
* Hint: Hold Command on Mac or Control on Windows and click that link | |
* to automagically open the Common Intents page | |
*/ | |
private void openLocationInMap() { | |
String addressString = "1600 Ampitheatre Parkway, CA"; | |
Uri geoLocation = Uri.parse("geo:0,0?q=" + addressString); | |
Intent intent = new Intent(Intent.ACTION_VIEW); | |
intent.setData(geoLocation); | |
if (intent.resolveActivity(getPackageManager()) != null) { | |
startActivity(intent); | |
} else { | |
Log.d(TAG, "Couldn't call " + geoLocation.toString() | |
+ ", no receiving apps installed!"); | |
} | |
} | |
@Override | |
public boolean onCreateOptionsMenu(Menu menu) { | |
/* Use AppCompatActivity's method getMenuInflater to get a handle on the menu inflater */ | |
MenuInflater inflater = getMenuInflater(); | |
/* Use the inflater's inflate method to inflate our menu layout to this menu */ | |
inflater.inflate(R.menu.forecast, menu); | |
/* Return true so that the menu is displayed in the Toolbar */ | |
return true; | |
} | |
@Override | |
public boolean onOptionsItemSelected(MenuItem item) { | |
int id = item.getItemId(); | |
if (id == R.id.action_refresh) { | |
invalidateData(); | |
getSupportLoaderManager().restartLoader(WEATHER_LOADER_ID, null, this); | |
return true; | |
} | |
if (id == R.id.action_map) { | |
openLocationInMap(); | |
return true; | |
} | |
return super.onOptionsItemSelected(item); | |
} | |
private void invalidateData() { | |
mForecastAdapter.setWeatherData(null); | |
} | |
public static class WeatherLoader extends AsyncTaskLoader<String[]> { | |
private String[] mWeatherData = null; | |
private WeakReference<MainActivity> mActivity; | |
private static final String TAG = WeatherLoader.class.getSimpleName(); | |
WeatherLoader(final MainActivity activity) { | |
super(activity.getApplicationContext()); | |
mActivity = new WeakReference<>(activity); | |
} | |
private MainActivity getActivity() { | |
return mActivity.get(); | |
} | |
@Override | |
protected void onStartLoading() { | |
if (mWeatherData != null) { | |
deliverResult(mWeatherData); | |
} else { | |
if (getActivity() != null) | |
getActivity().mLoadingIndicator.setVisibility(View.VISIBLE); | |
else { | |
Log.e(TAG, "Failed to retrieve activity reference"); | |
Toast.makeText(getContext(), "Loading . . .", Toast.LENGTH_LONG).show(); | |
} | |
forceLoad(); | |
} | |
} | |
@Override | |
public String[] loadInBackground() { | |
String locationQuery = SunshinePreferences.getPreferredWeatherLocation(getContext()); | |
URL weatherRequestUrl = NetworkUtils.buildUrl(locationQuery); | |
try { | |
String jsonWeatherResponse = NetworkUtils.getResponseFromHttpUrl(weatherRequestUrl); | |
return OpenWeatherJsonUtils.getSimpleWeatherStringsFromJson( | |
getContext(), jsonWeatherResponse | |
); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
@Override | |
public void deliverResult(String[] data) { | |
mWeatherData = data; | |
super.deliverResult(data); | |
} | |
} | |
@Override | |
public Loader<String[]> onCreateLoader(int id, final Bundle args) { | |
return new WeatherLoader(this); | |
} | |
@Override | |
public void onLoadFinished(Loader<String[]> loader, String[] data) { | |
mLoadingIndicator.setVisibility(View.INVISIBLE); | |
mForecastAdapter.setWeatherData(data); | |
if (null != data) { | |
showWeatherDataView(); | |
} else { | |
showErrorMessage(); | |
} | |
} | |
@Override | |
public void onLoaderReset(Loader<String[]> loader) { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment