onHandleIntent not firing with location updates - java

I am trying to listen to location changes using the Play Services Location API.
What I'm trying to do is go for the background app approach and get the updates using a PendingIntent. However, the onHandleIntent() function does not get called at all.
I didn't find a single source of comprehensive documentation regarding this approach. Can you tell me is there something I'm doing wrong?
public class LocationCollector extends IntentService implements GoogleApiClient.ConnectionCallbacks
, GoogleApiClient.OnConnectionFailedListener {
private Context mContext;
private GoogleApiClient mLocationClient;
public LocationCollector(Context context){
super("LocationCollector");
mContext = context;
mLocationClient = new GoogleApiClient.Builder(mContext)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
public void start(){
mLocationClient.connect();
}
public void stop(){
mLocationClient.disconnect();
}
#Override
public void onConnected(Bundle bundle) {
LocationRequest request = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
Intent locationIntent = new Intent(mContext, LocationCollector.class);
PendingIntent locationPendingIntent = PendingIntent.getService(mContext, 1, locationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingResult<Status> result = LocationServices.FusedLocationApi.requestLocationUpdates(mLocationClient, request, locationPendingIntent);
}
#Override
public void onConnectionSuspended(int i) {
Log.d("Location","Api connection suspended");
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d("Location","Api connection failed");
}
#Override
protected void onHandleIntent(Intent intent) {
Location location = intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
if(location != null){
String time= intent.getStringExtra("time");
Toast.makeText(mContext,location.toString(),Toast.LENGTH_SHORT).show();
}
}
}

Can you tell me is there something I'm doing wrong?
First, a Service is a Context. You do not need to — or even want to — pass in some Context to it.
Second, your constructor should never be used. Replace it with a zero-argument constructor.
Third, start() and stop() are not lifecycle methods on an IntentService, and so I am not quite certain what you are expecting will call them. That, coupled with the previous problem, means nothing will really happen with this service.
Saying that you want an IntentService to handle the locations, via an onHandleIntent() method, is reasonable. However, something outside of that service is going to need to do the connecting and disconnecting, or you are going to need a substantially more complex service.

Related

Create service to detect any action from the user

I'm trying to create a service where I want to detect something about user, let's say when user lays the device on a table, the thing is that I have that action detected but I have it on a MainActivty and I want it to put on Service.
The thing is that on my MainActivity() I had my registerAction() and on my onResume() were called and in onPause() I call the unregisterListener() from my sensor, as well I have a HandlerThread where I start it on my onCreate() how do I change it to Service? Would be a problem? I see that there aren't the same methods...
I've created my Service and I've got :
public class MyService extends Service {
public MyService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
super.onCreate();
Log.d("CREATE","ONCREATE");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("DESTROY","ONDESTROY");
}
}
Also my MainActivity I have put implements SensorEventListener.
A skeleton of my class is :
public class MainActivity extends Activity implements SensorEventListener {
private HandlerThread mSensorThread;
private SensorManager mSensorManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensorThread = new HandlerThread("sensor_thread");
mSensorThread.start();
}
private void registerSensorListener() {
mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST, new Handler(mSensorThread.getLooper()));
}
#Override
public void onSensorChanged(SensorEvent event) {
//DO stuff
if (isLayed()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.d("LAY","LAYLAY");
}
});
mSensorManager.unregisterListener(this);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private boolean isLayed() {
return stuff;
}
#Override
protected void onResume() {
super.onResume();
registerSensorListener();
}
#Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
}
EDIT
I'm using szamani20 code, but I'm having problems with runOnUiThread because I can not call from my Service also, I'm having this issue
java.lang.RuntimeException: Unable to start service com.example.developer.qwe.MyService#d8c613b with null: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Intent.getAction()' on a null object reference
First of all you need to decide whether you want the user to be aware of your running service or not. Take a review on Background Execution Limits in android Oreo:
To improve the user experience, Android 8.0 (API level 26) imposes limitations on what apps can do while running in the background.
So considering your case where it seems there are lots of work to do in many situations, it would be a better approach to use a foreground service. As android document says about foreground services:
A foreground service is a service that the user is actively aware of and is not a candidate for the system to kill when low on memory. A foreground service must provide a notification for the status bar, which is placed under the Ongoing heading. This means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
Since you mentioned that you have the action detected I won't enter that part of your code. So you need to create a subclass of Service as you did and use the startService method to get it's onCreate called. One thing you need to notice is that the onCreate method of service is called once you call startService on that service for the first time, no matter how many times you call startService again the onCreate method won't get called and only the onStartCommand get called. We use that fact alongside that you could provide a string action within your intent to properly register and unregister your listener.
In MainActivity.java:
String action = "start"; // Or to unregister listener "stop"!
final Intent intent = new Intent(this, MyService.class);
intent.setAction(action);
startService(intent);
and then in MyService.java:
#Override
public void onCreate() {
super.onCreate();
// Do initialization or whatever here (executed once per service lifecycle)
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals("start")) {
// Register your listener or whatever
showForegroundNotification();
}
if (intent.getAction().equals("stop")) {
// Unregister your listener or whatever
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
private void showForegroundNotification() {
Intent myServiceNotificationIntent = new Intent(this, MainActivity.class);
myServiceNotificationIntent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent
.getActivity(this, MY_SERVICE_REQUEST_CODE,
myServiceNotificationIntent, MY_SERVICE_FLAG);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle(MY_SERVICE_NOTIFICATION_CONTENT_TITLE)
.setTicker(MY_SERVICE_NOTIFICATION_TICKER)
.setContentText(MY_SERVICE_NOTIFICATION_CONTENT_TEXT)
.setSmallIcon(R.drawable.ic_whatever)
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(MY_SERVICE_NOTIFICATION_ID, notification);
}
Finally don't forget to unregister your listener in onDestroy in case of android kill your service (which is very rare):
#Override
public void onDestroy() {
super.onDestroy();
// Unregister your listener
}
You can register SensorManager inside service in OnStartCommand.Also try using startForeground as android os will kill your service when app is killed

GoogleApiClient issue: GoogleApiClient is not connected yet

In this class,:
public class Presence implements ConnectionCallbacks,
OnConnectionFailedListener, LocationListener
I have the following constructor:
private Presence(Context context)
{
this.context = context;
gApiClient = new GoogleApiClient.Builder(context, this, this)
.addApi(LocationServices.API)
.build();
if (!gApiClient.isConnecting() && !gApiClient.isConnected())
{
gApiClient.connect();
}
} // of constructor()
I use it to return a Singleton instance:
public static synchronized Presence getInstance(Context context)
{
if (presenceSingleton == null)
presenceSingleton = new Presence(context);
return presenceSingleton;
}
The onConnected() looks like this:
#Override
public void onConnected(Bundle connectionHint)
{
Log.e(LOG_TAG, "In onConnected(), gApiClient.isConnected(): " +
gApiClient.isConnected());
createLocationRequest();
getLocation();
getSubLocality();
} // of onConnected()
Based on a setting the user can make in the app, I call the following method to put the app into a so-called auto-pilot mode, where it starts tracking the user's location:
public void startLocationUpdates()
{
// Prints 'false' in the logs:
Log.e(LOG_TAG, "In startLocationUpdates(), gApiClient.isConnected(): " + gApiClient.isConnected());
Intent locationChangeIntent = new Intent(context, LocationTracker.class);
pendingIntent = PendingIntent.getService(context, 188, locationChangeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// Crash points to the following line:
LocationServices.FusedLocationApi.requestLocationUpdates(gApiClient, locationRequest, pendingIntent);
} // of startLocationUpdates()
In the main activity, I make an instance of the above class in the onCreate():
public class MainClass extends AppCompatActivity implements
OnSharedPreferenceChangeListener
{
....
....
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getAppSettings();
presence = Presence.getInstance(getApplicationContext());
....
....
startApp();
....
}
private void startApp()
{
if (pref_autoPilot)
presence.startLocationUpdates();
}
....
....
....
} // of class MainClass
When the user sets the auto pilot preference, the app crashes with the following exception:
java.lang.IllegalStateException: GoogleApiClient is not connected yet.
at the line indicated in the above method startLocationUpdates().
I read a lot of answers, but was unable to figure out a solution to this issue. Can you please point out what I am doing wrong? Is it that the Presence class should be in an AppCompatActivity or FragmentActivity or similar, and cannot be independent like in here? Please help me fix this nagging issue.
Many thanks in advance!
You should call presence.startLocationUpdates(); this method from onConnected() method. And check the condition befor calling, if your location is null. Before the connection happend of GoogleApiClient you can;t update location.
Follow the simple guidelines at Android Developers.
I think you missed the following part:
protected void onStart() {
mGoogleApiClient.connect();
super.onStart();
}
protected void onStop() {
mGoogleApiClient.disconnect();
super.onStop();
}
In your MainActivity you need to call connect and disconnect when required (in other words, when so called auto-pilot mode is on)
You should instantiate a client object in your Activity's onCreate(Bundle) method and then call connect() in onStart() and disconnect() in onStop(), regardless of the state.
For the sake of completeness, #Kedi...
Can you please point out what I am doing wrong?
gApiClient.connect() was not being called in the right place. Please see the accepted answer.
Is it that the Presence class should be in an AppCompatActivity or FragmentActivity or similar, and cannot be independent like in here?
It can be an independent class
Thanks every one for your kind help.

Background service stops working randomly in android in onCreate() and onResume()

I am working on android application in which i need to start the location service. All i need to make it sure that the service should work, whether it will be on any activity, if i press the back button/home button or even if i sweep the application by pressing home button. My location service stops working after sometime like i put the timing of 1 minute but it will call it after 2-3 minutes.
private static final LocationRequest REQUEST = LocationRequest.create()
.setInterval(1000 * 60 * 1) // 30 minutes seconds
.setFastestInterval(1000 * 60 * 1) // 16ms = 60fps
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
My code is given below for calling location service class and main class where i am running calling that service. Please help me out in the above described scenario where i want to run that service in background: When press the back button, home button, removing application by pressing home button.
public class GPSLoggerService extends Service {
private LocationManager lm;
private static long minTimeMillis = 2000;
private static long minDistanceMeters = 0;
private static float minAccuracyMeters = 35;
private static boolean showingDebugToast = false;
MyLocationTracker locationTracker;
private static final String tag = "MUrgency GPS Logger";
/** Called when the activity is first created. */
private void startLoggerService() {
if (locationTracker != null)
return;
locationTracker = new MyLocationTracker(this) {
#Override
public void onLocationFound(Location location) {
Constants.sMY_LOCATION = location;
float a = (float) location.getLatitude();
float b = (float) location.getLongitude();
SharedPreferences prefs = getSharedPreferences("locationPref", 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putFloat("latitudeFloat", a);
editor.putFloat("longitudeFloat", b);
editor.commit();
if (minutes > 5){
shouldSync = true;
}
}
};
}
private void shutdownLoggerService() {
}
}
#Override
public void onCreate() {
super.onCreate();
startLoggerService();
}
#Override
public void onDestroy() {
super.onDestroy();
shutdownLoggerService();
}
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/**
* Class for clients to access. Because we know this service always runs in
* the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
GPSLoggerService getService() {
return GPSLoggerService.this;
}
}
}
Main class where i am calling service at onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mainlanding);
startService(new Intent(this, GPSLoggerService.class));
}
#Override
protected void onDestroy() {
sActivityMain = null;
super.onDestroy();
stopLocationService();
}
From my view I can see this is a normal process, when the app enters OnPause method, this starts to works in background then you need a background process to execute your class and functions that you want.
If this is your first time using parallel programming I think you need to dedicate a little bit of your time to search information about this. It's amazing form to work with background processes. Really it's the difference between a normal android programmer and professional android programmer (among other things) because with the background processes can use all potency of your device.
Tell me if I helped you, good programming!

Android: Correct way to use a Service in a multi-Activity application

I know, that there are tons of questions regarding leaking Android services and how to "maintain" a service in an app, but it's a bit like seeing two doctors with one problem: you get three different opinions...
In other words: I am completely unsure, how to deal with my service in a proper way. Hence this question.
I have the following: Several Activities and a BoundService, which is started on app startup and should be kept alive until the app closes.
Android.delevoper proposes to use a bound service to ease communication between Service and Activity. So I bind my service in each activitie's onStart() and unbind it in every onStop() to keep it from leaking.
In addition, I read in several threads, that one should use startService() to keep the service from being destroyed when no Activity is binding to it anymore. So I use an explicit startService() in my first Activity and a stopService() on every exit point of my app.
So basically I have this as my Service:
public class NetworkService extends Service {
private final IBinder binder = new ServiceBinder();
#Override
public void onCreate() {
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class ServiceBinder extends Binder {
public NetworkService getService() {
return NetworkService.this;
}
}
}
And then a starting Activity like this:
public class ActFirstActivity extends Activity {
private NetworkService networkService;
private boolean networkServiceIsBound = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_1);
}
#Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(this, NetworkService.class);
startService(serviceIntent);
bindService(serviceIntent, networkServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(networkServiceConnection);
}
private ServiceConnection networkServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
ServiceBinder binder = (ServiceBinder) service;
networkService = binder.getService();
networkServiceIsBound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
networkServiceIsBound = false;
}
};
}
Every other Activity would have the same bind/unbind functionality and the same ServiceConnection. As far as I can see, this keeps my app from leaking a service connection. But I have the impression that I introduced a LOT of overhead here. How to do this
a) secure (no leaking),
b) efficient (minimal overhead), and
c) easy to maintain (code-wise) ?
Thanks to everyone who takes the time to answer :)

I have one Activity. In this Activity, I executed a AsyncTask , but it's not working

private class ExecuteLocations extends AsyncTask<String, Void, Void>{
private final ProgressDialog dialog = new ProgressDialog(ListProfiles.this);
protected void onPreExecute() {
//this.dialog.setMessage("Starting pre-execute...");
//this.dialog.show();
}
#Override
protected Void doInBackground(String... arg0) {
check_profiles_lm=(LocationManager) ListProfiles.this.getSystemService(LOCATION_SERVICE);
myLocListen = new LocationListener(){
#Override
public void onLocationChanged(Location location) {
HashMap params = new HashMap();
params.put("lat", Double.toString(location.getLatitude()));
params.put("long", Double.toString(location.getLongitude()));
postData("http://mydomain.com",params);
}
#Override
public void onStatusChanged(String provider, int status,Bundle extras) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
};
check_profiles_lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 0, myLocListen);
return null;
}
protected void onPostExecute(final Void unused) {
if (this.dialog.isShowing()) {
//this.dialog.dismiss();
}
//Do something else here
}
}
Basically, my objective is:
Constantly post the latitude/longitude to my website every minute or so.
Of course, I want to do this in another thread, so it doesn't mess up my UI thread. This AsyncTask is supposed to solve that...but it's bringing up an error and I don't know why.
What can you do to accomplish my objective? It's very simple...just scan location and post to web every minute, in the background.
By the way, my onCreate method is like this. That's basically it.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new ExecuteLocations().execute();
setContentView(R.layout.main_list);
}
Break it into 3 steps:
Make whatever you want to work work w/o AsyncTask
Make sure you've figured out AsyncTask, do a simple Log.e("haha", "gotcha") in doInBackground(...) and check that it shows
Combine the two.
For this case, I'd probably go with a Service triggered by AlarmManager. Guess you'll need the latter anyways.
Create this as a Service. No need to have a Timer or AlarmManager as you register for location events and act appropriately.
An example of listening to location events can be found at
http://code.google.com/p/android-bluetooth-on-motion/source/browse/#svn/trunk/BluetoothOnMotion/src/com/elsewhat
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_NETWORK, MIN_DISTANCE_NETWORK,locationListener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_GPS, MIN_DISTANCE_GPS,locationListener);

Categories