簡體   English   中英

緩存Google Map V2標記位圖

[英]Caching google map v2 markers bitmaps

這將需要一些時間,但是如果您能忍受我,不勝感激,並且我相信這對其他人也將是有用的。

我在我的Android應用中使用Google Map放置不同的標記。 每個標記屬於一個由位圖表示的類別。 我一直在使用“ 有效 顯示位圖”中BitmapFun示例在我的應用程序中緩存位圖,並嘗試使用Google地圖標記實現相同的解決方案。

我的代碼已添加到示例的ImageWorker.java中,如下所示(BitmapWorkerTask已經存在,並且已更新為可以處理標記):

private static Map<Marker, BitmapWorkerTask> markerToTaskMap = new HashMap<Marker, BitmapWorkerTask>();

public void loadImage(Object data, Marker marker) {
    if (data == null) {
        return;
    }

    BitmapDrawable value = null;

    if (mImageCache != null) {
        value = mImageCache.getBitmapFromMemCache(String.valueOf(data));
    }

    if (value != null) {
        // Bitmap found in memory cache
        marker.setIcon(BitmapDescriptorFactory.fromBitmap(value.getBitmap()));
    } else if (cancelPotentialWork(data, marker)) {
        final BitmapWorkerTask task = new BitmapWorkerTask(marker);
        markerToTaskMap.put(marker, task);

        task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR, data);
    }
}

private class BitmapWorkerTask extends AsyncTask<Object, Void, BitmapDrawable> {
    private Object data;
    private WeakReference<ImageView> imageViewReference = null;
    private WeakReference<Marker> markerReference = null;

    public BitmapWorkerTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    public BitmapWorkerTask(Marker marker) {
        markerReference = new WeakReference<Marker>(marker);
    }

    /**
     * Background processing.
     */
    @Override
    protected BitmapDrawable doInBackground(Object... params) {
        if (BuildConfig.DEBUG) {
            Log.d(TAG, "doInBackground - starting work");
        }

        data = params[0];
        final String dataString = String.valueOf(data);
        Bitmap bitmap = null;
        BitmapDrawable drawable = null;

        // Wait here if work is paused and the task is not cancelled
        synchronized (mPauseWorkLock) {
            while (mPauseWork && !isCancelled()) {
                try {
                    mPauseWorkLock.wait();
                } catch (InterruptedException e) {}
            }
        }

        // If the image cache is available and this task has not been cancelled by another
        // thread and the ImageView that was originally bound to this task is still bound back
        // to this task and our "exit early" flag is not set then try and fetch the bitmap from
        // the cache
        if (mImageCache != null && !isCancelled() && (getAttachedImageView() != null || getAttachedMarker() != null)
                && !mExitTasksEarly) {
            bitmap = mImageCache.getBitmapFromDiskCache(dataString);
        }

        // If the bitmap was not found in the cache and this task has not been cancelled by
        // another thread and the ImageView that was originally bound to this task is still
        // bound back to this task and our "exit early" flag is not set, then call the main
        // process method (as implemented by a subclass)
        if (bitmap == null && !isCancelled() && (getAttachedImageView() != null || getAttachedMarker() != null)
                && !mExitTasksEarly) {
            bitmap = processBitmap(params[0]);
        }

        // If the bitmap was processed and the image cache is available, then add the processed
        // bitmap to the cache for future use. Note we don't check if the task was cancelled
        // here, if it was, and the thread is still running, we may as well add the processed
        // bitmap to our cache as it might be used again in the future
        if (bitmap != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                // Running on Honeycomb or newer, so wrap in a standard BitmapDrawable
                drawable = new BitmapDrawable(mResources, bitmap);
            } else {
                // Running on Gingerbread or older, so wrap in a RecyclingBitmapDrawable
                // which will recycle automagically
                drawable = new RecyclingBitmapDrawable(mResources, bitmap);
            }

            if (mImageCache != null) {
                mImageCache.addBitmapToCache(dataString, drawable);
            }
        }

