i'm trying to use FusedLocationApi with pending intent to get period location updates so i can send the data to some server for processing. Everything works. However, there are some instances where the broadcast just stops receiving. I would need to restart the service again in order for it to continue.
I already have the service onStartCommand to return START_STICKY so even if the app is killed it should start the service again. Furthermore, I also added a Boot Completed Intent Receiver so if the phone died and user restarted phone it would restart my service.
So it seems everything is fine and working but just at some point, everything just stops. I did notice a few times that when it does stop working, the last location i received was NULL (i log every location update and error messages throughout my project).
Any ideas why location services just stops working?
P.S. there is no connection failure because i put a message and hook into that function and it's not being called. And it's not internet failure either as i log that as well. Once internet is restored it would continue as normal/expected.
Thanks.
This is the main activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);
if (findViewById(android.R.id.home) != null) {
findViewById(android.R.id.home).setVisibility(View.GONE);
}
LayoutInflater inflator = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflator.inflate(R.layout.header_logo, null);
ActionBar.LayoutParams params = new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setCustomView(view, params);
}
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.status_text);
// init preferences and status handler
MyStatusHandler.init(getApplicationContext(), textView);
// init web service call class
...;
// check for location services
if (!isLocationEnabled(getApplicationContext())){
String msg = "Location services not turned on";
MyStatusHandler.setStatusText(msg);
}
}
public static boolean isLocationEnabled(Context context) {
int locationMode = 0;
String locationProviders;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
try {
locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
}
return locationMode != Settings.Secure.LOCATION_MODE_OFF;
}else{
locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return !TextUtils.isEmpty(locationProviders);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
// create new intent activity for the settings page
Intent i = new Intent(this, MySettingsActivity.class);
startActivity(i);
return true;
}
else if (id == R.id.action_about){
// 1. Instantiate an AlertDialog.Builder with its constructor
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 2. Chain together various setter methods to set the dialog characteristics
if (Constants.DEBUG_BUILD == true) {
builder.setMessage("v." + MyStatusHandler.getReleaseVersionNum() + " dev Build: " + MyStatusHandler.getDevVersionNum())
.setTitle("About")
.setCancelable(false)
.setPositiveButton("OK", null);
}
else{
builder.setMessage("v." + MyStatusHandler.getReleaseVersionNum())
.setTitle("About")
.setCancelable(false)
.setPositiveButton("OK", null);
}
// 3. Get the AlertDialog from create()
AlertDialog dialog = builder.create();
// show it
dialog.show();
return true;
}
return super.onOptionsItemSelected(item);
}
// check email entered
public boolean isSettingsEntered(){
boolean result = true;
if (MyStatusHandler.getEmailText().equals("") || MyStatusHandler.getPasswordText().equals("")){
// 1. Instantiate an AlertDialog.Builder with its constructor
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 2. Chain together various setter methods to set the dialog characteristics
builder.setMessage("Please ensure both email and password are entered in settings")
.setTitle("Email and/or Password not set")
.setCancelable(false)
.setPositiveButton("OK",null);
// 3. Get the AlertDialog from create()
AlertDialog dialog = builder.create();
// show it
dialog.show();
result = false;
}
return result;
}
/** Called when the user clicks the Opt in button */
public void startService(View view) {
// Do something in response to button
if (isSettingsEntered() && isLocationEnabled(getApplicationContext())) {
// send opt in to web service
...;
// start service
startService(new Intent(this, BackgroundLocationService.class));
// update status text
String msg = "Connecting ...";
MyStatusHandler.setStatusText(msg);
}
}
/** Called when the user clicks the Opt out button */
public void stopService(View view) {
// Do something in response to button
if (isSettingsEntered() && isLocationEnabled(getApplicationContext())) {
// send OptOut to web service
...;
// update status text
String msg = "Connecting ...";
MyStatusHandler.setStatusText(msg);
}
}
public static void ...(boolean isOptIn, Location location, boolean sendOptIn){
if (sendOptIn)
{
// send opt in via async task
}
else{
// send location via async task
}
}
// Handle results returned to the FragmentActivity by Google Play services
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Decide what to do based on the original request code
switch (requestCode) {
case Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST :
// log the error
MyStatusHandler.logDataToFile("Connection Failure Resolution Request - Result Code: "+String.valueOf(resultCode));
// If the result code is Activity.RESULT_OK, try to connect again
switch (resultCode) {
case Activity.RESULT_OK :
// Try the request again
MyStatusHandler.logDataToFile("Attempting to re-start service");
// start service
startService(new Intent(this, BackgroundLocationService.class));
break;
}
}
}
}
Here is the background service:
public class BackgroundLocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
public static final String TAG = BackgroundLocationService.class.getSimpleName();
private GoogleApiClient mGoogleApiClient;
private boolean mInProgress;
private LocationRequest mLocationRequest;
public void onCreate(){
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
#Override
public int onStartCommand (Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if(mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
if(!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onConnected(Bundle bundle) {
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setFastestInterval(Constants.FASTEST_INTERVAL)
.setInterval(Constants.UPDATE_INTERVAL);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
new Intent(this, MyLocationHandler.class),
PendingIntent.FLAG_CANCEL_CURRENT);
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, pendingIntent);
}
else{
MyStatusHandler.setStatusText("Google Client Failed");
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
mInProgress = false;
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(null, Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST);
// * Thrown if Google Play services canceled the original
// * PendingIntent
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
//* If no resolution is available, display a dialog to the
// * user with the error.
Log.i(TAG, "Location services connection failed with code " + connectionResult.getErrorCode());
MyStatusHandler.setStatusText("Location services connection failed with code " + connectionResult.getErrorCode());
}
}
#Override
public void onDestroy(){
mInProgress = false;
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
super.onDestroy();
}
}
Here is the broadcast receiver:
public class MyLocationHandler extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Location location = intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
if (location != null && MyStatusHandler.getOptInStatus()) {
// debug messages
String msg = Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Log.d("debug", msg);
// log location to file
MyStatusHandler.logDataToFile("Location: "+msg);
// send Location to web service
MainActivity....(MyStatusHandler.getOptInStatus(), location, false);
}
if (location == null){
MyStatusHandler.logDataToFile("Location == NULL!");
}
}
}
I have set the interval to 5minutes and Fast interval to 2minutes.
NOTE: i removed some function calls and code for the web service operations
Everything works and i get updates as expected but at some point in time, the broadcast receiver doesn't get anything. When i check the logs, OptInStatus doesn't change and the last location broadcast i received was NULL.
The other parts of my code to call the web service and handle the status messages doesn't touch the service or location requests.
Related
Jugnoo Driver App has not been whitelisted in the Auto Start but yet it again starts the service after some time !
How jugnoo rider app runs even it is not Auto start too
I have done notification stuff , changed the manifest to stopWithTask="false" .
I have created a service for same issue,
please check it out with this.
It will help you
public class GpsServices extends Service implements LocationListener, GpsStatus.Listener {
Data data;
private LocationManager mLocationManager;
private SharedPreferences sharedPreferences;
private Data.onGpsServiceUpdate onGpsServiceUpdate;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
public String gps_notification = "gps_channel";
#SuppressLint("MissingPermission")
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
if (data == null) {
data = new Data(onGpsServiceUpdate);
} else {
data.setOnGpsServiceUpdate(onGpsServiceUpdate);
}
gpsListener();
mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
if (mLocationManager.getAllProviders().indexOf(LocationManager.GPS_PROVIDER) >= 0) {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 0, this);
} else {
Log.w("SideMenuActivity", "No GPS location provider found. GPS data display will not be available.");
}
if (!mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Toast.makeText(this, "Gps not enabled", Toast.LENGTH_SHORT).show();
}
}
public void onLocationChanged(Location location) {
Gson gson = new Gson();
String json = sharedPreferences.getString("data", "");
data = gson.fromJson(json, Data.class);
if (data == null) {
data = new Data(onGpsServiceUpdate);
} else {
data.setOnGpsServiceUpdate(onGpsServiceUpdate);
}
String speed = String.format(Locale.ENGLISH, "%.0f", location.getSpeed() * 3.6);
Toast.makeText(this, speed, Toast.LENGTH_SHORT).show();
Log.e("isRunningFalse", speed);
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;//needed for stop.
if (intent != null) {
msg.setData(intent.getExtras());
mServiceHandler.sendMessage(msg);
} else {
Toast.makeText(GpsServices.this, "The Intent to start is null?!", Toast.LENGTH_SHORT).show();
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
/* Remove the locationlistener updates when Services is stopped */
#Override
public void onDestroy() {
mLocationManager.removeUpdates(this);
mLocationManager.removeGpsStatusListener(this);
stopForeground(true);
}
#Override
public void onGpsStatusChanged(int event) {
switch (event) {
case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
#SuppressLint("MissingPermission") GpsStatus gpsStatus = mLocationManager.getGpsStatus(null);
int satsInView = 0;
int satsUsed = 0;
Iterable<GpsSatellite> sats = gpsStatus.getSatellites();
for (GpsSatellite sat : sats) {
satsInView++;
if (sat.usedInFix()) {
satsUsed++;
}
}
if (satsUsed == 0) {
data.setRunning(false);
stopService(new Intent(getBaseContext(), GpsServices.class));
// firstfix = true;
}
break;
case GpsStatus.GPS_EVENT_STOPPED:
if (!mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Toast.makeText(this, "Gps not enabled.", Toast.LENGTH_SHORT).show();
}
break;
case GpsStatus.GPS_EVENT_FIRST_FIX:
break;
}
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
//promote to foreground and create persistent notification.
//in Oreo we only have a few seconds to do this or the service is killed.
Notification notification = getNotification("App is running");
startForeground(msg.arg1, notification); //not sure what the ID needs to be.
// Normally we would do some work here, like download a file.
// For our example, we just sleep for 5 seconds then display toasts.
//setup how many messages
int times = 1, i;
Bundle extras = msg.getData();
if (extras != null) {
times = 1000*60*60*24; //default is one
}
//loop that many times, sleeping for 5 seconds.
for (i = 0; i < times; i++) {
synchronized (this) {
try {
wait(5000); //5 second sleep
} catch (InterruptedException e) {
}
}
String info = i + "GPS SPEED LOG";
Log.d("intentServer", info);
//make a toast
//unable to ensure the toasts will always show, so use a handler and post it for later.
// Toast.makeText(MyForeGroundService.this, info, Toast.LENGTH_SHORT).show();
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
// stopSelf(msg.arg1); //notification will go away as well.
}
}
// build a persistent notification and return it.
public Notification getNotification(String message) {
return new NotificationCompat.Builder(getApplicationContext(), gps_notification)
.setSmallIcon(android.R.drawable.ic_menu_mylocation)
.setOngoing(true) //persistent notification!
.setChannelId(gps_notification)
.setContentTitle("Gps Service") //Title message top row.
.setContentText(message) //message when looking at the notification, second row
.build(); //finally build and return a Notification.
}
}
I use a started service with the fused api, and implement the location listener directly on it.
The Location keeps updating even when the screen is locked, But it stops if the screen goes off.
So, is there any way to make sure that the location will keep updating when the screen is off?
I read a lot of other questions and I don't really know what i'm missing.
public class CLocationService extends Service implements GoogleApiClient.ConnectionCallbacks, LocationListener,
GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private PowerManager.WakeLock mWakeLock;
private LocationRequest mLocationRequest;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private Boolean servicesAvailable = false;
private boolean isStarted;
public static final int LOCATION_SERVICE_NOTIFICATION_ID = 4567654;
private void showNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(this)
.setContentTitle(getText(R.string.app_name))
.setContentText("")
.setSmallIcon(R.mipmap.ic_notification)
.setContentIntent(pendingIntent)
.setTicker("")
.build();
startForeground(LOCATION_SERVICE_NOTIFICATION_ID, notification);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
setUpLocationClientIfNeeded();
startLocationServices();
}
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
protected synchronized void buildGoogleApiClient() {
this.mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
return true;
} else {
return false;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
/*
WakeLock is reference counted so we don't want to create multiple WakeLocks. So do a check before initializing and acquiring.
This will fix the "java.lang.Exception: WakeLock finalized while still held: MyWakeLock" error that you may find.
*/
if (this.mWakeLock == null) { //**Added this
this.mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
}
if (!this.mWakeLock.isHeld()) { //**Added this
this.mWakeLock.acquire();
}
if (!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
setUpLocationClientIfNeeded();
if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
private void setUpLocationClientIfNeeded() {
if (mGoogleApiClient == null)
buildGoogleApiClient();
}
#Override
public void onDestroy() {
stopLocationServices();
super.onDestroy();
}
private void startLocationServices() {
mInProgress = false;
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 5 seconds
mLocationRequest.setInterval(5000);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(1000);
mLocationRequest.setSmallestDisplacement(0);
servicesAvailable = servicesConnected();
}
private void stopLocationServices() {
// Turn off the request flag
this.mInProgress = false;
if (this.servicesAvailable && this.mGoogleApiClient != null) {
this.mGoogleApiClient.unregisterConnectionCallbacks(this);
this.mGoogleApiClient.unregisterConnectionFailedListener(this);
this.mGoogleApiClient.disconnect();
// Destroy the current location client
this.mGoogleApiClient = null;
}
// Display the connection status
// Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ":
// Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
if (this.mWakeLock != null) {
this.mWakeLock.release();
this.mWakeLock = null;
}
}
private void cancelNotification() {
NotificationManager nMgr = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
nMgr.cancel(LOCATION_SERVICE_NOTIFICATION_ID);
}
#Override
public void onLocationChanged(Location location) {
// log the new location
}
#Override
public void onConnected(#Nullable Bundle bundle) {
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;
}
LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient,
mLocationRequest, this); // This is the changed line.
}
#Override
public void onConnectionSuspended(int i) {
// Turn off the request flag
mInProgress = false;
// Destroy the current location client
mGoogleApiClient = null;
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
mInProgress = false;
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
// If no resolution is available, display an error dialog
} else {
}
}
}
I'm not sure I have your answer but since you're 9 days in with nothing I'll give some suggestions.
My app is doing what you would like to do. I use a long running started Service to keep location updated even when the phone is off.
The difference most likely to cause different behavior between your code & mine is the return from onStartCommand(). You are returning START_STICKY. This is the recommended return for something like this:
This mode makes sense for things that will be explicitly started and
stopped to run for arbitrary periods of time, such as a service
performing background music playback.
However, I'm sending info in the Intent that I needed to have redelivered so I'm returning START_REDELIVER_INTENT. Try this (even if you have no need to redeliver any data) to see if it fixes your problem.
Also, I didn't need WakeLock in my implementation. Maybe your implementation needs this though. Have you tried without it?
Edit: Lastly, what kind of device are you using? link
Today I have a problem in my Android project. I use a Service with a thread in it to log location information in a period of 10s. However, when I change the screen orientation (Portrait -> Landscape), the period just messed up.
I think I may run another thread so that I got one more thread running behind once I rotate the screen. I have print log messages and it seems my guessing is right.
Here is my code:
public class LocationService extends Service
{
public Location loc;
public LocationService()
{
}
#Override
public int onStartCommand(Intent intent, int flags, int id)
{
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
if ( ContextCompat.checkSelfPermission(LocationService.this, android.Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED )
{
loc = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if(loc == null) // fall back to network if GPS is not available
{
loc = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
}
if(loc != null)
{
Timer timer = new Timer();
final String time = (new Date()).toString();
timer.scheduleAtFixedRate(new TimerTask()
{
#Override
public void run()
{
Log.d(time, "hehe");
double currentLat = loc.getLatitude();
double currentLng = loc.getLongitude();
Intent done = new Intent();
done.setAction("location");
done.putExtra("currentLat", currentLat);
done.putExtra("currentLng", currentLng);
sendBroadcast(done);
//Toast.makeText(LocationService.this, String.valueOf(currentLat) + String.valueOf(currentlng), Toast.LENGTH_LONG).show();
}
}, 10000, 10000);
}
}
else
{
Toast.makeText(LocationService.this, "Please allow app to access your location", Toast.LENGTH_LONG).show();
}
}
});
thread.start();
return START_STICKY; // stay running
}
#Override
public IBinder onBind(Intent intent)
{
// // TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
return null;
}
// #Override
// public void onDestroy()
// {
// Log.d("hehe","onDestroy");
// super.onDestroy();
// }
}
Here is the code for activity:
public class MainActivity extends AppCompatActivity
{
private Toolbar toolbar;
private FragmentManager fragmentManager;
private LocalFragment localFragment;
private ServerFragment serverFragment;
private QueryFragment queryFragment;
private FragmentTransaction transaction;
public SQLiteHelper dbHelper;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new SQLiteHelper(this);
//garb handlers
fragmentManager = getFragmentManager();
localFragment = (LocalFragment) fragmentManager.findFragmentById(R.id.fragment_local);
serverFragment = (ServerFragment) fragmentManager.findFragmentById(R.id.fragment_server);
queryFragment = (QueryFragment) fragmentManager.findFragmentById(R.id.fragment_query);
// initial visibility
transaction = fragmentManager.beginTransaction();
if(this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
// toolbar (must be put in checking orientation because landscape layout does not have toolbar here)
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(R.string.toolbar_title);
setSupportActionBar(toolbar);
transaction.show(localFragment);
transaction.hide(serverFragment);
transaction.hide(queryFragment);
transaction.commit();
}
else // ORIENTATION_LANDSCAPE
{
transaction.hide(queryFragment); // landscape orientation does not need query function (?)
transaction.show(localFragment);
transaction.show(serverFragment);
transaction.commit();
}
// register network status receiver
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
NetworkStatusReceiver myNetworkReceiver = new NetworkStatusReceiver();
registerReceiver(myNetworkReceiver, intentFilter);
// start location service
Intent intent = new Intent(this, LocationService.class);
intent.setAction("location");
startService(intent);
// register location receiver
IntentFilter intentFilterLocation = new IntentFilter();
intentFilterLocation.addAction("location");
LocationReceiver myLocationReceiver = new LocationReceiver();
registerReceiver(myLocationReceiver, intentFilterLocation);
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main_activity, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
transaction = fragmentManager.beginTransaction();
// handle click event
if(item.getItemId() == R.id.action_online)
{
transaction.hide(localFragment);
transaction.hide(queryFragment);
transaction.show(serverFragment);
transaction.commit();
}
else if(item.getItemId() == R.id.action_offline)
{
transaction.hide(serverFragment);
transaction.hide(queryFragment);
transaction.show(localFragment);
transaction.commit();
}
else // Query
{
transaction.hide(localFragment);
transaction.hide(serverFragment);
transaction.show(queryFragment);
transaction.commit();
}
return super.onOptionsItemSelected(item);
}
// receiver for network change action
private class NetworkStatusReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) // if receive network change event broadcast
{
Toast.makeText(context, "Network status changed!", Toast.LENGTH_LONG).show();
// why I cannot use another thread to do so? CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
// Thread thread = new Thread(new Runnable()
// {
// #Override
// public void run()
// {
int type = 0;
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); // get current network type
if (networkInfo != null && networkInfo.isAvailable())
{
type = networkInfo.getType();
String typeName = networkInfo.getTypeName(); // (?)
//serverFragment = (ServerFragment)fragmentManager.findFragmentById(R.id.fragment_server);
if (type == ConnectivityManager.TYPE_WIFI) // wifi
{
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
Log.d("wifiInfo", wifiInfo.toString());
Log.d("SSID",wifiInfo.getSSID());
serverFragment.setNetworkStatusText("WIFI: " + wifiInfo.getSSID()); // thread issues here. WTF
} else if (type == ConnectivityManager.TYPE_MOBILE) // Cellar
{
serverFragment.setNetworkStatusText("Mobile Data");
} else // no network
{
serverFragment.setNetworkStatusText("No Network");
}
} else // no network
{
serverFragment.setNetworkStatusText("No Network");
}
// }
// });
// thread.start();
}
}
}
// receiver for location per 10s
public class LocationReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if(action.equals("location"))
{
double currentLat = intent.getDoubleExtra("currentLat", 0.0);
double currentLng = intent.getDoubleExtra("currentLng", 0.0);
Date now = new Date();
localFragment.addNewLocation(now, currentLat, currentLng); // add to list for listview
// TODO: 11/5/16 implement SQLite insert
dbHelper.addEntry(now, currentLat, currentLng);
Toast.makeText(MainActivity.this, "Current Location: " + String.valueOf(currentLat) + ", " + String.valueOf(currentLng), Toast.LENGTH_LONG).show();
}
}
}
public void setNetworkStatus(String networkStatus)
{
serverFragment.setNetworkStatusText(networkStatus);
}
}
My Question are:
It seems that the onDestroy () method would not be called when rotate the screen?
A new Service will not be created when rotate the screen?
How can I stop the thread created previously? Or what is the best way to handle this problem?
It seems that the onDestroy() method would not be called when rotate the screen?
No, it isn't. Changing the screen orientation kills the Activity and creates a new one, but your Service is still running. Screen orientation has no effect on this.
A new Service will not be created when rotate the screen?
No. A Service is essentially a singleton. Android will not create a new instance of your Service if there is already one running. However, onStartCommand() will be called again because your Activity calls startService() when it is created.
How can I stop the thread created previously? Or what is the best way to handle this problem?
The easiest way to deal with this is to check in onStartCommand() if your thread is already running. If so, you don't need to start it again. Save a reference to your Thread in a member variable (a field) in your Service and call isAlive() on it to see if it is running.
Also, in onDestroy() you should make sure that your Thread shuts down, otherwise it will continue to run even after your Service is dead. To do that you should create a boolean member variable (field) in the Thread, which you check in each loop. In onDestroy() of your Service, set that boolean so that the Thread exits.
I am struggling with a problem to display my reverse geocoding results automatically. It works fine when I click button to get location, but I want it to simply display the location automatically when the app is loaded.
My code is in a Java class called geocoding, eventually i want to display this code on a marker on my map, which i have already created.
but this thread is to eliminate the button and display location as son as map loads.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // uses layout from mainactivity
mResultReceiver = new AddressResultReceiver(new Handler());
mLocationAddressTextView = (TextView) findViewById(R.id.location_address_view);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mFetchAddressButton = (Button) findViewById(R.id.fetch_address_button);
// Set defaults, then update using values stored in the Bundle.
mAddressRequested = false;
mAddressOutput = "";
updateValuesFromBundle(savedInstanceState);
updateUIWidgets();
buildGoogleApiClient();
}
public void buttonOnClick(View view) { // this links the maps activity via the XML layout file
startActivity(new Intent(Geocode.this, MapsActivity.class));
}
/**
* Updates fields based on data stored in the bundle.
*/
private void updateValuesFromBundle(Bundle savedInstanceState) {
if (savedInstanceState != null) {
// Check savedInstanceState to see if the address was previously requested.
if (savedInstanceState.keySet().contains(ADDRESS_REQUESTED_KEY)) {
mAddressRequested = savedInstanceState.getBoolean(ADDRESS_REQUESTED_KEY);
}
// Check savedInstanceState to see if the location address string was previously found
// and stored in the Bundle. If it was found, display the address string in the UI.
if (savedInstanceState.keySet().contains(LOCATION_ADDRESS_KEY)) {
mAddressOutput = savedInstanceState.getString(LOCATION_ADDRESS_KEY);
displayAddressOutput();
}
}
}
/**
* Builds a GoogleApiClient. Uses {#code #addApi} to request the LocationServices API.
*/
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
public void fetchAddressButtonHandler(View view) {
// We only start the service to fetch the address if GoogleApiClient is connected.
if (mGoogleApiClient.isConnected() && mLastLocation != null) {
startIntentService();
}
mAddressRequested = true;
updateUIWidgets();
}
#Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
#Override
protected void onStop() {
super.onStop();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
/**
* Runs when a GoogleApiClient object successfully connects.
*/
#Override
public void onConnected(Bundle connectionHint) {
// Gets the best and most recent location currently available, which may be null
// in rare cases when a location is not available.
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
return;
}
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocation != null) {
// Determine whether a Geocoder is available.
if (!Geocoder.isPresent()) {
Toast.makeText(this, R.string.no_geocoder_available, Toast.LENGTH_LONG).show();
return;
}
if (mAddressRequested) {
startIntentService();
}
}
}
/**
* Creates an intent, adds location data to it as an extra, and starts the intent service for
* fetching an address.
*/
protected void startIntentService() {
// Create an intent for passing to the intent service responsible for fetching the address.
Intent intent = new Intent(this, FetchAddressIntentService.class);
// Pass the result receiver as an extra to the service.
intent.putExtra(Constants.RECEIVER, mResultReceiver);
// Pass the location data as an extra to the service.
intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation);
startService(intent);
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Refer to the javadoc for ConnectionResult to see what error codes might be returned in
// onConnectionFailed.
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
}
#Override
public void onConnectionSuspended(int cause) {
// The connection to Google Play services was lost for some reason. We call connect() to
// attempt to re-establish the connection.
Log.i(TAG, "Connection suspended");
mGoogleApiClient.connect();
}
/**
* Updates the address in the UI.
*/
protected void displayAddressOutput() {
mLocationAddressTextView.setText(mAddressOutput);
}
/**
* Toggles the visibility of the progress bar. Enables or disables the Fetch Address button.
*/
private void updateUIWidgets() {
if (mAddressRequested) {
mProgressBar.setVisibility(ProgressBar.VISIBLE);
mFetchAddressButton.setEnabled(false);
} else {
mProgressBar.setVisibility(ProgressBar.GONE);
mFetchAddressButton.setEnabled(true);
}
}
/**
* Shows a toast with the given text.
*/
protected void showToast(String text) {
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save whether the address has been requested.
savedInstanceState.putBoolean(ADDRESS_REQUESTED_KEY, mAddressRequested);
// Save the address string.
savedInstanceState.putString(LOCATION_ADDRESS_KEY, mAddressOutput);
super.onSaveInstanceState(savedInstanceState);
}
/**
* Receiver for data sent from FetchAddressIntentService.
*/
class AddressResultReceiver extends ResultReceiver {
public AddressResultReceiver(Handler handler) {
super(handler);
}
/**
* Receives data sent from FetchAddressIntentService and updates the UI in MainActivity.
*/
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// Display the address string or an error message sent from the intent service.
mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY);
displayAddressOutput();
// Show a toast message if an address was found.
if (resultCode == Constants.SUCCESS_RESULT) {
showToast(getString(R.string.address_found));
}
// Reset. Enable the Fetch Address button and stop showing the progress bar.
mAddressRequested = false;
updateUIWidgets();
}
}
}
set mAdressRequested from False to True,
so in OnConnected, startIntentService can be called
// Set defaults, then update using values stored in the Bundle.
mAddressRequested = true;
mAddressOutput = "";
updateValuesFromBundle(savedInstanceState);
How can i remove the Geofence? everytime i click the reset button it shows the following error-
Caused by: java.lang.IllegalStateException: GoogleApiClient is not connected yet.
Here is my code-
public class GeofenceStore implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status>, LocationListener {
private Context mContext;
/**
* Google API client object.
*/
private GoogleApiClient mGoogleApiClient;
/**
* Geofencing PendingIntent
*/
private PendingIntent mPendingIntent;
/**
* List of geofences to monitor.
*/
private ArrayList<Geofence> mGeofences;
/**
* Geofence request.
*/
private GeofencingRequest mGeofencingRequest;
/**
* Location Request object.
*/
private LocationRequest mLocationRequest;
public GeofenceStore(Context context, ArrayList<Geofence> geofences) {
mContext = context;
mGeofences = new ArrayList<Geofence>(geofences);
mPendingIntent = null;
// Build a new GoogleApiClient, specify that we want to use LocationServices
// by adding the API to the client, specify the connection callbacks are in
// this class as well as the OnConnectionFailed method.
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
Log.i("APi is :", "+" + mGoogleApiClient);
mLocationRequest = new LocationRequest();
// We want a location update every 10 seconds.
mLocationRequest.setInterval(10000);
// We want the location to be as accurate as possible.
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle bundle) {
// We're connected, now we need to create a GeofencingRequest with
// the geofences we have stored.
mGeofencingRequest = new GeofencingRequest.Builder().addGeofences(
mGeofences).build();
mPendingIntent = createRequestPendingIntent();
// This is for debugging only and does not affect
// geofencing.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ActivityCompat.checkSelfPermission(mContext,Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
GeofenceStore.this.requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 100);
// public void requestPermissions(#NonNull String[] permissions, int requestCode)
// 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 Activity#requestPermissions for more details.
return;
}
}
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
// Submitting the request to monitor geofences.
PendingResult<Status> pendingResult = LocationServices.GeofencingApi
.addGeofences(mGoogleApiClient, mGeofencingRequest,
mPendingIntent);
// Set the result callbacks listener to this class.
pendingResult.setResultCallback(this);
}
private void requestPermissions(String[] strings, int i) {
switch(i)
{
case 100: {
{
Log.i("thanks","asdm");
}
}
}
}
public void removeGeofence(){
LocationServices.GeofencingApi.removeGeofences(
mGoogleApiClient,
// This is the same pending intent that was used in addGeofences().
mPendingIntent
).setResultCallback(this); // Result processed in onResult().
}
private void requestPermissions(int requestCode,String[] permissions, int[] grantResults) {
switch (requestCode) {
case 100: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(mContext, "Thanks for the permission", Toast.LENGTH_LONG).show();
// permission was granted, yay! do the
// calendar task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(mContext, "You did not allow to access your current location", Toast.LENGTH_LONG).show();
}
}
// other 'switch' lines to check for other
// permissions this app might request
}
}
/**
* This creates a PendingIntent that is to be fired when geofence transitions
* take place. In this instance, we are using an IntentService to handle the
* transitions.
*
* #return A PendingIntent that will handle geofence transitions.
*/
private PendingIntent createRequestPendingIntent() {
if (mPendingIntent == null) {
Log.v("HERE", "Creating PendingIntent");
Intent intent = new Intent(mContext, GeofenceIntentService.class);
Log.i("another class called",""+intent);
mPendingIntent = PendingIntent.getService(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
try {
mPendingIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
return mPendingIntent;
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onLocationChanged(Location location) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
public void onProviderEnabled(String provider) {
}
public void onProviderDisabled(String provider) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.v("THE CONNECTION HAS", "failed.");
}
public void onSuccess(AsyncTask.Status status) {
}
public void onFailure(Status status) {
}
#Override
public void onResult(Status result) {
if (result.isSuccess()) {
Log.v("THE RESULT IS", "Success!");
} else if (result.hasResolution()) {
// TODO Handle resolution
} else if (result.isCanceled()) {
Log.v("THE RESULT IS", "Canceled");
} else if (result.isInterrupted()) {
Log.v("THE RESULT IS", "Interrupted");
} else {
}
}
}
and here is the function to call from my maps activity, i have used a reset button to reset the geofence added.Pendind intent can be found in the above mentioned code. i want to know what is the problem with it?
The button is-
public void Reset(View view){
db.execSQL("DELETE FROM Coordinates");
//delete all rows in a table
request=request-1;
GeofenceStore mgeofencestore=new GeofenceStore(this,mGeofences);
mgeofencestore.removeGeofence();
db.close();
Toast.makeText(this,"The Data was reset,Please click on 'Add Geofence' to add more Geofence",Toast.LENGTH_LONG).show();
}
You're creating a new GoogleAPIClient every time create a new GeofenceStore object. Since you call removeGeofences() immediately after, the GoogleAPIClient never has a chance to connect, and throws the error you are seeing. Either place a copy of the GeofenceStore object you initially make in the calling class, or create a static instance method to get the current object and avoid recreating it.