My UI is blocked using AsyncTask with distancematr

2020-02-15 08:32发布

问题:

I'm using google maps to show some markers. The markers are download from a database and, at the same time, I get the distancematrix from google api, between the current position of the user and the marker that I get from the database.

My problem is that I was doing this with .get, bloking my ui (I've read that .get blocked the ui:

dataFromAsyncTask = testAsyncTask.get();

Now, I'm trying to do the same without blocking the ui, but I'm not be able to get at the same time, or in a good way, the distance for this markers.

I appreciate some help, please.

This is my code with my old and wrong .get:

 for (City city : listCity.getData()) {
        geoPoint = city.getLocation();
    nameBeach = city.getName();

    if (geoPoint == null) {

    } else {
        latitude = String.valueOf(geoPoint.getLatitude());
        longitude = String.valueOf(geoPoint.getLongitude());

        startRetrievenDistanceAndDuration();

        try {
            dataFromAsyncTask = testAsyncTask.get();
        } catch (InterruptedException i) {

        } catch (ExecutionException e) {
        }

        mMap.addMarker(new MarkerOptions().position(new LatLng(geoPoint.getLatitude(), geoPoint.getLongitude()))
                .title(nameCity)
                .snippet(dataFromAsyncTask)
                .icon(BitmapDescriptorFactory.defaultMarker()));
    }
}

startRetrievenDistanceAndDuration method:

private void startRetrievenDistanceAndDuration() {
final String url;

testAsyncTask = new DistanceBetweenLocations(new FragmentCallback() {

    @Override
    public void onTaskDone(String result) {

    }
});
url = "https://maps.googleapis.com/maps/api/distancematrix/json?origins=" + currentLatitude + "," + currentlongitude + "&destinations=" + latitude + "," + longitude + "&key=xxx";
testAsyncTask.execute(new String[]{url});
}
public interface FragmentCallback {
    public void onTaskDone(String result);

AsyncTask class:

        @Override
        protected String doInBackground(String... params) {
            HttpURLConnection urlConnection = null;
            URL url = null;
            StringBuilder result = null;
            String duration = "";
            String distance = "";

            try {
                url=new URL(params[0]);
            }catch (MalformedURLException m){

            }
            try {
                urlConnection = (HttpURLConnection) url.openConnection();
            }catch (IOException e){}

            try {
                InputStream in = new BufferedInputStream(urlConnection.getInputStream());
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                result = new StringBuilder();
                String line;
                while((line = reader.readLine()) != null) {
                    result.append(line);
                }
            }catch (IOException e){

            } finally {
                urlConnection.disconnect();
            }

            try {
                JSONObject jsonObject = new JSONObject(result.toString());
                JSONArray jsonArray = jsonObject.getJSONArray("rows");
                JSONObject object_rows = jsonArray.getJSONObject(0);
                JSONArray jsonArrayElements = object_rows.getJSONArray("elements");
                JSONObject  object_elements = jsonArrayElements.getJSONObject(0);
                JSONObject object_duration = object_elements.getJSONObject("duration");
                JSONObject object_distance = object_elements.getJSONObject("distance");

                duration = object_duration.getString("text");
                distance = object_distance.getString("text");

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

            return distance + ", " + duration;

        }

        @Override
        protected void onPostExecute(String result) {
            mFragmentCallback.onTaskDone(result);
        }
}

I'm trying to do this, but I only show the last marker of my list:

Call in the loop the method:

startRetrievenDistanceAndDuration();

And in onTaskDone try to put the marker, but only get the last marker of my list

@Override
            public void onTaskDone(String result) {
                mMap.addMarker(new MarkerOptions().position(new LatLng(geoPoint.getLatitude(), geoPoint.getLongitude()))
                        .title(nameBeach)
                        .snippet(result)
                        .icon(BitmapDescriptorFactory.defaultMarker()));

            }

UPDATED AFTER CHANGES: (still don't work) I can parse the data in Asynctask and send it in onPostExecute, but I only get one value, and not the 9 values that I have....

MAIN ACTIVITY:

DistanceBetweenLocations task = new DistanceBetweenLocations(mlatituDouble, mlongitudeDouble){
                    @Override
                    protected void onPostExecute(HashMap<String, String> result) {
                        super.onPostExecute(result);

                        String name = result.get("beachName");
                        String distance = result.get("distance");
                        String duration = result.get("duration");
                        String latitue = result.get("latitude");
                        String longitude = result.get("longitude");

                        Double mlatituDouble = Double.parseDouble(latitue);
                        Double mlongitudeDouble = Double.parseDouble(longitude);


                        if (mMap == null) {

                            mMap = ((SupportMapFragment) getFragmentManager().findFragmentById(R.id.mapView))
                                    .getMap();

                                Toast.makeText(getActivity(), "mMap NO null", Toast.LENGTH_SHORT).show();
                                mMap.addMarker(new MarkerOptions().position(new LatLng(mlatituDouble, mlongitudeDouble))
                                        .title(name)
                                        .snippet(distance + " " + duration)
                                        .icon(BitmapDescriptorFactory.defaultMarker()));
                        }
                    }
                };

                task.execute();

ASYNCTASK CLASS:.

public class DistanceBetweenLocations extends AsyncTask<String, String, HashMap<String, String>> {

    Double currentLatitude;
    Double currentlongitude;
    public BeachMap beachMap;
    public BackendlessCollection<Beach> dataBeach;
    public GoogleMap mMap;
    String latitude;
    String longitude;
    HashMap<String, String> map;

    public DistanceBetweenLocations(Double currentLatitude, Double currentlongitude){
        this.currentLatitude = currentLatitude;
        this.currentlongitude = currentlongitude;
    }

    @Override
    protected HashMap<String, String> doInBackground(String... params) {

        dataBeach = beachMap.listBeach;

        for (Beach city : dataBeach.getData()) {
            GeoPoint geoPoint = city.getLocation();
            String nameBeach = city.getName();

            if (geoPoint == null) {

            } else {
                latitude = String.valueOf(geoPoint.getLatitude());
                longitude = String.valueOf(geoPoint.getLongitude());

                HttpURLConnection urlConnection = null;
                URL url = null;
                StringBuilder result = null;
                String duration = "";
                String distance = "";

                try {
                    url = new URL("https://maps.googleapis.com/maps/api/distancematrix/json?origins=" + currentLatitude + "," + currentlongitude + "&destinations=" + latitude + "," + longitude + "&key=xxxx");
                } catch (MalformedURLException m) {

                }
                try {
                    urlConnection = (HttpURLConnection) url.openConnection();
                } catch (IOException e) {
                }

                try {
                    InputStream in = new BufferedInputStream(urlConnection.getInputStream());
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    result = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        result.append(line);
                    }
                } catch (IOException e) {

                } finally {
                    urlConnection.disconnect();
                }

                try {
                    JSONObject jsonObject = new JSONObject(result.toString());
                    JSONArray jsonArray = jsonObject.getJSONArray("rows");
                    JSONObject object_rows = jsonArray.getJSONObject(0);
                    JSONArray jsonArrayElements = object_rows.getJSONArray("elements");
                    JSONObject object_elements = jsonArrayElements.getJSONObject(0);
                    JSONObject object_duration = object_elements.getJSONObject("duration");
                    JSONObject object_distance = object_elements.getJSONObject("distance");

                    duration = object_duration.getString("text");
                    distance = object_distance.getString("text");


                    map = new HashMap<String, String>();
                    map.put("beachName", nameBeach);
                    map.put("distance", distance);
                    map.put("duration", duration);
                    map.put("latitude", latitude);
                    map.put("longitude", longitude);


                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }
        return map;
    }

}

回答1:

I'll use your last code (the "UPDATED AFTER CHANGES"), ok?

If I get it right, your DistanceBetweenLocations result will be a list of beaches geolocation data. So, on every iteration of the for loop in doInBackground, you are replacing the value of "map" variable, this is your problem.

To solve your problem, you can have a List of HashMap or a List of a Pojo like this:

public class BeachPojo {
    private String beachName;
    private String distance;
    private String duration;
    private String latitude;
    private String longitude;

    public String getBeachName() {
        return beachName;
    }

    public void setBeachName(String beachName) {
        this.beachName = beachName;
    }

    public String getDistance() {
        return distance;
    }

    public void setDistance(String distance) {
        this.distance = distance;
    }

    public String getDuration() {
        return duration;
    }

    public void setDuration(String duration) {
        this.duration = duration;
    }

    public String getLatitude() {
        return latitude;
    }

    public void setLatitude(String latitude) {
        this.latitude = latitude;
    }

    public String getLongitude() {
        return longitude;
    }

    public void setLongitude(String longitude) {
        this.longitude = longitude;
    }
}

Using the Pojo, your AsyncTask will be like this:

public class DistanceBetweenLocations extends AsyncTask<String, String, List<BeachPojo>> {

    Double currentLatitude;
    Double currentlongitude;
    public BeachMap beachMap;
    public BackendlessCollection<Beach> dataBeach;
    public GoogleMap mMap;
    String latitude;
    String longitude;


    public DistanceBetweenLocations(Double currentLatitude, Double currentlongitude){
        this.currentLatitude = currentLatitude;
        this.currentlongitude = currentlongitude;
    }

    @Override
    protected List<BeachPojo> doInBackground(String... params) {
        List<BeachPojo> list = new ArrayList<BeachPojo>();
        BeachPojo pojo;

        dataBeach = beachMap.listBeach;

        for (Beach city : dataBeach.getData()) {
            GeoPoint geoPoint = city.getLocation();
            String nameBeach = city.getName();

            if (geoPoint == null) {
            } else {
                latitude = String.valueOf(geoPoint.getLatitude());
                longitude = String.valueOf(geoPoint.getLongitude());

                HttpURLConnection urlConnection = null;
                URL url = null;
                StringBuilder result = null;
                String duration = "";
                String distance = "";

                try {
                    url = new URL("https://maps.googleapis.com/maps/api/distancematrix/json?origins=" + currentLatitude + "," + currentlongitude + "&destinations=" + latitude + "," + longitude + "&key=xxxx");
                } catch (MalformedURLException m) {

                }

                try {
                    urlConnection = (HttpURLConnection) url.openConnection();
                } catch (IOException e) {
                }

                try {
                    InputStream in = new BufferedInputStream(urlConnection.getInputStream());
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    result = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        result.append(line);
                    }
                } catch (IOException e) {

                } finally {
                    urlConnection.disconnect();
                }

                try {
                    JSONObject jsonObject = new JSONObject(result.toString());
                    JSONArray jsonArray = jsonObject.getJSONArray("rows");
                    JSONObject object_rows = jsonArray.getJSONObject(0);
                    JSONArray jsonArrayElements = object_rows.getJSONArray("elements");
                    JSONObject object_elements = jsonArrayElements.getJSONObject(0);
                    JSONObject object_duration = object_elements.getJSONObject("duration");
                    JSONObject object_distance = object_elements.getJSONObject("distance");

                    duration = object_duration.getString("text");
                    distance = object_distance.getString("text");

                    pojo = new BeachPojo();
                    pojo.setBeachName(nameBeach);
                    pojo.setDistance(distance);
                    pojo.setDuration(duration);
                    pojo.setLatitude(latitude);
                    pojo.setLongitude(longitude);

                    list.add(pojo);

                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }
}

Now you have a List to iterate. I have adjusted the code a little bit to this goal:

DistanceBetweenLocations task = new DistanceBetweenLocations(mlatituDouble, mlongitudeDouble){
    @Override
    protected void onPostExecute(List<BeachPojo> result) {
        super.onPostExecute(result);

        if (mMap == null) {
            mMap = ((SupportMapFragment) getFragmentManager().findFragmentById(R.id.mapView))
                    .getMap();
        }

        Double beachLatitude;
        Double beachLongitude;

        for (BeachPojo pojo : result) {
            beachLatitude = Double.parseDouble(pojo.getLatitude());
            beachLongitude = Double.parseDouble(pojo.getLongitude());

            mMap.addMarker(new MarkerOptions().position(new LatLng(beachLatitude, beachLongitude))
                .title(pojo.getBeachName())
                .snippet(pojo.getDistance() + " " + pojo.getDuration())
                .icon(BitmapDescriptorFactory.defaultMarker()));
        }
    }
};

task.execute();

I hope you understand the idea of returning a List from your AsyncTask and loop throught the result on onPostExecute method.

Note: this is an implementation without knowing the real code, then you should adjust to your reality.



回答2:

I'm not exactly sure what you're trying to do but I think you've made this more complicated then it has to be.

From what I understand you have a list of City objects and you use them to construct some URLs from which you retrieve a JSON object that is use to construct MarkerOptions objects.

You can do that using a AsyncTask like this:

public class Task extends AsyncTask<City, Void, Markers> {

    String currentLatitude;
    String currentlongitude;

    public Task(String currentLatitude, String currentlongitude){
        this.currentLatitude = currentLatitude;
        this.currentlongitude = currentlongitude;
    }

    @Override
    protected String doInBackground(City... cities) {
        final Markers mMap = ...;
        for (City city : cities) {
            GeoPoint geoPoint = city.getLocation();
            String nameBeach = city.getName();

            if (geoPoint != null) {
                String latitude = String.valueOf(geoPoint.getLatitude());
                String longitude = String.valueOf(geoPoint.getLongitude());

                HttpURLConnection urlConnection = null;
                BufferedReader reader = null;
                try {
                    URL url = new URL("https://maps.googleapis.com/maps/api/distancematrix/json?origins=" + currentLatitude + "," + currentlongitude + "&destinations=" + latitude + "," + longitude + "&key=xxx";);
                    urlConnection = (HttpURLConnection) url.openConnection();
                    reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
                    StringBuilder result = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        result.append(line);
                    }
                    JSONObject jsonObject = new JSONObject(result.toString()).getJSONArray("rows").getJSONObject(0).getJSONArray("elements").getJSONObject(0);
                    String duration = jsonObject.getJSONObject("duration").getString("text");
                    String distance = jsonObject.getJSONObject("distance").getString("text");

                    mMap.addMarker(new MarkerOptions().position(new LatLng(geoPoint.getLatitude(), geoPoint.getLongitude()))
                            .title(nameBeach)
                            .snippet(distance + ", " + duration)
                            .icon(BitmapDescriptorFactory.defaultMarker()));
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if(reader!=null){
                        try {
                            reader.close();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    if (urlConnection != null) {
                        try {
                            urlConnection.disconnect();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        return mMap;
    }
}

And here is how you can use this task.

public class Login extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(...);
        Task task = new Task(currentLatitude, currentlongitude){
            @Override
            protected void onPostExecute(Markers markers) {
                super.onPostExecute(markers);
                //This runs on the UI thread and "markers" is the "mMap" object that was create on the background thread.
            }
        };
        List<City> cities = ....
        task.execute(cities.toArray(new City[cities.size()]));
    }
}

The idea is that you need to execute all the long running operation in the AsyncTask's doInBackground(...) method. Also, you don't need to create other objects to deal with the AsyncTask response, you can override the task's onPostExecute(...) inside the class you've created the task in.