        if (BuildConfig.DEBUG) {
            Log.d(TAG, "doInBackground - finished work");
        }

        return drawable;
    }

    /**
     * Once the image is processed, associates it to the imageView
     */
    @Override
    protected void onPostExecute(BitmapDrawable value) {
        // if cancel was called on this task or the "exit early" flag is set then we're done
        if (isCancelled() || mExitTasksEarly) {
            value = null;
        }

        if (imageViewReference != null) {
            final ImageView imageView = getAttachedImageView();
            if (value != null && imageView != null) {
                if (BuildConfig.DEBUG) {
                    Log.d(TAG, "onPostExecute - setting bitmap");
                }
                setImageDrawable(imageView, value);
            }
        } else if (markerReference != null) {
            final Marker marker = getAttachedMarker();
            if (value != null && marker != null) {
                if (BuildConfig.DEBUG) {
                    Log.d(TAG, "onPostExecute - setting marker bitmap");
                }
                markerToTaskMap.remove(marker);
                marker.setIcon(BitmapDescriptorFactory.fromBitmap(value.getBitmap()));
            }
        }
    }

    @Override
    protected void onCancelled(BitmapDrawable value) {
        super.onCancelled(value);
        synchronized (mPauseWorkLock) {
            mPauseWorkLock.notifyAll();
        }
    }

    /**
     * Returns the ImageView associated with this task as long as the ImageView's task still
     * points to this task as well. Returns null otherwise.
     */
    private ImageView getAttachedImageView() {
        if (imageViewReference == null) {
            return null;
        }

        final ImageView imageView = imageViewReference.get();
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

        if (this == bitmapWorkerTask) {
            return imageView;
        }

        return null;
    }

    private Marker getAttachedMarker() {
        if (markerReference == null) {
            return null;
        }

        final Marker marker = markerReference.get();
        final BitmapWorkerTask bitmapWorkerTask = markerToTaskMap.get(marker); //getBitmapWorkerTask(marker);

        if (this == bitmapWorkerTask) {
            return marker;
        }

        return null;
    }
}

public static boolean cancelPotentialWork(Object data, Marker marker) {
    final BitmapWorkerTask bitmapWorkerTask = markerToTaskMap.get(marker);

    if (bitmapWorkerTask != null) {
        final Object bitmapData = bitmapWorkerTask.data;
        if (bitmapData == null || !bitmapData.equals(data)) {
            bitmapWorkerTask.cancel(true);
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "cancelPotentialWork - cancelled work for " + data);
            }
        } else {
            // The same work is already in progress.
            return false;
        }
    }
    return true;
}

如果您熟悉BitmapFun示例,則可以看到,除了使用AsyncDrawable將位圖連接到其加載的AsyncTask之外,其他操作幾乎與在ImageView中使用位圖相同。 由於我無法擴展Marker類(這是最終的),並且沒有getIcon()方法,因此我必須維護一個hashmap(markerToTaskMap)才能完成此工作。

該解決方案似乎總體上可行,除了一些小故障,即我為標記獲得錯誤的位圖。 我不知道為什么。 OOB示例代碼不會發生這種情況。

感謝有人可以在這里幫助我。 謝謝。

我可以要求您不要這樣做,以為您提供幫助。

如果您想進行優化,請更好地了解您的敵人。 對Google Maps Android API v2的每次調用都將涉及其他過程。 而且大多數都需要在主線程上完成。

因為對API的每次調用都同步到達其他進程,所以它將阻塞用戶界面。 例如,在一個體面的電話上添加2000個標記將花費1秒(經過測試)。 另一方面,加載20個小的位圖以表示onCreate類別將花費不到100毫秒(未經聲明的聲明)。 因此,您的代碼甚至會減慢速度,因為您至少有2個調用來添加MarkeraddMarkersetIcon

只需使用BitmapDescriptorFactory.fromResource將所有Bitmap加載到Map<Category, BitmapDescriptor>一次,然后在創建Marker使用它們。

總結一下:除非有問題,否則不要進行優化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM