一)高效加载大图
1)读取Bitmap尺寸和类型
BitmapFactory类提供了几个生成bitmap的解码方法:
decodeByteArray()
decodeFile()
decodeResource()
decodeStream()
这些方法会为Bitmap分配内存,因此很容易产生OutOfMemory异常。
可以通过BitmapFactory.Options类来设置解码选项,设置inJusteDecodeBounds属性为true, 则上述方法先不分配内存,而是设置Options的outWidth, outHeight 和 outMimeType, 此时返回值为null。可以通过这个方法在给bitmap分配内存前获取图片的尺寸和类型。例子如下:
BitmapFactory.Options options =newBitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
2)加载按比例缩减后的版本到内存
设置BitmapFactory.Options对象的inSampleSize为true,通知解码函数部分取样图片,加载一个较小的版本。一个分辨率为2048*1536的图片使用inSampleSize为4解码后生成分辨率大约为512*384的图片,加载该图片到内存只需要0.75MB而不是原来的12MB(假设图片使用ARGB_8888样式)。例子如下:
publicstaticint calculateInSampleSize(
BitmapFactory.Options options,int reqWidth,int reqHeight){
// Raw heightand width of image
final int height= options.outHeight;
final int width= options.outWidth;
int inSampleSize = 1;
if (height> reqHeight|| width> reqWidth){
final int halfHeight= height/2;
final int halfWidth= width/2;
// Calculate the largest inSampleSize value that is a power of 2 and keepsboth
// height and width larger than the requested height and width.
while ((halfHeight/ inSampleSize)> reqHeight
&& (halfWidth / inSampleSize)> reqWidth){
inSampleSize *= 2;
}
}
return inSampleSize;
}
注意:解码函数使用与2的幂次方的最近的值,所以使用2的幂次方来计算inSampleSize.
使用过程中,先设置inJusteDecodeBounds属性为true调用解码方法,然后对得到的option设置新的inSampleSize值和设置inJusteDecodeBounds属性为false再次调用解码方法,如下所示:
public staticBitmap decodeSampledBitmapFromResource(Resources res,int resId,
int reqWidth,int reqHeight){
// First decodewith inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options=newBitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeResource(res, resId, options);
// CalculateinSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmapwith inSampleSize set
options.inJustDecodeBounds=false;
return BitmapFactory.decodeResource(res, resId, options);
}
使用上述方法可以很容易的将任意大小的大图加载到显示100*100的缩略图的ImageView中,如下所示:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage,100,100));
二)在非UI线程中处理Bitmap
所有的BitmapFactory.decode*
方法在从外存或网络(或其他内存以外的其他地方)加载图片,都不应该在主UI线程中执行。
1) 使用AsyncTask
AsyncTask类可以在后台线程中执行处理工作并把结果显示到UI线程中。如下是使用AsyncTask加载图片的示例:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private int data = 0; public BitmapWorkerTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected imageViewReference = new WeakReference<ImageView>(imageView); } // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { data = params[0]; return decodeSampledBitmapFromResource(getResources(), data, 100, 100)); } // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } }
通过创建上述任务并执行它,可以异步加载图片:
public void loadBitmap(int resId, ImageView imageView) { BitmapWorkerTask task = new BitmapWorkerTask(imageView); task.execute(resId); }
ListView和GridView在跟上述AsyncTask一起使用时会存在如下问题:
为了有效的使用内存,上述组件会在用户滑动时回收子View。如果每个子View都触发一个AsyncTask的话,无法保证任务完成后关联的view没有被回收。
无法保证异步任务完成的顺序与开始的顺序一致。
一种解决方法是在ImageView中保存一个指向最近AsyncTask的指针,当任务完成后可以用来做比对。
创建一个自定义的Drawable子类保存指向AsyncTask的指针。这里使用BitmapDrawable在任务完成前,在ImageView中显示占位图。
static class AsyncDrawable extends BitmapDrawable { private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask); } public BitmapWorkerTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } }
在执行BitmapWorkerTask之前,创建AsyncDrawable并与目标ImageView绑定。
public void loadBitmap(int resId, ImageView imageView) { if (cancelPotentialWork(resId, imageView)) { final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); imageView.setImageDrawable(asyncDrawable); task.execute(resId); } }
cancelPotentialWork 方法检查是否已经有正在运行的任务与当前ImageView关联,如果有的话,先取消以前的任务。它的实现如下:
public static boolean cancelPotentialWork(int data, ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { final int bitmapData = bitmapWorkerTask.data; // If bitmapData is not yet set or it differs from the new data if (bitmapData == 0 || bitmapData != data) { // Cancel previous task bitmapWorkerTask.cancel(true); } else { // The same work is already in progress return false; } } // No task associated with the ImageView, or an existing task was cancelled return true; }
getBitmapWorkerTask()方法用来获取与ImageView 关联的任务:
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); if (drawable instanceof AsyncDrawable) { final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null; }
最后一步是更新BitmapWorkerTask 的onPostExecute()
方法,它负责检查任务是否已经被取消已经当前任务是否与关联的ImageView匹配:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { ... @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } } }
上述实现适合于ListView和GridView这样的会回收子View的组件。只需在ImageView中调用loadBitmap即可。例如,在GridView的实现中,这将在它的adapter的getView()方法中。
三)缓存Bitmap
需要为LruCache选择一个合适的大小,有如下因素需要考虑:
1)需要同时显示多少图片,以及需要多少图片备用着以便马上显示?
2)设备的屏幕大小和密度
3)Bitmap的尺寸和配置以便计算出每张图片的大小
4)图片的存储频率等
如下是使用LruCache缓存Bitmap的例子:
privateLruCache<String,Bitmap> mMemoryCache;
@Override protected void onCreate(Bundle savedInstanceState) { ... // Get max available VM memory, exceeding this amount will throw an // OutOfMemory exception. Stored in kilobytes as LruCache takes an // int in its constructor. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size will be measured in kilobytes rather than // number of items. return bitmap.getByteCount() / 1024; } }; ... } public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } public Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); }
注意:上例中,1/8的应用程序内存用来做缓存,使用正常设备的话,大约是4MB(32/8). 在800*480分辨率的设备中,满屏的GridView大约需要1.5MB(800*480*4bytes),这样内存中可以缓存大约2.5页图片。
当加载bitmap到ImageView时, 先检查LruCache,如果找到的话,立即使用其来更新ImageView, 否则启动后台线程来处理图片:
public void loadBitmap(int resId, ImageView imageView) { final String imageKey = String.valueOf(resId); final Bitmap bitmap = getBitmapFromMemCache(imageKey); if (bitmap != null) { mImageView.setImageBitmap(bitmap); } else { mImageView.setImageResource(R.drawable.image_placeholder); BitmapWorkerTask task = new BitmapWorkerTask(mImageView); task.execute(resId); } }
The BitmapWorkerTask
also needs to be updated to addentries to the memory cache:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { ... // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { final Bitmap bitmap = decodeSampledBitmapFromResource( getResources(), params[0], 100, 100)); addBitmapToMemoryCache(String.valueOf(params[0]), bitmap); return bitmap; } ... }
需要注意的是:对应频繁存取的图片, ContentProvider可能是更好的选择,图库应用程序就是使用的ContentProvider。
下例在上述的内存缓存的基础上添加外存缓存,该外存缓存是从Android source 中截取DiskLruCache类来实现的:
private DiskLruCache mDiskLruCache; private final Object mDiskCacheLock = new Object(); private boolean mDiskCacheStarting = true; private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB private static final String DISK_CACHE_SUBDIR = "thumbnails"; @Override protected void onCreate(Bundle savedInstanceState) { ... // Initialize memory cache ... // Initialize disk cache on background thread File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR); new InitDiskCacheTask().execute(cacheDir); ... } class InitDiskCacheTask extends AsyncTask<File, Void, Void> { @Override protected Void doInBackground(File... params) { synchronized (mDiskCacheLock) { File cacheDir = params[0]; mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE); mDiskCacheStarting = false; // Finished initialization mDiskCacheLock.notifyAll(); // Wake any waiting threads } return null; } } class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { ... // Decode image in background. @Override protected Bitmap doInBackground(Integer... params) { final String imageKey = String.valueOf(params[0]); // Check disk cache in background thread Bitmap bitmap = getBitmapFromDiskCache(imageKey); if (bitmap == null) { // Not found in disk cache // Process as normal final Bitmap bitmap = decodeSampledBitmapFromResource( getResources(), params[0], 100, 100)); } // Add final bitmap to caches addBitmapToCache(imageKey, bitmap); return bitmap; } ... } public void addBitmapToCache(String key, Bitmap bitmap) { // Add to memory cache as before if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } // Also add to disk cache synchronized (mDiskCacheLock) { if (mDiskLruCache != null && mDiskLruCache.get(key) == null) { mDiskLruCache.put(key, bitmap); } } } public Bitmap getBitmapFromDiskCache(String key) { synchronized (mDiskCacheLock) { // Wait while disk cache is started from background thread while (mDiskCacheStarting) { try { mDiskCacheLock.wait(); } catch (InterruptedException e) {} } if (mDiskLruCache != null) { return mDiskLruCache.get(key); } } return null; } // Creates a unique subdirectory of the designated app cache directory. Tries to use external // but if not mounted, falls back on internal storage. public static File getDiskCacheDir(Context context, String uniqueName) { // Check if media is mounted or storage is built-in, if so, try and use external cache dir // otherwise use internal cache dir final String cachePath = Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() : context.getCacheDir().getPath(); return new File(cachePath + File.separator + uniqueName); }
注意:初始化外存缓存也需要外存操作,因此不能在主线程中进行。因此,会存在缓存在初始化前被存取的情况。,添加锁对象以保证存取前先初始化。
尽管内存缓存是在UI线程中检查的,外存缓存需要在后台线程中做检查。当图片处理完毕后,需要同时添加到内存和外存缓存中以备后续使用。
3)处理配置发生改变的情况
配置发生改变,如横竖屏切换会引起activity的销毁和重新启动,这时候需要重新处理图片的加载。为避免这种情况的发生,通过设置setRetainInstance(true)使用Fragment保存前面讲到的内存缓存并提交给新的actiivty实例。在Activity被重新创建后,保留的Fragment重新添加到activity中,重新获取到已经保存的缓存对象并显示出来。
例子如下:
private LruCache<String, Bitmap> mMemoryCache; @Override protected void onCreate(Bundle savedInstanceState) { ... RetainFragment retainFragment = RetainFragment.findOrCreateRetainFragment(getFragmentManager()); mMemoryCache = retainFragment.mRetainedCache; if (mMemoryCache == null) { mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { ... // Initialize cache here as usual } retainFragment.mRetainedCache = mMemoryCache; } ... } class RetainFragment extends Fragment { private static final String TAG = "RetainFragment"; public LruCache<String, Bitmap> mRetainedCache; public RetainFragment() {} public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) { RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG); if (fragment == null) { fragment = new RetainFragment(); fm.beginTransaction().add(fragment, TAG).commit(); } return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } }
四)管理Bitmap的内存
Android系统对Bitmap内存的管理演变如下:
在Android 2.2(API 8)及以下的系统,当垃圾回收发生时,App的线程就会停止,这会产生一个滞后,降低性能。Android2.3加入实时并行垃圾回收机制,即一旦bitmap不再被引用,就回收该内存。
在Android2.3.3(API10)及以下的系统,bitmap的像素数据存储在native内存中。它与bitmap本身是分离的,后者存储在Dalvik堆中。在native内存中的像素数据的释放是不可预知的,很容易造成应用程序超出内存限制而崩溃。到Android3.0(API11)中,像素数据与bitmap数据一起都保存在Dalvik堆中。
这里只介绍Android3.0以上的情况。
在Android3.0引入BitmapFactory.Options.inBitmap选项,该选项被设置后,解码方法会在加载数据时试图复用已经存在的bitmap. 这意味着bitmap的内存被复用,产生更好的性能,减少内存的分配. 但是复用是有限制条件的,在Android4.4(API19)之前,只支持尺寸相同的bitmap.
1)保存bitmap以便后续复用
当一个bitmap被从LruCache收回时,在HashSet中保存指向该bitmap的软指针,以便后续使用inBitmap来复用它.代码如下:
Set<SoftReference<Bitmap>> mReusableBitmaps;
private LruCache<String,BitmapDrawable> mMemoryCache;
// If you'rerunning on Honeycomb or newer, create a
// synchronizedHashSet of references to reusable bitmaps.
if (Utils.hasHoneycomb()){
mReusableBitmaps =
Collections.synchronizedSet(newHashSet<SoftReference<Bitmap>>());
}
mMemoryCache = newLruCache<String,BitmapDrawable>(mCacheParams.memCacheSize){
// Notify theremoved entry that is no longer being cached.
@Override
protected void entryRemoved(boolean evicted,String key,
BitmapDrawable oldValue,BitmapDrawable newValue){
if (RecyclingBitmapDrawable.class.isInstance(oldValue)){
// The removed entry is a recyclingdrawable, so notify it
// that it has been removed from the memorycache.
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
} else{
// The removed entry is a standardBitmapDrawable.
if (Utils.hasHoneycomb()){
// We're running on Honeycomb or later,so add the bitmap
// to a SoftReference set for possibleuse with inBitmap later.
mReusableBitmaps.add
(newSoftReference<Bitmap>(oldValue.getBitmap()));
}
}
}
....
}
2)使用已经存在的bitmap
在运行app时,解码方法查看是否有已经存在的bitmap可被复用。如:
publicstaticBitmap decodeSampledBitmapFromFile(String filename,
int reqWidth,int reqHeight,ImageCache cache){
final BitmapFactory.Options options=newBitmapFactory.Options();
...
BitmapFactory.decodeFile(filename, options);
...
// If we'rerunning on Honeycomb or newer, try to use inBitmap.
if (Utils.hasHoneycomb()){
addInBitmapOptions(options, cache);
}
...
return BitmapFactory.decodeFile(filename, options);
}
其中addInBitmapOptions()方法用来寻找已经存在的bitmap以便设置它为inBitmap:
private static void addInBitmapOptions(BitmapFactory.Options options,
ImageCache cache){
// inBitmap onlyworks with mutable bitmaps, so force the decoder to
// returnmutable bitmaps.
options.inMutable = true;
if (cache!=null){
// Try to find a bitmap to use for inBitmap.
Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
if (inBitmap!=null){
// If a suitable bitmap has been found, setit as the value of
// inBitmap.
options.inBitmap = inBitmap;
}
}
}
// This methoditerates through the reusable bitmaps, looking for one
// to use forinBitmap:
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
Bitmap bitmap = null;
if (mReusableBitmaps!=null&&!mReusableBitmaps.isEmpty()){
synchronized (mReusableBitmaps) {
final Iterator<SoftReference<Bitmap>> iterator
= mReusableBitmaps.iterator();
Bitmap item;
while (iterator.hasNext()){
item = iterator.next().get();
if (null != item&& item.isMutable()){
// Check to see it the item can be usedfor inBitmap.
if (canUseForInBitmap(item, options)){
bitmap = item;
// Remove fromreusable set so it can't be used again.
iterator.remove();
break;
}
} else {
// Remove from the set if the referencehas been cleared.
iterator.remove();
}
}
}
}
return bitmap;
}
canUseForInBitmap方法用来判断是否满足复用条件:
static boolean canUseForInBitmap(
Bitmap candidate,BitmapFactory.Options targetOptions){
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
// From Android 4.4 (KitKat) onward we can re-use if the byte size of
// the new bitmap is smaller than the reusable bitmap candidate
// allocation byte count.
int width = targetOptions.outWidth / targetOptions.inSampleSize;
int height = targetOptions.outHeight / targetOptions.inSampleSize;
int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
return byteCount <= candidate.getAllocationByteCount();
}
// On earlierversions, the dimensions must match exactly and the inSampleSize must be 1
return candidate.getWidth()== targetOptions.outWidth
&& candidate.getHeight()== targetOptions.outHeight
&& targetOptions.inSampleSize==1;
}
/**
* A helper function to return the byte usage per pixel of a bitmap basedon its configuration.
*/
static int getBytesPerPixel(Config config){
if (config==Config.ARGB_8888){
return 4;
} else if (config == Config.RGB_565){
return 2;
} else if (config == Config.ARGB_4444){
return 2;
} else if (config == Config.ALPHA_8){
return 1;
}
return 1;
}
四)在UI中展示Bitmap
1)在GridView中加载Bitmap的实现
首先给出GridView的一个标准实现,在Fragment添加GridView,它的子View使用ImageView。
publicclassImageGridFragmentextendsFragmentimplementsAdapterView.OnItemClickListener{
private ImageAdapter mAdapter;
// A staticdataset to back the GridView adapter
public final staticInteger[] imageResIds=newInteger[]{
R.drawable.sample_image_1, R.drawable.sample_image_2, R.drawable.sample_image_3,
R.drawable.sample_image_4, R.drawable.sample_image_5, R.drawable.sample_image_6,
R.drawable.sample_image_7, R.drawable.sample_image_8, R.drawable.sample_image_9};
// Emptyconstructor as per Fragment docs
public ImageGridFragment(){}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
mAdapter = new ImageAdapter(getActivity());
}
@Override
public View onCreateView(
LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState){
final View v= inflater.inflate(R.layout.image_grid_fragment, container, false);
final GridView mGridView=(GridView) v.findViewById(R.id.gridView);
mGridView.setAdapter(mAdapter);
mGridView.setOnItemClickListener(this);
return v;
}
@Override
public void onItemClick(AdapterView<?> parent,View v, int position,long id){
final Intent i=newIntent(getActivity(),ImageDetailActivity.class);
i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);
startActivity(i);
}
private classImageAdapterextendsBaseAdapter{
private finalContext mContext;
public ImageAdapter(Context context){
super();
mContext = context;
}
@Override
public int getCount(){
return imageResIds.length;
}
@Override
public Object getItem(int position){
return imageResIds[position];
}
@Override
public long getItemId(int position){
return position;
}
@Override
public View getView(int position,View convertView,ViewGroup container){
ImageView imageView;
if (convertView==null){// if it's notrecycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(newGridView.LayoutParams(
LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
} else{
imageView = (ImageView) convertView;
}
imageView.setImageResource(imageResIds[position]);// Load image into ImageView
return imageView;
}
}
}
该实现的问题是图片在UI线程中进行设置。对于小图由于系统加载和缓冲尚可承受,当图片较大,需要额外的处理时,UI就会出现停顿。
可以采用在非UI线程里处理Bitmap一节中的技巧,如下是更新后的解决方案:
public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener{
...
private classImageAdapterextendsBaseAdapter{
...
@Override
public View getView(int position,View convertView,ViewGroup container){
...
loadBitmap(imageResIds[position],imageView)
return imageView;
}
public void loadBitmap(int resId,ImageView imageView){
if (cancelPotentialWork(resId, imageView)){
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable =
new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}
static classAsyncDrawableextendsBitmapDrawable{
private finalWeakReference<BitmapWorkerTask>bitmapWorkerTaskReference;
public AsyncDrawable(Resources res,Bitmap bitmap,
BitmapWorkerTask bitmapWorkerTask){
super(res, bitmap);
bitmapWorkerTaskReference =
new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask(){
return bitmapWorkerTaskReference.get();
}
}
public staticboolean cancelPotentialWork(int data,ImageView imageView){
final BitmapWorkerTask bitmapWorkerTask= getBitmapWorkerTask(imageView);
if (bitmapWorkerTask!=null){
final int bitmapData = bitmapWorkerTask.data;
if (bitmapData!= data){
// Cancel previous task
bitmapWorkerTask.cancel(true);
} else{
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task wascancelled
return true;
}
private staticBitmapWorkerTask getBitmapWorkerTask(ImageView imageView){
if (imageView!=null){
final Drawable drawable= imageView.getDrawable();
if (drawableinstanceofAsyncDrawable){
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
... // include updatedBitmapWorkerTask class