Android asynchronous loading full resolution introduces a level-1 cache, android asynchronous loading

Source: Internet
Author: User

Android asynchronous loading full resolution introduces a level-1 cache, android asynchronous loading
Introducing cache for Android asynchronous loading full resolution
Why do we need to cache images through scaling? We have implemented asynchronous loading and Optimization for large images. However, the current App is not only a large image in high definition, but also a multi-image in high definition. It is also a mix of text and text, if these images are loaded into the memory, they will surely become OOM. Therefore, the discarded images should be recycled immediately after the user browses the images. However, this poses another problem, that is, when the user browses the images once, if you want to return and review the images, you need to re-load the recycled images. If you are not sure, you need to check that you recycle the GC for those who are bored with it, while watching you reload. These two things must conflict with each other and are also a very important reason for affecting performance.
Memory cache provides a set of memory cache technology to address such a problem that requires finding a balance between each other. The memory cache technology provides quick access to images that occupy a large amount of valuable memory of applications. The core class is LruCache. This class is very suitable for caching images. Its main algorithm principle is to store recently used objects with strong references in javashashmap, in addition, the minimum recently used objects are removed from the memory before the cache value reaches the preset value. LruCache is introduced in the support-v4. Before introducing LruCache, Google recommends using SoftReference or WeakReference for memory caching. However, since Android 2.3, when the GC algorithm is modified, soft references and weak references are also preferentially recycled by GC. Therefore, this method has no high value, many articles on the Internet that are still using SoftReference and WeakReference are outdated. We suggest you keep up with the pace of the Party and keep up with the times.
The memory cache size used by LruCache to cache LruCache is determined by the developer. developers need to consider the image usage, resolution, Access frequency, device performance, and many other factors. This balance point often requires a lot of experience and testing. LruCache is very simple:

Private LruCache <String, Bitmap> mMemoryCaches; // get the application memory int maxMemory = (int) Runtime. getRuntime (). maxMemory (); // allocate cacheint cacheSize = maxMemory/10; mMemoryCaches = new LruCache <String, Bitmap> (cacheSize) {@ Override protected int sizeOf (String key, Bitmap value) {return value. getByteCount () ;}}; // obtain the cache object from LruCache public Bitmap getBitmapFromMemoryCaches (String url) {return mMemoryCaches. get (url);} // Add the cache object to LruCachepublic void addBitmapToMemoryCaches (String url, Bitmap bitmap) {if (getBitmapFromMemoryCaches (url) = null) {mMemoryCaches. put (url, bitmap );}}

First, we need to declare LruCache. Then, we create a cache object through the LruCache constructor and assign it a cacheSize. This cacheSize is usually obtained through Runtime, obtain the available memory allocated to the App by the current system, and use a portion of the memory as the LruCache cache. The sizeOf method must be rewritten in LruCache. Through this method, LruCache can obtain the size of each cache object, and the subclass must be rewritten, because the default LruCache gets the number of caches... Nima. Finally, we provide two methods: getBitmapFromMemoryCaches and addBitmapToMemoryCaches, which are used to obtain and increase the memory cache to LruCache. Wait, we don't seem to have written the method to release the memory yet. No need to write it. The Lru algorithm can ensure that cacheSize is not OOM. Once this size is exceeded, GC will recycle the objects with the longest time, release space.
After learning about the basic information about the cache, we will return to the current example and think about how to use the cache for asynchronous processing optimization. First of all, ListView, GridView, and other spoiled things cannot be broken, or even when it rolls happily, you are still playing and loading in the backend. Therefore, the first key point is to make it happy to roll when it is rolled, and then start loading.
To achieve this, add the AbsListView. OnScrollListener interface to the Adapter. Of course, you must also note that you must manually load the image during the first initialization. Otherwise, the system determines that you have not rolled the image. You can only call the onScroll method instead of the onScrollStateChanged method. In addition, we also need to continuously Obtain visible items in the onScroll method. Note that visibleItemCount only starts to display the image when it is greater than 0.
@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {    if (scrollState == SCROLL_STATE_IDLE) {        mImageLoader.loadImages(mStart, mEnd);    } else {        mImageLoader.cancelAllTasks();    }}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {    mStart = firstVisibleItem;    mEnd = firstVisibleItem + visibleItemCount;    if (mFirstFlag  && visibleItemCount > 0) {        mImageLoader.loadImages(mStart, mEnd);        mFirstFlag = false;    }}

