I'm doing a Udacity Android Basics course and am trying to change my app to use a Loader rather than AsyncTask. My app uses a locationListener to generate a custom URL that is then passed to the loader, loader calls ChargePointLoader class which initates the HTTP request and returns a list of chargePoints, which should populate the adapter once loaded.
However nothing appears on my screen. Do I have the order of my loader wrong or is the locationListener interfering with the loader? Thanks for any help
Here is a link to the old Async Project: https://github.com/Kovah101/ChargeMyCarBareBones/blob/master/app/src/main/java/com/example/android/chargemycar/MainActivity.java
Here is my Main activity
public class MainActivity extends AppCompatActivity implements LoaderCallbacks<List<ChargePoint>> {
public static final String LOG_TAG = MainActivity.class.getName();
public static double myLat;
public static double myLong;
private static String ChargePoint_REQUEST_URL = "http://chargepoints.dft.gov.uk/api/retrieve/registry/postcode/SW15+5QS/dist/7/format/json/limit/10";
private ChargePointAdapter adapter;
private LocationManager locationManager;
private LocationListener locationListener;
private static final int CHARGEPOINT_LOADER_ID = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Find a reference to the {@link ListView} in the layout
final ListView chargePointListView = (ListView) findViewById(R.id.list);
// Create a ChargingPointAdapter, whose data source is a list of ChargePoints, which creates listview items for each item
adapter = new ChargePointAdapter(this, new ArrayList<ChargePoint>());
//possible error with order of loaders or inside listener
final LoaderManager loaderManager = getLoaderManager();
locationManager = (LocationManager) this.getSystemService(LOCATION_SERVICE);
locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
//set lat & long variables
myLat = location.getLatitude();
myLong = location.getLongitude();
String myLatString = Double.toString(myLat);
String myLongString = Double.toString(myLong);
//test with toast
Context context = getApplicationContext();
CharSequence text = " my latitude=" +myLatString +"\nmy longitude=" +myLongString ;
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
//create request URL using live location
ChargePoint_REQUEST_URL = "http://chargepoints.dft.gov.uk/api/retrieve/registry/lat/" +myLat + "/long/" +myLong +"/dist/10/format/json/limit/10";
// Set the adapter on the {@link ListView}
// so the list can be populated in the user interface
chargePointListView.setAdapter(adapter);
loaderManager.initLoader(CHARGEPOINT_LOADER_ID, null, MainActivity.this );
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
};
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}else{
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 100, locationListener);
//time in milliseconds
//distance in meters
}
// On click take to maps
chargePointListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String myLatString = Double.toString(myLat);
String myLongString = Double.toString(myLong);
ChargePoint currentChargePoint = (ChargePoint) chargePointListView.getItemAtPosition(position);
double destinationLatitude = currentChargePoint.getLatitude();
double destinationLongitude = currentChargePoint.getLongitude();
String destLatString = Double.toString(destinationLatitude);
String destLongString = Double.toString(destinationLongitude);
//create uri for map intent
String url = "http://maps.google.com/maps?saddr="+myLatString+","+myLongString+"&daddr="+destLatString+","+destLongString+"&travelmode=driving";
Intent mapIntent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(url));
mapIntent.setPackage("com.google.android.apps.maps");
if (mapIntent.resolveActivity(getPackageManager()) != null) {
startActivity(mapIntent);
}
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)== PackageManager.PERMISSION_GRANTED){
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 25, locationListener);
//time in ms, distance in meters
}
}
}
@Override
public Loader<List<ChargePoint>> onCreateLoader(int i, Bundle bundle) {
return new ChargePointLoader(this, ChargePoint_REQUEST_URL);
}
@Override
public void onLoadFinished(Loader<List<ChargePoint>> loader, List<ChargePoint> chargePoints) {
// Clear the adapter of previous data
adapter.clear();
//check for null charge point list, return early if that is the case, if there is a valid list then add to the adapter
if (chargePoints != null && !chargePoints.isEmpty()){
adapter.addAll(chargePoints);
}
}
@Override
public void onLoaderReset(Loader<List<ChargePoint>> loader) {
// TODO: Loader reset, so we can clear out our existing data.
adapter.clear();
}
}
Loaders are now deprecated, use Android Architecture Components (LiveData and ViewModel) with your old AsyncTask. It works more efficiently, faster and cleaner than Loaders. It also makes your AsyncTask to be lifecycle aware. Lifecycle aware in a sense that your network request will not be made again after once no matter how many times you call onCreate() because it automatically caches downloaded data. Configuration changes won't affect your app. For more information on using AsyncTask with LiveData and ViewModel, visit https://medium.com/androiddevelopers/lifecycle-aware-data-loading-with-android-architecture-components-f95484159de4