JamesDoh96
JamesDoh96

Reputation: 41

How to asynchronusly read JSON and use its data in android?

I am trying to download code from a link as JSON(JSON Link: https://api.myjson.com/bins/ehzqu) and render it. Here is the code:

public class FetchJSON extends AsyncTask<Void,Void,Void> {
    String data = "";
    String id, name, address, lat, lng, type = "";

    @Override
    protected Void doInBackground(Void... voids) {
        Log.i("", "TEST");
        try {
            URL url = new URL("https://api.myjson.com/bins/ehzqu");
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            InputStream inputStream = httpURLConnection.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line = "";
            while(line!=null){
                line = bufferedReader.readLine();
                data = data + line;
            }
            Log.i("", "TEST2");
            JSONArray JA = new JSONArray(data);
            for( int i = 0 ; i < JA.length();i++)
            {
                JSONObject JO = (JSONObject) JA.get(i);
                //Log.i("", JO.toString());

                id = (String) JO.get("id");
                name = (String) JO.get("name");
                address = (String) JO.get("address");

                lat = JO.get("lat").toString();
                lng = JO.get("lng").toString();
                Log.i("JSON Values", lat + " " + lng);  //////////////////////////
                type = (String) JO.get("type");
            }
            Log.i("", "TEST3");

        } catch (MalformedURLException e) {
            e.printStackTrace();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        }


        return null;
    }


    @Override
    protected void onPostExecute(Void aVoid) {

        TrackDifferentLocation.data.setText(this.data);


        super.onPostExecute(aVoid);
    }

    public void printLat()
    {
        Log.i("", lat);
    }

    public String getLat (){
        return lat;
    }
    public String getLng (){
        return lng;
    }
    public String getName (){
        return name;
    }
    public String getAddress (){
        return address;
    }
    public String getID (){
        return id;
    }
    public String getType (){
        return type;
    }
}

In another class, I then have the following;

public class TrackDifferentLocation extends AppCompatActivity implements OnMapReadyCallback {

    private GoogleMap mMap;

    String json_string;
    public static TextView data;

    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Toast.makeText(this, "Tracking location...", Toast.LENGTH_LONG).show();
        setContentView(R.layout.activity_track_different_location);
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map_fragment);
        mapFragment.getMapAsync(this);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        Log.i("", "onMapReady()");
        FetchJSON f = new FetchJSON();

        // f.execute();
        // f.printLat();
        //Log.i("", f.getLat());
        //Log.i("", f.getLng());

        double latitude = Double.valueOf(f.getLat());
        double longitude = Double.valueOf(f.getLng());
        LatLng latlng = new LatLng(latitude,longitude);
        //Log.d("Marker: ", m.getTitle());
        mMap.addMarker(new MarkerOptions().position(latlng));
    }

    //Part of menu see following
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == android.R.id.home){
            //ends the activity
            this.finish();
        }
        switch (item.getItemId()) {
            case R.id.mapTypeNone:
                mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
                break;
            case R.id.mapTypeNormal:
                mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                break;
            case R.id.mapTypeTerrain:
                mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
                break;
            case R.id.mapTypeSatellite:
                mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
                break;
            case R.id.mapTypeHybrid:
                mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
                break;
            default:
                break;
        }
        return super.onOptionsItemSelected(item);
    }

But upon running this code, the application on android, crashes.

If I hard-code the latitude and longitude as set figures in the getLat/getLng functions, the code works perfectly. Please note, I have removed the import and package names for this question.

EDIT:

public class TrackDifferentLocation extends AppCompatActivity implements OnMapReadyCallback {

    private GoogleMap mMap;
    LatLng mLatlng;
    String json_string;
    public static TextView data;
    LatLng latLng = null;

    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Toast.makeText(this, "Tracking location...", Toast.LENGTH_LONG).show();
        setContentView(R.layout.activity_track_different_location);
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map_fragment);
        mapFragment.getMapAsync(this);
            getSupportActionBar().setDisplayShowHomeEnabled(true);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        //new FetchJSON.execute(); //Not valid syntax
        FetchJSON f = new FetchJSON();
    }

