1

Edit: I have added the code at the bottom of my most recent attempt.


I am trying to follow the example code here. I am having difficulty retrieving the last known location. I've tried several ways.

  1. In the linked example above, they use "GPSTracker". It gives there error "Cannot resolve symbol 'GPSTracker'". Is there an import I am missing for it?
  2. I tried to get the location using LocationManager as shown in this example but I get the error "Cannot resolve method 'requestLocationUpdates(java.lang.String, long, long, anonymous java.util.TimerTask)'". I also tried changing the third parameter to a float and got a similar error.
  3. I tried to get the location with FusedLocationProviderApi. I added the new GoogleApiClient.Builder to my onCreate() method, imported com.google.android.gms.location.FusedLocationProviderApi, and updated the build.gradle file. But it would give errors in the new GoogleApiClient.Builder method when I added "this" to .addConnectionCallbacks(this) and .addOnConnectionFailedListener(this). From what I've read, this is the best option as it is optimized for minimal battery use?

I am sort of at a loss on how to get the location. My goal is similar to the first linked example. I want to build a service that runs in the background and will send updates of it's location to a server on a specified interval. So far I have created two buttons which allow the service to start and stop. Once the service starts it checks if the device has mobile data. I then want it to check if location is turned on and has a GPS signal. I only want GPS signal and not also network location. Finally, if both data and location/GPS signal are true, then it beings to send it's location to the server during the specified interval. Here is my code so far.

MainActivity.java

package com.testtracker;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    MyService.setUp(this);
}

@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)
    {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

public void startService(View view)
{
    startService(new Intent(getBaseContext(), MyService.class));
}

public void stopService(View view)
{
    stopService(new Intent(getBaseContext(), MyService.class));
}
}

MyService.java

package com.testtracker;

import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.os.IBinder;
import android.os.PowerManager;
import android.widget.Button;
import android.widget.Toast;

import java.lang.reflect.Method;
import java.util.Timer;
import java.util.TimerTask;

