Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
252 views
in Technique[技术] by (71.8m points)

android - Adding Markers in Background while Looping through ArrayList

I add Markers to a googleMap from an ArrayList of Objects. There are about 250 markers; I even have to convert them to bitmaps to customize them. It's a pretty resource intensive task. BUT It seriously blocks my UI thread.

Here is how I do it:

    final HashMap<Marker, NearLocation> markerIdMap = new HashMap<Marker, NearLocation>();
    for (final NearLocation result : MainActivity.nearLocationList) {

            // Do all the hard work here

    }

How can I do this somehow dynamically, after the map loads and it populates them as they are produced? I am not sure if I can do that by doing some of the work in the background, then when the marker is complete move it to the UI thread to add.

I know how to do this INDIVIDUALLY with an AsyncTask. Not sure though while I am looping through...

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As far as I know, adding the markers cannot be done outside the UI thread.

What you can do is perform all the preparations in background (create marker, convertion to bitmap, etc..). To spare the UI thread a bit when adding markers you could zoom in and use https://code.google.com/p/android-maps-extensions/ to only show the visible markers or cluster markers to bring down the amount, though 250 is not alot imo.

Here is a SO answer on the topic: Add markers dynamically on Google Maps v2 for Android

I have an app that has around ~4500 markers running fairly well using the first method (as long as it is not rapidly zoomed out that is). It should be noted that here I have chosen to make a splash screen with a progress bar, preparing all the markers before the user has any chance of opening the map.

If you want a simple mechanism for selecting surrounding markers without using 3rd part library you can do something like this: Android Maps v2 - animate camera to include most markers

An idea that comes to mind just now, if the creation of markers is really that expensive, is to add EventBus https://github.com/greenrobot/EventBus to your project.

Using EventBus you could do a long running preparation of markers inside an onEventAsync() method. Inside that method, whenever a marker is ready, post the new marker to UI EventBus.getDefault().post(marker) and catch it in onEventMainThread(Marker marker). Here you could save the marker in a list of all the prepared markers and if the map is currently open, add it.

Here is some of the code I use to prepare the markers in the app I mentioned earlier. It is used to show hydrants in an app used by the fire dept. At first startup all the hydrants are read from a set of CSV files and MarkerOptions for all ~4500 hydrants are created. Undoubtedly a lot of the code is of no use to you, just leaving it in case there is something that you can benefit from:

private List<HydrantHolder> mHydrants;
private Map<HydrantType, List<MarkerOptions>> mMarkers;