When loading the data of the displayed project, obtain the first displayed Item and the last visible Item, and load only this part. So we create a method -- loadImages (int start, int end ). This method is used to load Item data from start to end. During the loading, the file is first retrieved from the memory cache. If yes, it indicates that the file has been loaded recently, and the file can be loaded directly. If not, enable synctask to download the file.
public void loadImages(int start, int end) {    for (int i = start; i < end; i++) {        String url = Images.IMAGE_URLS[i];        Bitmap bitmap = getBitmapFromMemoryCaches(url);        if (bitmap == null) {            ASyncDownloadImage task = new ASyncDownloadImage(url);            mTasks.add(task);            task.execute(url);        } else {            ImageView imageView = (ImageView) mListView.findViewWithTag(url);            imageView.setImageBitmap(bitmap);        }    }}

When setting the image, we use findViewWithTag and url to find the corresponding Imageview. This is different from the previous one because we use start to end to load the image, obtaining the corresponding Imageview from the ListView object is relatively simple.
The old method for downloading and Asynctask is still used:
private static Bitmap getBitmapFromUrl(String urlString) {    Bitmap bitmap;    InputStream is = null;    try {        URL url = new URL(urlString);        HttpURLConnection conn = (HttpURLConnection) url.openConnection();        is = new BufferedInputStream(conn.getInputStream());        bitmap = BitmapFactory.decodeStream(is);        conn.disconnect();        return bitmap;    } catch (Exception e) {        e.printStackTrace();    } finally {        try {            if (is != null)                is.close();        } catch (IOException e) {        }    }    return null;}

Asynctask is similar to the previous one:
class ASyncDownloadImage extends AsyncTask<String, Void, Bitmap> {    private String url;    public ASyncDownloadImage(String url) {        this.url = url;    }    @Override    protected Bitmap doInBackground(String... params) {        url = params[0];        Bitmap bitmap = getBitmapFromUrl(url);        if (bitmap != null) {            addBitmapToMemoryCaches(url, bitmap);        }        return bitmap;    }    @Override    protected void onPostExecute(Bitmap bitmap) {        super.onPostExecute(bitmap);        ImageView imageView = (ImageView) mListView.findViewWithTag(url);        if (imageView != null && bitmap != null) {            imageView.setImageBitmap(bitmap);        }        mTasks.remove(this);    }}

