Creating my first app,
Looking for locations using the GoogleApi and the callback is working as intended. I use the location and it gets put into a database, onDestroy() I am closing the DB connection. But now I occasionally get a stack trace saying
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase
I added a removeLocationUpdates() right before the closeDB call, and that MOSTLY solved the issue, but it still happens from time to time. It appears that I may still be getting a rogue callback after removeLocationUpdates and closeDB have been called.
Is this a typical delay? could a 500ms delay between the removeLocUpdates and the closeDB fix the issue? I'd imagine there'd be a better way, or that maybe I am simply doing something wrong with the removeLocUpdates. But the stack trace seems pretty clear that I am getting a callback as the trace shows
onLocationChanged
part way down the stack
EDIT to add Code:
onCreate():
openDB();
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
//End Google Location API implementation
locationListener = new com.google.android.gms.location.LocationListener() {
#Override
public void onLocationChanged(Location location) {
//newestLocation = location;
//bestLocation = bestLocation();
//todo maybe come back and reimplement bestLocation() if needed
bestLocation = location;
getCode(null);
saveCode(null);
}};
onDestroy()
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, locationListener);
}
}
closeDB();
onConnected()
public void onConnected(#Nullable Bundle bundle) {
CharSequence text = "Google API Connected";
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
LocationRequest locationRequest = new LocationRequest()
.setFastestInterval(500)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setMaxWaitTime(3000)
.setInterval(1000);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_FINE_LOCATION},10);
return;
}else {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, locationListener);
}
return;
}
I appreciate your help on the matter
Related
I implemented into my app a location service that uses FusedLocationProviderClient in order to track my position every 1 second when tracking is started.
The tracking works correctly, but after about 10-15 minutes of active tracking, the update becomes slower, it updates the position every 3 seconds, and after 20-30 minutes..about 5 seconds. The app, after this time, becomes jerky.
The position, altitude, bearing and time are recorded every second into a database (Room database) that remains opened until the stop of tracking.
Do someone had a similar issue? Can it be connected to the phone memory due to many data saved or to a sort of "saving mode" made independently by FusedLocationProviderClient? I am trying that on a Samsung S9+ that should not have memory issues. Is there a lighter way?
I post here below the code for the location service:
public class LocationService extends Service {
FusedLocationProviderClient fusedLocationProviderClient;
LocationCallback locationCallback;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
Intent intent = new Intent("ACT_LOC");
intent.putExtra("latitude", locationResult.getLastLocation().getLatitude());
intent.putExtra("longitude", locationResult.getLastLocation().getLongitude());
intent.putExtra("altitude", locationResult.getLastLocation().getAltitude());
intent.putExtra("precision", locationResult.getLastLocation().getAccuracy());
intent.putExtra("speed", locationResult.getLastLocation().getSpeed());
intent.putExtra("time", locationResult.getLastLocation().getTime());
intent.putExtra("bearing",locationResult.getLastLocation().getBearing());
sendBroadcast(intent);
}
};
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
requestLocation();
return super.onStartCommand(intent, flags, startId);
}
private void requestLocation() {
LocationRequest locationRequest = new LocationRequest();
locationRequest.setInterval(1000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());
}
}
It looks like you have some sort of leaks. Like creating a new subscription to location updates every time you get a location. You can check for memory leaks or add logging to verify that the all code that handles location updates runs exactly once for each new location.
I implemented an app that uses GPS location and so I am dealing with the permissions.
The app runs correctly, all locations are correctly performed.
When I install the application, the first fragment opens correctly and shows to user the request for GPS permission.
Behind that, I added also a dialog that requests to set ON the GPS in order to start the application.
If before first start (after installation) the GPS is set to OFF, when the app starts, it is shown the first fragment on background, the request for GPS permission and then the request to set ON the GPS inside settings.
If before first start (after installation) the GPS is set to ON, when the app starts, it is shown the first fragment on background, the request for GPS permission and app crashes maintaining the request for GPS permission active. If I press OK, I restart the application and all works properly.
Why this issue when GPS on settings is set to ON?
Here below a part of code:
On first fragment, onCreateView I wrote the following code for GPS permission:
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.addApi(LocationServices.API)
.addOnConnectionFailedListener(this)
.addConnectionCallbacks(this)
.build();
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) {
showAlert(getString(R.string.PermessoDisp), getString(R.string.PermessoDispTitle));
} else {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSION_REQUEST_LOCATION);
}
} else {
}
In the onStart() I have:
#Override
public void onStart() {
super.onStart();
if (isLocationEnabled()) {
mGoogleApiClient.connect();
} else {
showAlertGPSSettings();
}
}
where showAlertGPSSettings() is the method that ask user to set the GPS ON in order to use app.
And I have also:
#Override
public void onConnected(#Nullable Bundle bundle) {
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(1000);
locationRequest.setFastestInterval(500);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, this);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
switch (requestCode) {
case MY_PERMISSION_REQUEST_LOCATION:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
}
}
}
When the app crashes (at first start when GPS is set to ON), I have the following error:
java.lang.SecurityException: Client must have ACCESS_FINE_LOCATION permission to request PRIORITY_HIGH_ACCURACY locations.
The row where the error is located, is inside the onConnected method:
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, this);
Any suggestion?
Did you add permission in your manifest?
android.permission.ACCESS_COARSE_LOCATION or android.permission.ACCESS_FINE_LOCATION.
This permissions are needed.
I am trying to get my updated location from my app. however my app does not check this block of code
public void onSuccess(Location location)
{} inside mFusedLocationProviderClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener(){} and returns me null location. Even my device location is turned on and the map shows my location. My code for accessing updated location is below:
protected void onCreate(Bundle savedInstanceState) {
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
}
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
Toast.makeText(this, "Map is ready", Toast.LENGTH_SHORT).show();
if (mLocationPermissionGranted) {
getDeviceLocation();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
mMap.setMyLocationEnabled(true);
}
}
private void getDeviceLocation(){
try {
if(mLocationPermissionGranted) //true in my case
{
mFusedLocationProviderClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// Got last known location. In some rare situations this can be null.
if (location != null) {
moveCamera(new LatLng(location.getLatitude(),location.getLongitude()),17);
// mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(),location.getLongitude()),12),2000,null);
}
else{
Toast.makeText(MapsActivity.this, "Unable to get current location", Toast.LENGTH_SHORT).show();
}
}
});
}
}catch (SecurityException ex){
}
}
I am getting the following exceptions in stacktrace
09-11 15:12:14.525 5864-5864/? E/Zygote: v2
09-11 15:12:14.530 5864-5864/? E/Zygote: accessInfo : 0
09-11 15:12:27.333 5864-5864/com.example.muzammil.maptestingproject E/BoostFramework: BoostFramework() : Exception_1 = java.lang.ClassNotFoundException: Didn't find class "com.qualcomm.qti.Performance" on path: DexPathList[[],nativeLibraryDirectories=[/system/lib, /vendor/lib]]
09-11 15:12:39.327 5864-5864/com.example.muzammil.maptestingproject E/art: The String#value field is not present on Android versions >= 6.0
Everything else is correct including manifest permissions and gradle dependencies. May be any body help me in solving my problem. Thanks in advance.
Fused Location Provider will give the location if at least one client is connected to it. If the client connects, it will immediately try to get a location. If your activity is the first client to connect and you call getLastLocation() that might not be enough time for the first location to come in.
getLastLocation() is suitable where a location is needed immediately or none at all can be used. If you really want to wait for a location it's best to use requestLocationUpdates() and wait for the callback rather than busy waiting in a thread.
Here is the method to get location using requestLocationUpdates()
LocationCallback locationCallback;
LocationRequest locationRequest;
FusedLocationProviderClient fusedLocationClient;
void getLocation() {
locationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(30 * 1000)
.setFastestInterval(5 * 1000);
locationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
//Location received
}
};
fusedLocationClient = LocationServices.getFusedLocationProviderClient(getApplicationContext());
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);
}
And don't forget to remove location updated in onStop or onDestroy
fusedLocationClient?.removeLocationUpdates(locationCallback)
I use FusedLocationProviderClient within a Service.
I would like to "stop" it in a right way.
Is it good to use following code?
#Override
public void onDestroy() {
super.onDestroy();
// Stop Looper of FusedLocationProviderClient
if (locationClient != null) {
locationClient = null;
}
}
And the rest code
FusedLocationProviderClient locationClient;
protected void startLocationUpdates() {
// Create the location request to start receiving updates
mLocationRequest = new LocationRequest();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
// Create LocationSettingsRequest object using location request
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
// Check whether location settings are satisfied
// https://developers.google.com/android/reference/com/google/android/gms/location/SettingsClient
SettingsClient settingsClient = LocationServices.getSettingsClient(this);
settingsClient.checkLocationSettings(locationSettingsRequest);
// new Google API SDK v11 uses getFusedLocationProviderClient(this)
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
locationClient = getFusedLocationProviderClient(this);getFusedLocationProviderClient(this).requestLocationUpdates(mLocationRequest, new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
// do work here
onLocationChanged(locationResult.getLastLocation());
}
}, Looper.myLooper());
}
just call removeLocationUpdates in onDestroy
for requestLocationUpdates, it says:
This call will keep the Google Play services connection active, so make sure to call removeLocationUpdates(LocationCallback) when you no longer need it, otherwise you lose the benefits of the automatic connection management.
just change onDestroy(); to this:
#Override
protected void onDestroy() {
super.onDestroy();
if(locationclient!=null)
locationclient.disconnect();
}
I am trying to get the current location coordinates in longitude and latitude. Here is my code so far:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
MyLocationListener myLocationListener = new MyLocationListener();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, myLocationListener);
}
}
and this class too:
public class MyLocationListener implements LocationListener {
private static final String TAG = "COORDINATES: ";
#Override
public void onLocationChanged(Location location) {
if(location != null){
Log.e(TAG, "Latitude: " + location.getLatitude());
Log.e(TAG, "Longitude: " + location.getLongitude());
}
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
}
When I run the app in emulator, I don't get any log message with coordinates. Any suggestions?
The best approach is to use the latest FusedLocationApi provided by Google Play Services library.
If you want to use the old approach, that is fine but you might not get very accurate results.
Either way, make sure you have enabled internet permission, either COARSE_LOCATION or FINE_LOCATION or both in your android manifest.
Also, if you have android 6.0, remember you must request runtime permissions or it won't work for you!
I answered a similar question yesterday and you can find here -which works;
There is also a link to a sample code for FusedLocationApi here.
I hope this helps you and good luck!
UPDATE
You can add Google Play Services to your build.gradle like this:
compile 'com.google.android.gms:play-services:9.2.'
But if you are only interested in one service like location, you can be specific:
compile 'com.google.android.gms:play-services-location:9.2.1'
NOTE
I would highly discourage you from getting user location on your UI thread because it will destroy user experience in the long run! Use a separate thread!!
You have to mimic the location in Emulator. You can do that by accessing the Android Device Manager and Select Emulator Control tab and send the locations to Emulator.