private class ReadHydrantsFiles extends
        AsyncTask<Void, Integer, List<HydrantHolder>> {

    File[] hydrantsFiles = new File[0];

    // Before running code in separate thread
    @Override protected void onPreExecute() {

        loadStarted();

        String filepath = paths.PATH_LOCAL_HYDRANTS;

        File hydrantsPath = new File(filepath);
        hydrantsFiles = hydrantsPath.listFiles(new FilenameFilter() {

            @Override public boolean accept(File dir, String filename) {
                return filename.toLowerCase(Locale.ENGLISH).contains(
                        Constants.FILETYPE_CSV);
            }
        });

        int lineCount = 0;

        if (hydrantsFiles == null || hydrantsFiles.length == 0) {
            if (!preferences.isFirstStartUp()) {
                // TODO notify user
            }
            Log.e(TAG, "Missing hydrants");
            if (moduleCallback != null) {
                moduleCallback.doneLoadingModule(toString());
            }
            this.cancel(false);
        } else {

            for (File file : hydrantsFiles) {
                // store linecount for visual progress update
                lineCount += Files.lineCount(file);

            }

        }

    }

    // The code to be executed in a background thread.
    @Override protected List<HydrantHolder> doInBackground(Void... args) {
        List<HydrantHolder> all_hydrants = new ArrayList<HydrantHolder>();
        // Directory path here
        int totalLineProgress = 0;

        // // required
        int indexLatitude = modulePreferences.indexLatitude();
        int indexLongitude = modulePreferences.indexLongitude();
        // optional
        int indexAddress = modulePreferences.indexAddress();
        int indexAddressNumber = modulePreferences.indexAddressNumber();
        int indexAddressRemark = modulePreferences.indexAddressRemark();
        int indexRemark = modulePreferences.indexRemark();
        // decimals
        int latitude_decimal = modulePreferences.latitude_decimal();
        int longitude_decimal = modulePreferences.longitude_decimal();

        if (indexLatitude <= 0 || indexLongitude <= 0)
            return all_hydrants;

        for (File file : hydrantsFiles) {
            BufferedReader in = null;
            try {

                String hydrantspath = paths.PATH_LOCAL_HYDRANTS;
                File hydrantsPath = new File(hydrantspath);

                // Read File Line By Line
                in = new BufferedReader(new InputStreamReader(
                        new FileInputStream(file), "windows-1252"));
                String strLine;
                while ((strLine = in.readLine()) != null) {

                    totalLineProgress++;

                    String[] hydrantParts = strLine.split(";", -1);

                    String errors = "";
                    final String hydrantType = file.getName().replace(
                            Constants.FILETYPE_CSV, "");

                    File[] iconFiles = hydrantsPath
                            .listFiles(new FilenameFilter() {

                                @Override public boolean accept(File dir,
                                        String filename) {
                                    if (filename.contains(hydrantType)
                                            && filename
                                                    .contains(Constants.FILETYPE_PNG)) {
                                        return true;
                                    }
                                    return false;
                                }
                            });

                    HydrantHolder.Builder hb = new HydrantHolder.Builder();
                    if (hydrantParts.length >= 5) {

                        hb.setHydrantType(hydrantType);
                        if (iconFiles.length != 0) {
                            hb.setIconPath(hydrantspath
                                    + File.separatorChar
                                    + iconFiles[0].getName());
                        }
                        hb.setLatitude(hydrantParts[indexLatitude],
                                latitude_decimal);
                        hb.setLongitude(hydrantParts[indexLongitude],
                                longitude_decimal);

                        if (indexAddress > 0)
                            hb.setAddress(hydrantParts[indexAddress]);
                        if (indexAddressNumber > 0)
                            hb.setAddressNumber(hydrantParts[indexAddressNumber]);
                        if (indexAddressRemark > 0)
                            hb.setAddressRemark(hydrantParts[indexAddressRemark]);
                        if (indexRemark > 0)
                            hb.setRemark(hydrantParts[indexRemark]);

                        if (hb.getErrors().isEmpty()) {
                            HydrantHolder hydrant = hb.build();
                            all_hydrants.add(hydrant);
                        } else {
                            // TODO write error file to Dropbox if possible,
                            // otherwise locally
                            Log.d(TAG, errors);
                        }
                    } else {
                        errors = "Missing information";
                    }

                    publishProgress(totalLineProgress);
                }
            } catch (InvalidPathException e) {
                Log.e(TAG, e.getMessage(), e);
            } catch (IOException e) {
                Log.e(TAG, e.getMessage(), e);
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                    }
                }
            }
        }
        Log.d(TAG, "" + all_hydrants.size());
        return all_hydrants;
    }

    // Update the progress
    @Override protected void onProgressUpdate(Integer... values) {
        // set the current progress of the progress dialog
        // if (progressDialog != null && values != null && values.length >
        // 0) {
        // progressDialog.setProgress(values[0]);
        // }
    }

    @Override protected void onPostExecute(List<HydrantHolder> hydrants) {
        // Device.releaseOrientation((Activity) context);

        Log.d(TAG, "Saved " + hydrants.size() + " hydrants");
        mHydrants = hydrants;

        new PrepareMarkerOptionsTask(hydrants).execute();

        super.onPostExecute(hydrants);
    }

}

private class PrepareMarkerOptionsTask extends
        AsyncTask<Void, Integer, Map<HydrantType, List<MarkerOptions>>> {
    // Before running code in separate thread
    List<HydrantHolder> mHydrants;

    public PrepareMarkerOptionsTask(List<HydrantHolder> hydrants) {
        this.mHydrants = hydrants;
        mMarkers = new HashMap<HydrantsModule.HydrantType, List<MarkerOptions>>();
    }

    @Override protected void onPreExecute() {

    }

    // The code to be executed in a background thread.
    @Override protected Map<HydrantType, List<MarkerOptions>> doInBackground(
            Void... arg) {

        for (HydrantHolder hydrant : mHydrants) {

            final String hydrant_type = hydrant.getHydrantType();
            final String hydrant_icon_path = hydrant.getIconPath();
            double latitude = hydrant.getLatitude();
            double longitude = hydrant.getLongitude();

            final LatLng position = new LatLng(latitude, longitude);

            final String address = hydrant.getAddress();
            final String addressNumber = hydrant.getAddressNumber();
            final String addressremark = hydrant.getAddressRemark();
            final String remark = hydrant.getRemark();

            // Log.d(TAG, hydrant.toString());

            BitmapDescriptor icon = BitmapDescriptorFactory
                    .defaultMarker(BitmapDescriptorFactory.HUE_RED);

            if (!hydrant_icon_path.isEmpty()) {
                File iconfile = new File(hydrant_icon_path);
                if (iconfile.exists()) {
                    BitmapDescriptor loaded_icon = BitmapDescriptorFactory
                            .fromPath(hydrant_icon_path);
                    if (loaded_icon != null) {
                        icon = loaded_icon;
                    } else {
                        Log.e(TAG, "loaded_icon was null");
                    }
                } else {
                    Log.e(TAG, "iconfile did not exist: "
                            + hydrant_icon_path);
                }
            } else {
                Log.e(TAG, "iconpath was empty on hydrant type: "
                        + hydrant_type);
            }

            StringBuffer snippet = new StringBuffer();
            if (!address.isEmpty())
                snippet.append("
" + address + " " + addressNumber);
            if (addressremark.isEmpty())
                snippet.append("
" + addressremark);
            if (!remark.isEmpty())
                snippet.app

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...