The only difference is that we load the image to Lrucache after downloading the image.
Assemble OK, everything is ready, and prepare to brush the code. Before refreshing the image, we will refresh our ideas. First, when loading the ListView In the Adapter, we will start to download the image of the items in the display range. At this time, the image is not cached, so I downloaded all the files, and the files are displayed in the Item and cached. If the files are not downloaded, you can't wait to roll them up, so I will immediately cancel all tasks, let ListView roll happily. After rolling, Continue loading. Okay, I 've talked about it all. Now we're starting to refresh the code. It's all in the dark. Only the code knows you the best.
package com.imooc.listviewacyncloader;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.util.LruCache;import android.widget.ImageView;import android.widget.ListView;import java.io.BufferedInputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import java.util.HashSet;import java.util.Set;public class ImageLoaderWithCaches {    private Set<ASyncDownloadImage> mTasks;    private LruCache<String, Bitmap> mMemoryCaches;    private ListView mListView;    public ImageLoaderWithCaches(ListView listview) {        this.mListView = listview;        mTasks = new HashSet<>();        int maxMemory = (int) Runtime.getRuntime().maxMemory();        int cacheSize = maxMemory / 10;        mMemoryCaches = new LruCache<String, Bitmap>(cacheSize) {            @Override            protected int sizeOf(String key, Bitmap value) {                return value.getByteCount();            }        };    }    public void showImage(String url, ImageView imageView) {        Bitmap bitmap = getBitmapFromMemoryCaches(url);        if (bitmap == null) {            imageView.setImageResource(R.drawable.ic_launcher);        } else {            imageView.setImageBitmap(bitmap);        }    }    public Bitmap getBitmapFromMemoryCaches(String url) {        return mMemoryCaches.get(url);    }    public void addBitmapToMemoryCaches(String url,Bitmap bitmap) {        if (getBitmapFromMemoryCaches(url) == null) {            mMemoryCaches.put(url, bitmap);        }    }    public void loadImages(int start, int end) {        for (int i = start; i < end; i++) {            String url = Images.IMAGE_URLS[i];            Bitmap bitmap = getBitmapFromMemoryCaches(url);            if (bitmap == null) {                ASyncDownloadImage task = new ASyncDownloadImage(url);                mTasks.add(task);                task.execute(url);            } else {                ImageView imageView = (ImageView) mListView.findViewWithTag(url);                imageView.setImageBitmap(bitmap);            }        }    }    private static Bitmap getBitmapFromUrl(String urlString) {        Bitmap bitmap;        InputStream is = null;        try {            URL url = new URL(urlString);            HttpURLConnection conn = (HttpURLConnection) url.openConnection();            is = new BufferedInputStream(conn.getInputStream());            bitmap = BitmapFactory.decodeStream(is);            conn.disconnect();            return bitmap;        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                if (is != null)                    is.close();            } catch (IOException e) {            }        }        return null;    }    public void cancelAllTasks() {        if (mTasks != null) {            for (ASyncDownloadImage task : mTasks) {                task.cancel(false);            }        }    }    class ASyncDownloadImage extends AsyncTask<String, Void, Bitmap> {        private String url;        public ASyncDownloadImage(String url) {            this.url = url;        }        @Override        protected Bitmap doInBackground(String... params) {            url = params[0];            Bitmap bitmap = getBitmapFromUrl(url);            if (bitmap != null) {                addBitmapToMemoryCaches(url, bitmap);            }            return bitmap;        }        @Override        protected void onPostExecute(Bitmap bitmap) {            super.onPostExecute(bitmap);            ImageView imageView = (ImageView) mListView.findViewWithTag(url);            if (imageView != null && bitmap != null) {                imageView.setImageBitmap(bitmap);            }            mTasks.remove(this);        }    }}

The following is the Adapter code:
package com.imooc.listviewacyncloader;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.ListView;import java.util.List;public class MyAdapterUseCaches extends BaseAdapter implements        AbsListView.OnScrollListener {    private LayoutInflater mInflater;    private List<String> mData;    private ImageLoaderWithCaches mImageLoader;    private int mStart = 0, mEnd = 0;    private boolean mFirstFlag;    public MyAdapterUseCaches(Context context, List<String> data, ListView listView) {        this.mData = data;        mInflater = LayoutInflater.from(context);        mImageLoader = new ImageLoaderWithCaches(listView);        mImageLoader.loadImages(mStart, mEnd);        mFirstFlag = true;        listView.setOnScrollListener(this);    }    @Override    public int getCount() {        return mData.size();    }    @Override    public Object getItem(int position) {        return mData.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        String url = mData.get(position);        ViewHolder viewHolder = null;        if (convertView == null) {            viewHolder = new ViewHolder();            convertView = mInflater.inflate(R.layout.listview_item, null);            viewHolder.imageView =                    (ImageView) convertView.findViewById(R.id.iv_lv_item);            convertView.setTag(viewHolder);        } else {            viewHolder = (ViewHolder) convertView.getTag();        }        viewHolder.imageView.setTag(url);        viewHolder.imageView.setImageResource(R.drawable.ic_launcher);        mImageLoader.showImage(url, viewHolder.imageView);        return convertView;    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        if (scrollState == SCROLL_STATE_IDLE) {            mImageLoader.loadImages(mStart, mEnd);        } else {            mImageLoader.cancelAllTasks();        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {        mStart = firstVisibleItem;        mEnd = firstVisibleItem + visibleItemCount;        if (mFirstFlag  && visibleItemCount > 0) {            mImageLoader.loadImages(mStart, mEnd);            mFirstFlag = false;        }    }    public class ViewHolder {        public ImageView imageView;    }}

Is it very simple? Now the cache is introduced. The downloaded images will be saved in the memory temporarily, so Mom no longer needs to worry about OOM. Let's pull down and try again. After the downloaded image appears again, it can be loaded immediately, unless it slides too much, causing GC.


We can see that this time we use the cache for loading has the following features: 1. Loading during initialization 2. Loading only when sliding 3. loading content temporarily stored in cache 4. Loading only the displayed area

We will continue to optimize the cache later, not complete ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~ My Github
My video MOOC

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.