Android image processing artifact BitmapFun source code analysis

Source: Internet
Author: User
Tags set background

Android image processing artifact BitmapFun source code analysis

 

 

As an Android developer, I believe everyone is familiar with the problem of image OOM. There are many open-source projects on image caching and OOM solutions, the well-known ones are Universal_image_loader and Volley, which have been introduced in the previous article. Universal_image_loader should be the most powerful in terms of image caching functions, but it seems that many functions are not available. Therefore, in projects, I generally do not like Universal_image_loader (because its own App source code is very large, adding these open-source libraries is even bigger and prone to compilation failures. Because Android seems to have a limit on the number of methods in an application, it seems to be 655, ).

BitmapFun is used in the two player projects I have used to process image cache. BitmapFun is a training tutorial provided by Google for Android development. Since it is provided by Google, so I think it is necessary for a qualified Android developer to learn, and BitmapFun is very simple and can basically meet the image cache processing needs in our project.

I usually seldom learn open-source projects at the application level, because there are already a lot of blogs on how to use an open-source project, and they are all very detailed, for most open-source projects, it comes with sample, so if you want to learn how to use an open-source project, you just need to study the sample, but I always think that familiarity with the source code of a classic open-source project is king. Let's get started with BitmapFun source code.

1. BitmapFun Structure
BitmapFun has a slightly different structure from other open-source libraries, because it is only a Google training tutorial, so BitmapFun and Its sample are put in a project, the structure diagram is as follows: The above part is the application of BitmapFun, the following is the source code of BitmapFun.

 

 

 

2. Related Categories
The most important class in BitmapFun is ImageFetcher. The request image mainly calls the loadImage method. However, this class inherits ImageResizer and ImageResizser inherits ImageWorker. So let's start learning from ImageWorker.

 