public class MyService extends Service
{
PowerManager powerManager;
PowerManager.WakeLock wakeLock;
static Button startButton;
static Button stopButton;

public static void setUp(Activity act)
{
    startButton = (Button)act.findViewById(R.id.button2);
    stopButton = (Button)act.findViewById(R.id.button);
}

@Override
public IBinder onBind(Intent intent)
{
    return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
    powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
    wakeLock.acquire();

    startButton.setEnabled(false);
    stopButton.setEnabled(true);

    final ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

    Timer timer = new Timer();
    final int TIME_INTERVAL = 10000;

    Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();

    timer.schedule(new TimerTask()
    {
        boolean mobileDataEnabled = false;

        public void run() {
            try {
                //Check if phone has data
                Class cmClass = Class.forName(connectivityManager.getClass().getName());
                Method method = cmClass.getDeclaredMethod("getMobileDataEnabled");
                method.setAccessible(true);
                mobileDataEnabled = (Boolean) method.invoke(connectivityManager);

                if (mobileDataEnabled)
                {
                    //Has data. Check if phone has location and GPS signal. If it does, get location and send to server.
                }
                else
                {
                    //No data. Check again during the next timer interval.
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }, 0, TIME_INTERVAL);

    return START_STICKY;
}

@Override
public void onDestroy()
{
    super.onDestroy();
    wakeLock.release();
    startButton.setEnabled(true);
    stopButton.setEnabled(false);
    Toast.makeText(this, "Service Stopped", Toast.LENGTH_SHORT).show();
}
}

Edit:

With the attempts I've been making (documented in my below comments), I figured I'd post my updated code. I'll keep the below code updated with my current attempt and leave the above code as my original. I would ideally like to use either FusedLocationProviderApi or Little Fluffy Location Library as they appear to be optimized for running in the background.

MainActivity.java

package com.testtracker;

import android.app.Activity;
import android.content.Intent;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity implements Button.OnClickListener
{
Button startButton;
Button stopButton;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    startButton = (Button)findViewById(R.id.button2);
    stopButton = (Button)findViewById(R.id.button);

    startButton.setEnabled(true);
    stopButton.setEnabled(false);

    LocationManager service = (LocationManager)getSystemService(LOCATION_SERVICE);
    boolean locationEnabled = service.isProviderEnabled(LocationManager.GPS_PROVIDER);

    if(!locationEnabled)
    {
        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        startActivity(intent);
    }

    startButton.setOnClickListener(this);
    stopButton.setOnClickListener(this);
}

@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)
    {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void onClick(View v)
{
    switch(v.getId())
    {
        case R.id.button2:
            startButton.setEnabled(false);
            stopButton.setEnabled(true);

            Intent intentStart = new Intent(this, MyService.class);
            startService(intentStart);
            break;

        case R.id.button:
            startButton.setEnabled(true);
            stopButton.setEnabled(false);

            Intent intentStop = new Intent(this, MyService.class);
            stopService(intentStop);
            break;
    }

}
}

MyService.java

package com.testtracker;

import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.widget.Toast;

import java.lang.reflect.Method;
import java.util.Timer;
import java.util.TimerTask;

public class MyService extends Service implements LocationListener
{
protected LocationManager locationManager;
protected LocationListener locationListener;

PowerManager powerManager;
PowerManager.WakeLock wakeLock;

public void onCreate()
{
    Toast.makeText(this, "in on create", Toast.LENGTH_SHORT).show();
}

@Override
public IBinder onBind(Intent intent)
{
    return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
    powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
    wakeLock.acquire();

    final ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

    Timer timer = new Timer();
    final int TIME_INTERVAL = 10000;

    Looper.prepare(); //Fatal error said this must be added?

    Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();

    timer.schedule(new TimerTask()
    {
        boolean mobileDataEnabled = false;

        public void run() {
            try {
                //Check if phone has data
                Class cmClass = Class.forName(connectivityManager.getClass().getName());
                Method method = cmClass.getDeclaredMethod("getMobileDataEnabled");
                method.setAccessible(true);
                mobileDataEnabled = (Boolean) method.invoke(connectivityManager);

                if (mobileDataEnabled)
                {
                    //Has data. Check if phone has location and GPS signal. If it does, get location and send to server.
                    locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
                    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);

                    final Criteria cri = new Criteria();

                    String bestProvider = locationManager.getBestProvider(cri, true);

                    Location loc = locationManager.getLastKnownLocation(bestProvider);
                    double lat = loc.getLatitude();
                    double lng = loc.getLongitude();

                    Toast.makeText(getBaseContext(), lat + " - " + lng, Toast.LENGTH_SHORT).show();
                }

            } catch (Exception e) {
                e.printStackTrace();
                onDestroy();
            }
        }
    }, 0, TIME_INTERVAL);

    return START_STICKY;
}

@Override
public void onDestroy()
{
    super.onDestroy();
    wakeLock.release();

    Toast.makeText(this, "Service Stopped", Toast.LENGTH_SHORT).show();
}

@Override
public void onLocationChanged(Location location) {

}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {

}

@Override
public void onProviderEnabled(String provider) {

}

@Override
public void onProviderDisabled(String provider) {

}
}

logcat errors look like they are caused by Looper.prepare(). If I don't add that then I get the fatal error "Can't create handler inside thread that has not called Looper.prepare()". Can't win on this one?

2263-2263/com.testtracker E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.testtracker, PID: 2263
java.lang.RuntimeException: Unable to start service com.testtracker.MyService@18edb4e4 with Intent { cmp=com.testtracker/.MyService }: java.lang.RuntimeException: Only one Looper may be created per thread
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2914)
at android.app.ActivityThread.access$2100(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1401)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5257)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: java.lang.RuntimeException: Only one Looper may be created per thread
at android.os.Looper.prepare(Looper.java:76)
at android.os.Looper.prepare(Looper.java:71)
at com.testtracker.MyService.onStartCommand(MyService.java:53)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2897)
at android.app.ActivityThread.access$2100(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1401)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5257)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Community
  • 1
  • 1
  • I tried using [Little Fluffy Location Library](https://code.google.com/p/little-fluffy-location-library/) as it looks like it is optimized for battery performance as well. I followed [this example](http://stackoverflow.com/questions/11342449/mylocationoverlay-or-locationmanager-for-updating-current-location) but get build errors. "error: cannot access GooglePlayServicesClient error: cannot access OnConnectionFailedListener" I'm not worried about the above two errors as much as I'd like to get GPS location on specified intervals as described above. Any advice would be appreciated! – scottyWatty Aug 11 '15 at 02:02
  • I'm continuing to try to solve this with no success yet. My latest attempt was to follow [this question](http://stackoverflow.com/questions/21434314/getting-location-updates-frequently-in-service). I seemed to have made a little progress, but that particular question is incomplete and the OP never responded about the solution. Using the info found there, I am now running into fatal exception errors which are unable to start the service. Any advice on how to get the location during a given interval via a service as described above would be appreciated! – scottyWatty Aug 11 '15 at 03:46
  • Possible duplicate of http://stackoverflow.com/questions/31840260/android-get-location-and-send-data-to-remote-server-only-while-condition-is-tr/31840640?noredirect=1#comment51604099_31840640 – andrewdleach Aug 12 '15 at 16:20

0 Answers0