1.In My application ,I want to track User Current Location Every 1 minute then Save in Local Database.Not getting Proper location Some times ,when user is Walking.
2.For finding location ,I want to use Google play Services API.
My Need:
Background Service Should get the location, save in local Database every minute.Even Application is Closed.
also with low Battery Usage.
I tried Some codes some methods are deprecated and not Working.
Please Suggest me any solution.
This is one of the way you can achieve it, make your service class like below
here,
with this ScheduledThreadPoolExecutor your service will be on in background
and you will need LocationManager
your SERVICE.class extend Service implements LocationListener, Runnable
private LocationManager mgr = null;
private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
#Override
public void onCreate() {
//check gps is on/off
//get locationmanager
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//check if internet is on/off
setBeforeDelay15();
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onLocationChanged(Location location) {
//re-execute at time you need with **scheduleAtFixedRate**
}
#Override
public void run() {
//here get the best location from two ways 1>gps and 2>network provider
//once you get this insert to database
//set delay then request call method again
}
#Override
public void onDestroy() {
//if locationmanager is not null then remove all updates
}
private void setDelay15() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Log.e("srop self by", "delay15");
startWork();
}
}, 300000);//120000
}
private void startWork() {
//check here for 1>gps and 2>network provider
//get location and save it
}
use this , when you require location in your application.
Related
I have following requirement in my app.
1. I select some files & click Upload button. On clicking Upload button, the app exits. (I am enqueueing the request & finishing the activity).
2. Once the app exits, these files need to get synced to server in background.
3. Also after certain time duration (I have set 16min interval), in background, I need to check if there is unsynced data, sync it in background.
4. If user is offline, the unsynced data should get synced in background once network connectivity is established.
I have used WorkManager's Periodic Work Request to achieve this.
But on testing it on my Asus Zenfone3, I observed that :
1. If my device goes into sleep mode, the doWork() does not get execute after that.
If I turn off mobile data & then turn it on, the doWork() does not get executed immediately.
I have set 1 constraint : .setRequiredNetworkType(NetworkType.CONNECTED). It gets executed only after its interval is completed. I need immediate sync on network.
Also sometimes, clicking on upload button does not immd call doWork().
Is WorkManager (2.3.2) the right approach I am following. Any guideline to achieve above req. will be appreciated.
WorkManager is the preferred solution on Android for deferrable tasks. If you want an immediate action, you may want to look into implementing the logic you want in a Foreground Service.
Regarding what happens when your device goes into doze mode, WorkManager tries to minimize battery consumption and respect it. Again, if you need to have your tasks to be executed even when the device is supposed to go into doze mode, WorkManager is not the right solution. But you need to have a good reason to justify this behavior as it is going to have a negative impact on battery life.
To be able to understand what is happening with the Asus Zenfone3, it would be helpful to see how you enqueue the WorkRequest and WorkManager's logs.
If you read android documentation work manager is not a good way to schedule recurring tasks and when the device goes to sleep mode the thread work manager is working on is also put to sleep to conserve battery try using handler instead.
https://developer.android.com/reference/android/os/Handler
in the code i have posted i have used handler and firebase jobdispatcher to log user location on fixed interval of 30 seconds and it works even when device is in sleep mode
//file with handler and firebase job dispatcher
public class GetterService extends JobService {
private HandlerThread handlerThread = new HandlerThread("HandlerThread");
private Handler threadHandler;
int delay = 30*1000;
Runnable runnable;
LocationManager locationManager;
#Override
public boolean onStartJob(#NonNull com.firebase.jobdispatcher.JobParameters job) {
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
handlerThread.start();
threadHandler = new Handler();
threadHandler.postDelayed(runnable= () -> {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
Location repeatedLocations = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
Log.i("location logger", "getting Longitude as "+ repeatedLocations.getLongitude() +" getting Latitide as "+repeatedLocations.getLatitude());
threadHandler.postDelayed(runnable,delay);
},delay);
return false;
}
#Override
public boolean onStopJob(#NonNull com.firebase.jobdispatcher.JobParameters job) {
return false;
}
}
//file with location getter
public class LocationGetter extends AppCompatActivity implements LocationListener {
LocationManager locationManager;
#BindView(R.id.latitudeVal)
TextView latitudeVal;
#BindView(R.id.longitudeVal)
TextView longitudeVal;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 2);
} else {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1, 1, this);
}
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher
(new GooglePlayDriver(getApplicationContext()));
Job notificationJob = dispatcher.newJobBuilder()
.setService(GetterService.class)
.setRecurring(true).setTag("jid")
.setLifetime(Lifetime.UNTIL_NEXT_BOOT)
.setTrigger(Trigger.executionWindow(1, 10000))
.setReplaceCurrent(false)
.build();
dispatcher.mustSchedule(notificationJob);
}
#OnClick(R.id.ping)
public void onPingClick() {
}
#Override
public void onLocationChanged(Location location) {
latitudeVal.setText("" + location.getLatitude() + "");
longitudeVal.setText("" + location.getLongitude() + "");
Log.i("dfjh", "" + location.getLongitude() + " " + location.getLatitude());
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
I've read the Google doc and this post notably the interesting #bendaf's answer, about managing Location settings, and everything works well.
Anyway one problem remains if the user first decide to not use its position and then decide later to activate it, the application is not trigged by this action so I don't know that I can request for periodic updates.
What did I miss here?
You can provide an AlertDialog every time user launches an app so that user can enable the location. But this will be a bit annoying because every time you have to deny to same thing.
Alternatively, you can use Preferences so that user can enable/disable the location
There is another better way to do it. Use LocationListener. It is for the sole purpose that you want to achieve.
public class XYZ Activity implements LocationListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000,10,this);
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
}
}
I think this is the right thing that you are looking for. It's the event way.
Happy Coding :)
#fralbo Although this thread is a tiny bit old, I just wanted to provide a solution for you, as I recently had to do something similar myself.
I would recommend implementing a BroadcastReceiver into your App, with the intent filter PROVIDERS_CHANGED..
This will trigger every time the Location/GPS Provider state changes - and you can use an if statement inside of the BroadcastReceiver's onReceive method, to determine whether or not your required conditions are met, and proceed accordingly.. For example, inside the onReceive method of the BroadcastReceiver, you can determine whether the PROVIDERS_CHANGED event has made the GPS become available (and to what degree) - and if it now meets the needs of your App, you can then call whichever Method within your App that is responsible for starting the required calls to the GPS engine, etc.
Here's an example of what the code might look like:
public class LocationReceiver extends BroadcastReceiver {
private final static String TAG = "[ LocationReceiver ]:";
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "PROVIDERS_CHANGED has been detected - Firing onReceive");
// Retrieve the LocationManager
LocationManager locationManager =
(LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
// Provide boolean references for Location availability types
boolean isGpsEnabled;
boolean isNetworkEnabled;
// Provide values to retrieve the Location availability
isGpsEnabled =
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
isNetworkEnabled =
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
Log.i(TAG, "Detected - GPS: "+ isGpsEnabled + "NET: "+ isNetworkEnabled);
// If (for example), the GPS is ENABLED, start one of your Activities, etc.
if (isGpsEnabled) {
Intent startYourActivity =
new Intent(context.getApplicationContext(), YourActivity.class);
context.startActivity(startYourActivity);
}
}
}
I hope this helps! Better late than never :)
Happy coding!
*Please have a look at the below written source code lines and suggest:-*
In the below mentioned code lines I am trying to request for GPS and Network location in every 60 seconds.
if (gps_enabled)
{
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 0, locationListenerGps);
}
if (network_enabled)
{
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 60000, 0,
locationListenerNetwork);
}
Below mentioned is the code for location change listeners:-
LocationListener locationListenerGps = new LocationListener()
{
public void onLocationChanged(Location location)
{
mCurrentGpsLocation = location;
String userCurrentGpsLocation = findUserAddress(mCurrentGpsLocation, mPreviousGpsLocation);
mPreviousGpsLocation = location;
for(int i = 0, size = mLocationUpdateListeners.size(); i<size; i++)
{
LocationUpdateListener listener = mLocationUpdateListeners.get(i);
listener.recieveGpsNotification(userCurrentGpsLocation);
}
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
LocationListener locationListenerNetwork = new LocationListener()
{
public void onLocationChanged(Location location)
{
mCurrentNetworkLocation = location;
String userCurrentNetworkLocation =findUserAddress(mCurrentNetworkLocation, mPreviousNetworkLocation);
mPreviousNetworkLocation = location;
for(int i = 0, size = mLocationUpdateListeners.size(); i<size; i++)
{
LocationUpdateListener listener = mLocationUpdateListeners.get(i);
listener.recieveNetworkNotification(userCurrentNetworkLocation);
}
}
public void onProviderDisabled(String provider) {
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
And on receiving location change event for GPS and Network, I simply call my listeners recieveGpsNotification() and recieveNetworkNotification() methods respectively.
Logic of recieveGpsNotification() are as follows :-
public void recieveGpsNotification(String gpsLocation)
{
sendEmail();
}
**Problems :-
1) I expect call to sendMail() should come after every 60 sec but I am receiving notifications very frequently and after 4-5 notifications my application crashes. Please help if you see any error in implementation logic.
2) Will i also receive notifications for GPS even when my activity is in pause state or in stopped state, because i want to receive notification even when my activity is in background or it is stopped?
Additional Query :-
Whenever android framework provides us with GPS location updates, every time notification come through a different thread or is it the thread that request for notifications?
Thanks in Advance.
**
Try adding a min distance to each update. Also consider grouping the updates together for your sendEmail() method
Move your Location Listener logic into a Service that runs in the background. This will keep the updates coming. However, be aware that GPS updates are awful for battery life and users will not be happy to see GPS trying to get a lock all the time.
I believe those updates happen on the thread in which they are declared. So you should move your findUserAddress() method and other complex operations to a different thread.
Also, You should only use one method to get GPS updates. The normal use case is to try GPS Adapter, if not enabled or preset, fall through to use the Network listener. Since you have both you will be getting both updates on different intervals.
In my code i'm using an IntentService to listen to location updates (either GPS or network updates) and this IntentService is triggered when an event is received, so it is started with startService() from any activity.
public class AddLocationService extends IntentService implements LocationListener {
/*My code here*/
}
#Override
protected void onHandleIntent(Intent intent) {
if(getOldLoc() == null)
{
//Get a new location
this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, TIME_INTERVAL_GPS, 0, this);
this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, TIME_INTERVAL_GPS, 0, this);
Log.d(AddLocationService.TAG, "Network listener started");
this.time_start_listening = System.currentTimeMillis();
mTimerThread mTimerRunnable = new mTimerThread();
this.timerThread = new Thread(mTimerRunnable);
this.timerThread.start();
}
else
/*REUSE OLD LOCATION*/
}
Now my problem is : When two events start this IntentService and the second starts it while the first one is still requesting for updates, I will like the second one to wait until first one is fully finished (location found OR timer thread finishes).
However whenever the IntentService is executed a second time (first instance still running), it prints me the log and does as it was executing in parallel.
However I thought that the main goal of IntentService was that it is something sequential so a second intent would have to wait until first one is done...
Did I missunderstood something ?
It appears that your onHandleIntent method is not blocking the thread it is executing on, so it will return quickly and allow the second intent to be processed. Not only that, but any callbacks from the LocationManager to that thread are unlikely to be processed as the background thread is likely to be killed when onHandleIntent is finished.
If you really want to use IntentService to manage your intent queue then you will need to do your location handling on its own thread, and join the IntentService thread to the location thread whilst it is waiting for the location callback.
Heres a bit of code that demonstrates the idea:
public class TestService extends IntentService {
private static final String TAG = "TestService";
private Location mLocation = null;
public TestService() {
super(TAG);
}
#Override
public void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent");
if (mLocation == null) {
Log.d(TAG, "launching location thread");
LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
LocationThread thread = new LocationThread(locationManager);
thread.start();
try {
thread.join(10000);
} catch (InterruptedException e) {
Log.d(TAG, "timeout");
return;
}
Log.d(TAG, "join finished, loc="+mLocation.toString());
} else {
Log.d(TAG, "using existing loc="+mLocation.toString());
}
}
private class LocationThread extends Thread implements LocationListener {
private LocationManager locationManager = null;
public LocationThread(LocationManager locationManager) {
super("UploaderService-Uploader");
this.locationManager = locationManager;
}
#Override
public void run() {
Log.d(TAG, "Thread.run");
Looper.prepare();
this.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
Looper.loop();
}
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
Log.d(TAG, "onLocationChanged("+location.toString()+")");
mLocation = location;
Looper.myLooper().quit();
}
#Override
public void onProviderDisabled(String arg0) {
}
#Override
public void onProviderEnabled(String arg0) {
}
#Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
}
}
}
Of interest in there is the Looper that runs a message loop on the thread (to allow handling of the callbacks).
Given the effort required to do this with IntentService it might be worthwhile investigating deriving from Service instead and managing your own intent queue.
onHandleIntent is in it's own thread already. You don't (shouldn't) create on in there. It's all handled by IntentService for you.
Thanks a million, that is exactly what I needed to handle the location requests.
Thank you for explanations and making it clear for me, I wasn't very familiar with all the looper concept, now I understand it better !
In case someone need the same kind of thing, don't forget to stop the thread looper if your location thread is not stopping naturally (end of time on join(millis)), by adding this in onHandleIntent() :
if(thread.isAlive())
{
thread.onThreadStop();
try{
thread.interrupt();
}catch (Exception e) {
Log.d(TAG, "Exception on interrupt: " + e.getMessage());
}
}
after thread.join(yourTime), so for example if you didn't find any location update you still stop the thread after a certain time. And on method onThreadStop() :
/*We remove location updates here and stop the looper*/
public void onThreadStop()
{
this.locationManager1.removeUpdates(this);
handleLocationChange(AddLocationService.this.currentBestLocation);
Looper.myLooper().quit();
}
However I thought I saw my two intents being treated the first time I ran this code, but now only the first one is treated when I have multiple intents while still requesting location updates.
My method onHandleIntent() seems to execute correctly, stops the thread after the time specified and even displays the very last Log (last statement of the method) but the second intent is not executed...
Would you have any idea why ?
I have been stuck on this problem for quite some time. I am working on an app that uses location quite extensively in several different Activities. Every example I have found uses a separate LocationListener in every Activity. This is not feasible in my situation.
I am wondering what is the most efficient way to keep track of the user's location across several activities. Right now I have created a service that implements LocationListener and uses a broadcast to update static lat and long fields in an Activity base class. This means that the service is always running, which isn't ideal. But if I shut down the service and restart it only when I need it, it takes time to get a good location. I need the location data in the Activity's onCreate(). It's the same if I try to implement it in the activity base class. If I constantly register the listener in onResume/onCreate and unregister it in onPause(), it takes too much time to start receiving updates. I also tried to create a service that I could bind to, so it only starts when I need a location. But I have the same problem, it takes too long to bind to the service and start getting updates.
The service that I am using now works but from everything I've read, I shouldn't be using a constantly running service for trivial things like this. But the whole point of the app is to provide relevant data based on the user's current location. So I have a service that just runs in the background and provides updates periodically. The one major problem that has caused me to reexamine the design at this point is that I recently discovered that onProviderEnabled() doesn't get called if the user starts the app without GPS turned on and then subsequently enables it. In this scenario the app has no way of recognizing that GPS was enabled so it can start listening for updates.
I thought I understood LocationManager and LocationListener from looking at the examples but I can't seem to apply it to this situation where I need location data in multiple Activities. Any help or advice would be greatly appreciated.
The way that I would typically implement this requirement is using a bound Service implementation, like the one in the Local Service Sample in the SDK Documentation. Obviously you're familiar with the advantage of the Service allowing you to create all the location code only once.
Accessing the Service through Bindings allows the Service to start and stop itself so it isn't running when your application isn't in the foreground (it will die as soon as no more Activities are bound). The key, IMO, to making this work well is to BIND the service in onStart() and UNBIND in onStop(), because those two calls overlap as you move from one Activity to another (Second Activity Starts before the First one Stops). This keeps the Service from dying when moving around inside the app, and only lets the service die when the entire application (or at least any part interested in location) leaves the foreground.
With Bindings, you don't have to pass the Location data in a Broadcast, because the Activity can call methods directly on the Service to get the latest location. However, a Broadcast would still be advantageous as a method of indicating WHEN a new update is available...but this would just become a notifier to the listening Activity to call the getLocation() method on the Service.
My $0.02. Hope that Helps!
I got the same problem and I tried to solve it with the good answer of Devunwired, but I had some troubles. I couldn't find a way to stop the service and when I finished my app the GPS-module was still running. So i found another way:
I wrote a GPS.java class:
public class GPS {
private IGPSActivity main;
// Helper for GPS-Position
private LocationListener mlocListener;
private LocationManager mlocManager;
private boolean isRunning;
public GPS(IGPSActivity main) {
this.main = main;
// GPS Position
mlocManager = (LocationManager) ((Activity) this.main).getSystemService(Context.LOCATION_SERVICE);
mlocListener = new MyLocationListener();
mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener);
// GPS Position END
this.isRunning = true;
}
public void stopGPS() {
if(isRunning) {
mlocManager.removeUpdates(mlocListener);
this.isRunning = false;
}
}
public void resumeGPS() {
mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener);
this.isRunning = true;
}
public boolean isRunning() {
return this.isRunning;
}
public class MyLocationListener implements LocationListener {
private final String TAG = MyLocationListener.class.getSimpleName();
#Override
public void onLocationChanged(Location loc) {
GPS.this.main.locationChanged(loc.getLongitude(), loc.getLatitude());
}
#Override
public void onProviderDisabled(String provider) {
GPS.this.main.displayGPSSettingsDialog();
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
}
}
This class is used in every Activity which needs the GPS coordinates. Every Activity has to implement the following Interface (needed for the communication):
public interface IGPSActivity {
public void locationChanged(double longitude, double latitude);
public void displayGPSSettingsDialog();
}
Now my main Activity looks like that:
public class MainActivity extends Activity implements IGPSActivity {
private GPS gps;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gps = new GPS(this);
}
#Override
protected void onResume() {
if(!gps.isRunning()) gps.resumeGPS();
super.onResume();
}
#Override
protected void onStop() {
gps.stopGPS();
super.onStop();
}
public void locationChanged(double longitude, double latitude) {
Log.d(TAG, "Main-Longitude: " + longitude);
Log.d(TAG, "Main-Latitude: " + latitude);
}
#Override
public void displayGPSSettingsDialog() {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
}
and a second one like that:
public class TEST4GPS extends Activity implements IGPSActivity{
private GPS gps;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.gps = new GPS(this);
}
#Override
public void locationChanged(double longitude, double latitude) {
Log.d("TEST", "Test-Longitude: " + longitude);
Log.d("TEST", "Test-Latitude: " + latitude);
}
#Override
protected void onResume() {
if(!gps.isRunning()) gps.resumeGPS();
super. onResume();
}
#Override
protected void onStop() {
gps.stopGPS();
super.onStop();
}
#Override
public void displayGPSSettingsDialog() {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
}
It's not as beautiful as the solution of Devunwired, but it works for me.
cya
You could have your service writing your lat & long coords to an sqlite db on change that way you dont have the service binding overhead and your coords will be accessible across all your activities.
Perhaps in your main activity you can check the GPS status and if it is turned off you can prompt the user to enable it. Once GPS is enabled start your service.
Here is a very simple and straightforward way to do it:
In your main activity:
private boolean mFinish;
#Override
public void onBackPressed() {
mFinish = true;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFinish = false;
...
}
#Override
public void onPause(){
super.onPause();
if (mFinish) {
// remove updates here
mLocationManager.removeUpdates(mLocationListener);
}
}
This will only remove the updates listener if you press Back in your main activity, but otherwise stay on.
It's not the best way to do it, but it's a simple copy-paste solution.
Make sure you don't call location listener if it's already running (for example on screen rotate), otherwise you'll end up with multiple listeners running in the background.