@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    Log.i("", "onMapReady()");
    displayMarkers();
}
    private void displayMarkers(){
        if(mMap == null) return;
        if(mLatlng == null) return;

        mMap.addMarker(new MarkerOptions().position(mLatlng));
    }

    //Part of menu see following
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == android.R.id.home){
            //ends the activity
            this.finish();
        }
        switch (item.getItemId()) {
            case R.id.mapTypeNone:
                mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
                break;
            case R.id.mapTypeNormal:
                mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                break;
            case R.id.mapTypeTerrain:
                mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
                break;
            case R.id.mapTypeSatellite:
                mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
                break;
            case R.id.mapTypeHybrid:
                mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
                break;
            default:
                break;
        }
        return super.onOptionsItemSelected(item);
    }
    class FetchJSON extends AsyncTask<String, Integer, LatLng> {
        String JSONStr = "";
        String name, address, type = "";
        String lat = "";
        String lng = "";
        String id = "";
        //double lat, lng;
        int idInt;
        double latDouble = -1;
        double lngDouble = -1;

        protected LatLng doInBackground(String... args) {
            //LatLng latLng = null;
            try {
                URL url = new URL("https://api.myjson.com/bins/ehzqu");
                HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                InputStream inputStream = httpURLConnection.getInputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                String line = "";
                while (line != null) {
                    line = bufferedReader.readLine();
                    JSONStr = JSONStr + line;
                }
                Log.i("", "TEST2");

                JSONObject obj = new JSONObject(JSONStr);
                JSONArray array = obj.getJSONArray("server response");
                for (int i = 0; i < array.length(); i++) {
                    JSONObject o = array.getJSONObject(i);
                    id = o.optString("id");
                    name = o.optString("name");
                    address = o.optString("address");
                    lat = o.optString(lat);
                    lng = o.optString("lng");
                    latDouble = Double.parseDouble(lat);
                    lngDouble = Double.parseDouble(lng);

                    latLng = new LatLng(latDouble, lngDouble);

                    Log.i("JSON Values", lat + " " + lng);
                    type = o.optString("type");
                }
            } catch (Exception ex) {
                Log.e(TAG, "FetchJSON --- " + ex.getMessage());
            }
            return latLng;
        }

        protected void onPostExecute(LatLng latLng) {
            if (latLng != null) {
                mLatlng = latLng;
                displayMarkers();
            }
        }
        private void displayMarkers(){
            if(mMap == null) return;
            if(mLatlng == null) return;

            mMap.addMarker(new MarkerOptions().position(mLatlng));
        }
}
}

Upvotes: 1

Views: 126

Answers (2)

Bentaye
Bentaye

Reputation: 9766

FetchJSON is an asynchronous task, so when you try to access f.getLat(), it has no value yet because the task has not completed, you need to wait for the FetchJSON object to complete before trying to access the getLat() getter.

I reckon you can probably do something along these lines:

pass the map to the FetchJSON object:

@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    Log.i("", "onMapReady()");
    FetchJSON f = new FetchJSON(mMap);
}

and set the marker in your FetchJSON when the latitude and longitude are known:

@Override
protected Void doInBackground(Void... voids) {
    ....
    lat = JO.get("lat").toString();
    lng = JO.get("lng").toString();
    type = (String) JO.get("type");

    // Set the marker here:
    double latitude = Double.valueOf(f.getLat());
    double longitude = Double.valueOf(f.getLng());
    LatLng latlng = new LatLng(latitude,longitude);
    mMap.addMarker(new MarkerOptions().position(latlng));
    ....
}

Or better practice is probably to do it in the onPostExecute method, which is called after the doInBackground method (See API here):

LatLng latlng = null;

@Override
protected Long doInBackground(URL... urls) {
    ...
    double latitude = Double.valueOf(f.getLat());
    double longitude = Double.valueOf(f.getLng());
    latlng = new LatLng(latitude,longitude);
    ....
}

@Override
protected void onPostExecute(Long result) {
    if(latlng != null)
        mMap.addMarker(new MarkerOptions().position(latlng));
}

Also, you are not reading the JSON correctly, you are expecting a JSONArray when it is actually a JSONObject, with a JSONArray inside. This example works for the JSON you get at your link:

