1

I have created a distance calculator for my final year project. The calculator should display the users current location and then display a marker on the map when pressed. The distance will be displayed from the users current location to the marker.

I have retrieved the users location and stored as a variable which I use in my code but I get thrown with the java.lang.IllegalStateException: System services not available to Activities before onCreate() error. I've tried placing my code from the start in the onCreate() method but this doesn't work either. Any help would be greatly appreciated. I have been trying for hours to get it working but no luck. When I try to place the (LocationManager)getSystemService(Context.LOCATION_SERVICE); in the onCreate() it requires a permission and I've tried everything.

Here is my code

package com.example.matthewmcnabb.moyola;

import android.Manifest;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.view.View;

import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

import java.io.IOException;
import java.util.List;

public class MapsActivity extends FragmentActivity {
    // the Google Map object
    private GoogleMap mMap;
    private LocationManager locationManager;
    private Location mCurrentLocation;

    LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
    public Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
    private double longitude = location.getLongitude();
    private double latitude = location.getLatitude();
    private LatLng STARTING_MARKER_POSITION =new LatLng(longitude, latitude);
    private LatLng distanceFrom = STARTING_MARKER_POSITION;

    // line will be drawn at the click event
    private Polyline line=null;

    // A Geocoder can transform a pair of latitude/longitude into a street address and viceversa.
    // We'll use it in the listener
    private static Geocoder geocoder=null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // we set the layout for the Activity
        setContentView(R.layout.activity_maps);

        // the geocoder is instantiated for the first time
        geocoder=new Geocoder(this);

        // if there isn't a map, it will be created
        setUpMapIfNeeded();
    }

    private GoogleMap.OnMapClickListener clickListener=new GoogleMap.OnMapClickListener() {
        @Override
        public void onMapClick(final LatLng pos) {

            // this method is called when the user taps the map

            // if a line already appears, it's removed
            if (line!=null)
                line.remove();

            // a new line is created
            line = mMap.addPolyline(new PolylineOptions()
                    .add(distanceFrom, pos)
                    .width(5) // width of the line
                    .color(Color.RED)); // line color

            // call the converter object for geocoding invocation and distance calculation
            new AddressConverter().execute(distanceFrom, pos);

        }
    };

    @Override
    protected void onResume() {
        super.onResume();

        // the availability of the GoogleMap will be checked before the Activity starts interacting with the user
        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {

        // the map is created only it has not been initialized
        if (mMap == null) {

            // the map is located in the layout
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();

            // if a map exists, we proceed with initialization
            if (mMap != null) {
                setUpMap();
            }
        }
    }

    // Now it's time to configure the map. We can add markers, shapes, event handlers and so on
    private void setUpMap() {

        // the camera will be positioned according to the new coordinates
        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(STARTING_MARKER_POSITION, 16));

        // we choose the type of the map: Satellite in this case
        mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);

        // markerOptions describes the marker we want to place
        MarkerOptions markerOptions=new MarkerOptions()
                .position(STARTING_MARKER_POSITION)
                .draggable(true);
        // the marker has to be draggable as we'll move it

        // the marker is rendered on the map
        mMap.addMarker(markerOptions);

        // we define the object to invoke when the marker is dragged
        mMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener()
        {
            @Override
            public void onMarkerDragStart(Marker arg0)
            {
                // this method is called when the drag starts
                // the operation we need is the cancellation of a preexisting line
                if (line!=null)
                    line.remove();
            }
            @Override
            public void onMarkerDragEnd(final Marker pos)
            {
                // we get the final position of the marker
                distanceFrom=pos.getPosition();

            }

            @Override
            public void onMarkerDrag(Marker arg0)
            {
                // operations performed during the movement. Nothing to do
            }
        });

        // the callback to invoke is set
        mMap.setOnMapClickListener(clickListener);
    }

    // we want to know which address corresponds to this location
    // we use AsyncTask to perform slower operations on a separate thread
    private class AddressConverter extends AsyncTask<LatLng,Void,String>
    {
        // The ProgressDialog window we'll show during the calculation
        private ProgressDialog progress=null;

        // this method is called before the background job starts. It works on the main thread
        @Override
        protected void onPreExecute() {

            // ProgressDialog is shown
            progress= ProgressDialog.show(MapsActivity.this,"Distance calculator","We are calcultating the distance...", true,false);
        }

        // this method works on a separate thread
        // it performs geocoding operations to retrieve the address of the points and calculates the distance in meters between them
        @Override
        protected String doInBackground(LatLng... params) {

            float[] distance=new float[1];
            try {
                // the Location class contains what we need to calculate distances

                Location.distanceBetween(params[0].latitude,params[0].longitude,params[1].latitude,params[1].longitude,distance);

                // geocoding operations
                List<Address> fromResult=geocoder.getFromLocation(params[0].latitude,params[0].longitude,1);
                List<Address> toResult=geocoder.getFromLocation(params[1].latitude,params[1].longitude,1);

                // the message informs the user about the distance from the marker to the point selected with the click
                // if we have got both the addresses, we use them to compose the message, otherwise we show only the distance
                if (fromResult.size()>0 && toResult.size()>0)
                {
                    return "The distance is " + Math.round(distance[0]) + " meters";
                }
                else
                    return "The distance is " + Math.round(distance[0]) + " meters";

            }
            catch (IOException e) {
                return "The distance is " + Math.round(distance[0]) + " meters";
            }
        }

        @Override
        protected void onPostExecute(String message)
        {
            if (progress!=null)
                progress.dismiss();

            // The builder of the window is instantiated
            AlertDialog.Builder builder=new AlertDialog.Builder(MapsActivity.this);
            builder.setTitle("Distance");
            builder.setMessage(message);

            // the Alert dialog appears
            builder.show();
        }
    }

    // this method only formats the message with addresses
    private String getAddressDescription(Address a)
    {
        String city=a.getLocality();
        String address=a.getAddressLine(0);

        return "'"+address+"' ("+city+")";

    }
}