ImageWorker. java/** this class is used to encapsulate an image loading process, including loading from the cache */public abstract class ImageWorker {private static final String TAG = ImageWorker; // This variable is used for animation effects. It has no practical significance to private static final int FADE_IN_TIME = 200; // cache, including disk cache and memory cache private ImageCache mImageCache; // The private ImageCache parameter required to create the cache. imageCacheParams mImageCacheParams; // The image private Bitmap mLoadingBitmap displayed by ImageView during loading; // whether to use the gradient effect private boolean mFadeInBitma P = true; // whether to exit the task in advance. If it is true, the private boolean mExitTasksEarly = false is not displayed after the image is returned; // whether to suspend the job protected boolean mPauseWork = false; private final Object mPauseWorkLock = new Object (); protected Resources mResources; private static final int MESSAGE_CLEAR = 0; private static final int MESSAGE_INIT_DISK_CACHE = 1; private static final int MESSAGE_FLUSH = 2; private static final int MESSAGE_CLOSE = 3; Protected ImageWorker (Context context) {mResources = context. getResources ();}/*** request an image's interface * @ param image url * @ param to display the image's ImageView */public void loadImage (Object data, ImageView imageView) {if (data = null) {return;} BitmapDrawable value = null; // if the cache object is not empty, read the object if (mImageCache! = Null) {value = mImageCache. getBitmapFromMemCache (String. valueOf (data);} if (value! = Null) {// memory cache hit, then the imageView is displayed directly. setImageDrawable (value);} else if (cancelPotentialWork (data, imageView) {// if the memory cache does not hit, create an image request Task, use imageView as the final BitmapWorkerTask task = new BitmapWorkerTask (imageView) parameter; // a subclass of BitmapDrawable, mainly used to store the weak application final processing AsyncDrawable = new AsyncDrawable (mResources, mLoadingBitmap, task); // set asyncDrawable to imageView, so that the imageView and current task are one by one Corresponds to imageView. setImageDrawable (asyncDrawable); // call the executeOnExecutor method of AsyncTask. This AsyncTask is different from AsyncTask in Android systems, but it uses the same task.exe cuteOnExecutor (AsyncTask. DUAL_THREAD_EXECUTOR, data) ;}/ *** sets the default image during the loading process ** @ param bitmap */public void setLoadingImage (Bitmap bitmap) {mLoadingBitmap = bitmap ;} /*** set the local image as the default image ** @ param resId */public void setLoadingImage (int resId) {mLoadingBitmap = BitmapFactory. decodeResource (mResources, resId);}/*** Add a buffer object, when creating a disk cache, you need to complete * @ param fragmentManager * @ param cacheParams The cache parameters to use for the image cache. */public void addImageCache (FragmentManager fragmentManager, ImageCache. imageCacheParams cacheParams) {mImageCacheParams = cacheParams; mImageCache = ImageCache. getInstance (fragmentManager, mImageCacheParams); // complete disk cache Initialization New cacheasynctask(cmd.exe cute (MESSAGE_INIT_DISK_CACHE);}/*** Adds an {@ link ImageCache} to this {@ link ImageWorker} to handle disk and memory bitmap * caching. * @ param activity * @ param diskCacheDirectoryName See * {@ link ImageCache. imageCacheParams # ImageCacheParams (Context, String )}. */public void addImageCache (FragmentActivity, String diskCacheDirectoryName) {mImageCacheParams = New ImageCache. imageCacheParams (activity, diskCacheDirectoryName); mImageCache = ImageCache. getInstance (activity. getsuppfrfragmentmanager (), mImageCacheParams); new Gradient cute (gradient);}/*** set whether to use gradient effect */public void setImageFadeIn (boolean fadeIn) {mFadeInBitmap = fadeIn ;} // whether to exit the task public void setExitTasksEarly (boolean exitTasksEarly) {Mexico tasksearly = exitTa SksEarly; setPauseWork (false);}/*** Subclasses shoshould override this to define any processing or work that must happen to produce * the final bitmap. this will be executed in a background thread and be long running. for * example, you cocould resize a large bitmap here, or pull down an image from the network. ** @ param data The data to identify which image to process, as provided by * {@ link Imag EWorker # loadImage (Object, ImageView)} * @ return The processed bitmap */protected abstract Bitmap processBitmap (Object data ); /*** @ return The {@ link ImageCache} object currently being used by this ImageWorker. */protected ImageCache getImageCache () {return mImageCache;}/*** Cancels any pending work attached to the provided ImageView. * @ param imageView */public static void cancelWork (Image View imageView) {// use ImageView to locate a task. Why can this problem be found? Because imageView and task correspond to final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask (imageView) one by one; // if the task is not empty, cancel if (bitmapWorkerTask! = Null) {bitmapWorkerTask. cancel (true); if (BuildConfig. DEBUG) {final Object bitmapData = bitmapWorkerTask. data; Log. d (TAG, cancelWork-canceled work for + bitmapData );}}} /*** Returns true if the current work has been canceled or if there was no work in * progress on this image view. * Returns false if the work in progress deals with the same data. the work is not * stopped in that case. * /Public static boolean cancelPotentialWork (Object data, ImageView imageView) {// find the task final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask (imageView) through imageView; if (bitmapWorkerTask! = Null) {// if the task found is not null and the task url is the same as the given url, cancel the final Object bitmapData = bitmapWorkerTask. data; if (bitmapData = null |! BitmapData. equals (data) {bitmapWorkerTask. cancel (true); if (BuildConfig. DEBUG) {Log. d (TAG, cancelPotentialWork-canceled work for + data) ;}} else {// The same work is already in progress. return false;} return true;}/*** find the corresponding Task */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 ;}/ *** an asynchronous task requesting images, */private class BitmapWorkerTask extends AsyncTask
 
  
{// Request the image url private Object data; // hold the weak reference private final WeakReference of ImageView
  
   
ImageViewReference; public BitmapWorkerTask (ImageView imageView) {imageViewReference = new WeakReference
   
    
(ImageView);}/*** 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; // if the work has been paused and the image request has not been canceled, wait for synchronized (mPauseWorkLock) {while (mPauseWork &&! IsCancelled () {try {mPauseWorkLock. wait () ;}catch (InterruptedException e) {}}// if there is a cache and it is not canceled, the task corresponding to the imageView in the weak reference is still self (task ), so read from the disk cache // Why is the disk cache read here? Because the disk cache can only be read in asynchronous threads, doingbackground is to execute if (mImageCache! = Null &&! IsCancelled () & getAttachedImageView ()! = Null &&! Mexico tasksearly) {bitmap = mImageCache. getBitmapFromDiskCache (dataString);} // if no hit is found and not canceled, and the task corresponding to the ImageView in the weak reference is still itself, request the network image, // call the processBitmap method, this method is abstract. In ImageFecter, if (bitmap = null &&! IsCancelled () & getAttachedImageView ()! = 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 canceled // 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 (Utils. hasHoneycomb () {// 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 recyclgbitmapdrawable // which will recycle automagically drawable = new recyclgbitmapdrawable (mResources, bitmap);} // Add the image to the cache 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 the image is canceled or exited in advance, set null if (isCancelled () | mExitTasksEarly) {value = null;} final ImageView imageVi Ew = getAttachedImageView (); if (value! = Null & imageView! = Null) {if (BuildConfig. DEBUG) {Log. d (TAG, onPostExecute-setting bitmap) ;}// display the image setImageDrawable (imageView, value) ;}@ Override protected void onCancelled (BitmapDrawable value) {super. onCancelled (value); // if the task is canceled, the background thread must be notified to stop waiting for synchronized (mPauseWorkLock) {mPauseWorkLock. policyall () ;}} private ImageView getAttachedImageView () {final ImageView imageView = imageViewReference. get (); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask (imageView); if (this = bitmapWorkerTask) {return imageView;} return null ;}} /*** is used to implement the class of one-to-one correspondence between imageView and task */private static class AsyncDrawable extends BitmapDrawable {private final WeakReference
    
     
BitmapWorkerTaskReference; public AsyncDrawable (Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {super (res, bitmap); bitmapWorkerTaskReference = new WeakReference
     
      
(BitmapWorkerTask);} public BitmapWorkerTask getBitmapWorkerTask () {return bitmapWorkerTaskReference. get () ;}}/*** show image, gradient display or common display ** @ param imageView * @ param drawable */private void setImageDrawable (ImageView imageView, Drawable drawable) {if (mFadeInBitmap) {// Transition drawable with a transparent drawable and the final drawable final TransitionDrawable td = new TransitionDrawable (new Drawable [] {new ColorDrawable (android. r. color. transparent), drawable}); // Set background to loading bitmap imageView. setBackgroundDrawable (new BitmapDrawable (mResources, mLoadingBitmap); imageView. setImageDrawable (td); td. startTransition (FADE_IN_TIME);} else {imageView. setImageDrawable (drawable );}}}
     
    
   
  
 

After analyzing ImageWorker, we found that loadImage has been provided in ImageWorker to obtain network images. After I call this method, I first try to obtain images from the memory cache, if the image is obtained successfully, a BitmapWorkerTask is started to use an asynchronous thread to obtain the image. In an asynchronous thread, the task is first retrieved from the disk. If the disk is not obtained, finally, it is obtained from the network. We found that BitmapWorkerTask obtains images by calling the processBitmap method. However, this method is an abstract method and must be implemented by sub-classes, let's go to its subclass ImageResizer.

 

 

 

@Override    protected Bitmap processBitmap(Object data) {        return processBitmap(Integer.parseInt(String.valueOf(data)));    }

It calls another overloaded processBitmap method. Let's look at another method.

 

 

private Bitmap processBitmap(int resId) {        if (BuildConfig.DEBUG) {            Log.d(TAG, processBitmap -  + resId);        }        return decodeSampledBitmapFromResource(mResources, resId, mImageWidth,                mImageHeight, getImageCache());    }

We found that this method is only used to load local images. How does it load network images? If you read the source code of ImageResizer, you will find that the main functions of the ImageResizer class are as follows:
1. Set the sizse of the displayed Image
2. load images from the disk cache

Therefore, loading images from the network is not a function of this class. Smart students should immediately think of the ImageFetcher class !, Let's take a look at the ImageFetcher class.

 

 

Private Bitmap processBitmap (String data) {final String key = ImageCache. hashKeyForDisk (data); FileDescriptor fileDescriptor = null; FileInputStream fileInputStream = null; DiskLruCache. snapshot snapshot; // check whether mHttpDiskCache has been initialized. Note that mHttpDiskCache is initialized when ImageFetcher calls addImageCache, if you do not call addImageCache //, the image cannot be obtained. for details, please analyze the code by yourself. {// Wait for dis K cache to initialize while (mHttpDiskCacheStarting) {try {mHttpDiskCacheLock. wait () ;}catch (InterruptedException e) {}}// the following code writes an image from the mHttpDiskCache if (mHttpDiskCache! = Null) {try {snapshot = mHttpDiskCache. get (key); if (snapshot = null) {if (BuildConfig. DEBUG) {Log. d (TAG, processBitmap, not found in http cache, downloading ...);} diskLruCache. editor editor = mHttpDiskCache. edit (key); if (editor! = Null) {// download the image logic here if (downloadUrlToStream (data, editor. newOutputStream (DISK_CACHE_INDEX) {editor. commit ();} else {editor. abort () ;}} snapshot = mHttpDiskCache. get (key) ;}if (snapshot! = Null) {fileInputStream = (FileInputStream) snapshot. getInputStream (DISK_CACHE_INDEX); fileDescriptor = fileInputStream. getFD () ;}} catch (IOException e) {Log. e (TAG, processBitmap-+ e);} catch (IllegalStateException e) {Log. e (TAG, procincluitmap-+ e);} finally {if (fileDescriptor = null & fileInputStream! = Null) {try {fileInputStream. close () ;}catch (IOException e) {}}} Bitmap bitmap = null; if (fileDescriptor! = Null) {// call the method in ImageResizer to generate bitmap = bytes (fileDescriptor, mImageWidth, mImageHeight, getImageCache () of the specified size image in the cache of mHttpDiskCache;} if (fileInputStream! = Null) {try {fileInputStream. close ();} catch (IOException e) {}} return bitmap;}/*** download the image from the network through HttpURLConnection, and write it to The disk cache ** @ param urlString The URL to fetch * @ return true if successful, false otherwise */public boolean downloadUrlToStream (String urlString, OutputStream outputStream) {bytes (); httpURLConnection urlConnection = null; BufferedOutputStream out = Null; BufferedInputStream in = null; try {final URL = new url (urlString); urlConnection = (HttpURLConnection) URL. openConnection (); in = new BufferedInputStream (urlConnection. getInputStream (), IO_BUFFER_SIZE); out = new BufferedOutputStream (outputStream, IO_BUFFER_SIZE); int B; while (B = in. read ())! =-1) {out. write (B);} return true;} catch (final IOException e) {Log. e (TAG, Error in downloadBitmap-+ e);} finally {if (urlConnection! = Null) {urlConnection. disconnect ();} try {if (out! = Null) {out. close () ;}if (in! = Null) {in. close () ;}} catch (final IOException e) {}} return false ;}

Well, I will simply analyze the entire Bitmapfun code logic here. In fact, after learning about Bitmapfun's code logic, we can fully optimize it, I will only propose some optimizations here. The optimization methods will be handed over to you.

For example, when BitmapWorkerTask obtains an image, it first reads the disk cache and then obtains the image from the network. That is to say, if the local image is read and the image is read in the same thread, at this time, a problem may occur, but local images cannot be loaded. For example, in the case of poor network conditions, all the threads are used up for the first five image requests, due to poor network conditions, no response has been returned, and the sixth image has a cache, so it cannot be loaded because there is no thread, so the solution is to learn the solution in Volley (which I mentioned earlier) and let a thread specifically process local images. Other threads are used to process network images.

Let's write it here. If you haven't understood anything or I 've written it wrong, please leave a message .....

 

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.