String data = "{\"server response\":[{\"id\":\"991\",\"name\":\"GPSname\",\"address\":\"GPSaddress\",\"lat\":\"52.948616\",\"lng\":\"-1.169131\",\"type\":\"GPStype\"}]}";
Log.i("TAG", data);

String id, name, address, lat, lng, type;
try {
    JSONObject json = new JSONObject(data);
    JSONArray serverResponse = json.getJSONArray("server response");

    for (int i = 0; i < serverResponse.length(); i++) {
        JSONObject JO = (JSONObject) serverResponse.get(i);
        id = (String) JO.get("id");
        name = (String) JO.get("name");
        address = (String) JO.get("address");

        lat = JO.get("lat").toString();
        lng = JO.get("lng").toString();
        type = (String) JO.get("type");

        Log.i("TAG", String.format(
            "JSON Values: id=%s, name=%s, address=%s, lat=%s, lng=%s, type=%s", 
            id, name, address, lat, lng, type));
    }
} catch (Exception e) {
    Log.e("TAG", "exception", e);
}

Prints out

JSON Values: id=991, name=GPSname, address=GPSaddress, lat=52.948616, lng=-1.169131, type=GPStype

Upvotes: 3

Barns
Barns

Reputation: 4849

The data you showed in your link is not an JSONArray.

{"server response":[{"id":"991","name":"GPSname","address":"GPSaddress","lat":"52.948616","lng":"-1.169131","type":"GPStype"}]}

It is a JSONObject with a JSONArray child.

.

EDIT

Create a class variable for the Latlng values.

Latlng mLatlng;

Now in your onCreate() method add: loadLocation();

Here is the new code:

 @Override
 public void onMapReady(GoogleMap googleMap) {
     mMap = googleMap;

     displayMarker();
 }


 private void loadLocation() {
     new FetchJSON().execute();
 }


 class FetchJSON extends AsyncTask<String, Integer, LatLng> {

     @Override
     protected LatLng doInBackground(String... params) {
         LatLng latLng = null;
         try {
             URL url = new URL("https://api.myjson.com/bins/ehzqu");
             HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
             InputStream inputStream = httpURLConnection.getInputStream();
             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

             StringBuilder stringBuilder = new StringBuilder();
             String line = "";
             while ((line = bufferedReader.readLine()) != null) {
                 stringBuilder.append(line).append("\n");
             }
             bufferedReader.close();

             String json = stringBuilder.toString();

             Log.e(TAG, "Return = " + json);
             String lat= "";
             String lng= "";
             JSONObject obj = new JSONObject(json);
             JSONArray array = obj.getJSONArray("server response");
             for(int i = 0; i < array.length(); i++){
                 JSONObject o = array.getJSONObject(i);

                 lat = o.optString("lat");
                 lng = o.optString("lng");
             }

             Log.e(TAG, "Lat = " + lat);
             Log.e(TAG, "lng = " + lng);

             double latDouble = Double.parseDouble(lat);
             double lngDouble = Double.parseDouble(lng);

             latLng = new LatLng(latDouble, lngDouble);
         }
         catch (Exception ex) {
             Log.e(TAG, "doInBackground --- " + ex.getMessage());
         }
         return latLng;
     }


     @Override
     protected void onPostExecute(LatLng latLng) {
         try{
             if(latLng != null){
                 mLatLng = latLng;
                 displayMarker();
             }

         }
         catch(Exception ex){
             Log.e(TAG, "onPostExecute" + ex.getMessage());
         }
     }

 }

 private void displayMarker(){
     if(mMap == null) return;
     if(mLatLng == null) return;

     MarkerOptions markerOption = new MarkerOptions();
     markerOption.position(mLatLng);
     mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mLatLng, zoomFactor));
     mMap.addMarker(markerOption);
 }

The result on my device:

enter image description here

When your GoogleMap is ready it will call displayMarkers(). If the "mLatLng" is null because the FetchJSON is not finished it nothing happens.

You could call FetchJSON from the onMapReady() method as suggested, but why not download the data in a background thread while you system is getting the GoogleMap ready?

Upvotes: 2

Related Questions