The error thrown

FATAL EXCEPTION: main
  Process: com.example.matthewmcnabb.moyola, PID: 27349
  java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.matthewmcnabb.moyola/com.example.matthewmcnabb.moyola.MapsActivity}: java.lang.IllegalStateException: System services not available to Activities before onCreate()
      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2515)
      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2723)
      at android.app.ActivityThread.access$900(ActivityThread.java:172)
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1422)
      at android.os.Handler.dispatchMessage(Handler.java:102)
      at android.os.Looper.loop(Looper.java:145)
      at android.app.ActivityThread.main(ActivityThread.java:5832)
      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:1399)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
   Caused by: java.lang.IllegalStateException: System services not available to Activities before onCreate()
      at android.app.Activity.getSystemService(Activity.java:5259)
      at com.example.matthewmcnabb.moyola.MapsActivity.<init>(MapsActivity.java:51)
      at java.lang.reflect.Constructor.newInstance(Native Method)
      at java.lang.Class.newInstance(Class.java:1650)
      at android.app.Instrumentation.newActivity(Instrumentation.java:1079)
      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2505)
      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2723) 
      at android.app.ActivityThread.access$900(ActivityThread.java:172) 
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1422) 
      at android.os.Handler.dispatchMessage(Handler.java:102) 
      at android.os.Looper.loop(Looper.java:145) 
      at android.app.ActivityThread.main(ActivityThread.java:5832) 
      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:1399) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194) 
antonio
  • 18,044
  • 4
  • 45
  • 61
matthewoak
  • 49
  • 9

2 Answers2

5

I get thrown with the java.lang.IllegalStateException: System services not available to Activities before onCreate() error.

That is because you are trying to call methods inherited from Activity, like getSystemService(), from a field initializer. This will not work. You need to wait until onCreate(), and usually until after super.onCreate(), before calling methods like getSystemService().

Ive tried placing my code from the start in the onCreate() method but this doesn't work either.

In this sample app, I get the LocationManager in onCreate() of a fragment:

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);

    template=getActivity().getString(R.string.url);
    mgr=
        (LocationManager)getActivity().getSystemService(Context.LOCATION_SERVICE);
  }

The same principle will hold with onCreate() of an activity.

When I try to place the (LocationManager)getSystemService(Context.LOCATION_SERVICE); in the onCreate() it requires a permission

You need to have a <uses-permission> element in the manifest for ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION, depending on whether you plan on using GPS_PROVIDER or NETWORK_PROVIDER.

On Android 6.0+, if your targetSdkVersion is 23 or higher, you need to implement runtime permissions, as those permissions are dangerous.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
0

You try to get contexts and services in a constructor. This is wrong. The constructor is executed when the object is created, before it is attached to the Android framework.

Just move member initialization to onCreate().

18446744073709551615
  • 16,368
  • 4
  • 94
  • 127
  • thanks for your response. I am unsure how I can store the lat and long in a variable in the onCreate. I have tried this LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE); Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER); double longitude = location.getLongitude(); double latitude = location.getLatitude(); STARTING_MARKER_POSITION =new LatLng(longitude, latitude); LatLng distanceFrom = STARTING_MARKER_POSITION; – matthewoak Mar 22 '16 at 15:56
  • You see, getting latitude an longitude is tricky. If some application already is listening, you can get the last known location. If no application has been listening to location changes, there's no last known location. In addition, the location services may be just disabled in the settings (and if GPS is chosen, it will not work in a building). – 18446744073709551615 Mar 22 '16 at 16:00