安卓权威编程指南 挑战练习 24章 预加载以及缓存

24.7 挑战练习:预加载以及缓存

应用中并非所有任务都能即时完成,对此,大多用户表示理解。不过,即使是这样,开发者们也一直在努力做到最好。为了让应用反应更快,大多数现实应用都通过以下两种方式增强自己的代码:? 增加缓存层

? 预加载图片

缓存指存储一定数目 Bitmap 对象的地方。这样,即使不再使用这些对象,它们也依然存储在那里。

缓存的存储空间有限,因此,在缓存空间用完的情况下,需要某种策略对保存的对象做一定的取舍。许多缓存机制使用一种叫作LRU(least recently used,最近最少使用)的存储策略。基于该种策略,当存储空间用尽时,缓存会清除最近最少使用的对象。Android支持库中的 LruCache 类实现了LRU缓存策略。

作为第一个挑战练习,请使用LruCache 为 ThumbnailDownloader 增加简单的缓存功能。这样,每次下载完 Bitmap 时,将其存入缓存中。随后,准备下载新图片时,应首先查看缓存,确认它是否存在。缓存实现完成后,即可使用它进行预加载。预加载是指在实际使用对象前,就预先将处理对象加载到缓存中。

这样,在显示 Bitmap 时,就不会存在下载延迟。实现完美的预加载比较棘手,但对用户来说,良好的预加载是一种截然不同的体验。作为第二个稍有难度的挑战练习,请在显示 GalleryItem 时,为前十个和接下来十个 GalleryItem 预加载 Bitmap

1.增加缓存层

ThumbnailDownloader增加:

private LruCache<String, Bitmap> mBitmapLruCache;

在onLooperPrepared()方法中实例化这一对象:

//onLooperPrepared()在Looper首次检查消息队列之前调用的。
    @Override
    protected void onLooperPrepared() {
        mRequestHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {  //队列中的下载消息取出并可以处理时,就会触发调用Handler.handleMessage()方法。
                if (msg.what == MESSAGE_DOWNLOAD) {
                    T target = (T) msg.obj;
                    Log.i(TAG, "Got a request for URL" + mRequestMap.get(target));
                    handleRequest(target);
                }
            }
        };
        mBitmapLruCache = new LruCache<String, Bitmap>(24 * 1024); //实际中,LruCache分配的内存应该进行动态计算
    }

修改handleRequest()方法:



private void handleRequest(final T target) {
    try {
        final String url = mRequestMap.get(target);

        final Bitmap bitmap;
        if (url == null) {
            return;
        }

        if (mBitmapLruCache.get(url) != null) {
            bitmap = mBitmapLruCache.get(url);
        } else {
            byte[] bitmapBytes = new FlickrFetcher().getUrlBytes(url);
            bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);//将返回的字节数组转化为位图
        }

        if (mBitmapLruCache.get(url) == null) {
            mBitmapLruCache.put(url, bitmap);
            Log.d(TAG, "handleRequest:  Put ---"  + url);
        }
        Log.i(TAG, "Bitmap Created");
        mResponseHandler.post(new Runnable() {
            @Override
            public void run() {
                if (mRequestMap.get(target) != url || mHasQuit) {  //再次检查requestMap 因为RecyclerView会循环使用其视图,
                    return;
                }
                mRequestMap.remove(target); //在requesMap中删除配对的PhotoHolder-URL
                mThumbnailDownloadListener.onThumnailDownloaded(target, bitmap);
            }
        });
    } catch (IOException ioe) {
        Log.e(TAG, "Error downloading image", ioe);
    }
}

2.预加载:

PhotoGalleryFragment中
@Override
        public void onBindViewHolder(PhotoHolder photoHolder, int position) {

            GalleryItem galleryItem = mGalleryItems.get(position);
            Drawable placeholder = getResources().getDrawable(R.drawable.bill_up_close);
            photoHolder.bindDrawable(placeholder);
            mThumbnailDownloader.queueThumbnail(photoHolder, galleryItem.getUrl());
            for(int i=position-10; i<position + 10; i++) {  //这里必须要put进LruCache之后才会显示图片。十分耗时
                if (i < 0 || i == position || i >= mItems.size()) {
                    continue;
                }
                GalleryItem item = mGalleryItems.get(i);
                mThumbnailDownloader.putUrl(item.getUrl());
            }

ThumbnailDownloader中:
public void putUrl(final String url){
        if(mBitmapLruCache.get(url) == null){
               new Thread(new Runnable() {
                   @Override
                   public void run() {
                       try{
                       byte[] bitmapBytes = new FlickrFetcher().getUrlBytes(url);
                       Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
                       mBitmapLruCache.put(url,bitmap);
                   }catch(Exception e){
                       e.printStackTrace();
                   }
                   }
               }).start();
        }
    }

 

时间: 2024-10-10 17:30:09

安卓权威编程指南 挑战练习 24章 预加载以及缓存的相关文章

安卓权威编程指南 挑战练习 22章 应用图标

本章使用了 ResolveInfo.loadLabel(...) 方法,在启动器应用中显示了各个activity的名 称. ResolveInfo 类还提供了另一个名为 loadIcon() 的方法.可以使用该方法为每个应用加载 显示图标.你要接受的挑战就是,为NerdLauncher应用中显示的所有应用添加对应的图标. 首先增加一个RecyclerView的条目布局,代码如下: 1 <?xml version="1.0" encoding="utf-8"?&

安卓权威编程指南 挑战练习 25章 深度优化 PhotoGallery 应用

你可能已经注意到了,提交搜索时, RecyclerView 要等好一会才能刷新显示搜索结果.请接受挑战,让搜索过程更流畅一些.用户一提交搜索,就隐藏软键盘,收起 SearchView 视图(回到只显示搜索按钮的初始状态).再来个挑战.用户一提交搜索,就初始化 RecyclerView ,显示一个搜索结果加载状态界面(使用状态指示器).下载到JSON数据之后,就删除状态指示器.也就是说,一旦开始下载图片, 就不应显示加载状态了 1.提交搜索,隐藏软键盘,收起SearchView: 将SearchV

安卓权威编程指南 挑战练习(第26章 在 Lollipop 设备上使用 JobService)

26.11 挑战练习:在 Lollipop 设备上使用 JobService 请创建另一个 PollService 实现版本.新的 PollService 应该继承 JobService 并使用 JobScheduler 来运行.在 PollService 启动代码中,检查系统版本是否为Lollipop:如果是,就 使用 JobScheduler 计划运行 JobService :如果不是,依然使用 AlarmManager 实现. JobService类: 1 package com.bign

安卓权威编程指南-笔记(第24章 Looper Handler 和 HandlerThread)

AsyncTask是执行后台线程的最简单方式,但它不适用于那些重复且长时间运行的任务. 1. Looper Android中,线程拥有一个消息队列(message queue),使用消息队列的线程叫做消息循环(message loop).消息循环会循环检查队列上是否有新消息. 消息循环由线程和looper组成,Looper对象管理着线程的消息队列. 主线程就是个消息循环,因此也拥有Looper,主线程的所有工作都是由其looper完成的,looper不断的从消息队列中抓去消息,然后完成消息指定的

安卓权威编程指南 - 第五章学习笔记(两个Activity)

学习安卓编程权威指南第五章的时候自己写了个简单的Demo来加深理解两个Activity互相传递数据的问题,然后将自己的学习笔记贴上来,如有错误还请指正. IntentActivityDemo学习笔记 题目:ActivityA登录界面(用户名.密码.登陆按钮),ActivityB(Edit,返回按键:SubmitButton).A界面输入用户名和密码传到B中,B验证用户输入的用户名和密码,如果错误就返回A,并用Toast 显示用户名和密码错误:如果正确,就在第二个 activity中显示一个Edi

安卓权威编程指南-笔记(第21章 XML drawable)

在Andorid的世界里,凡事要在屏幕上绘制的东西都可以叫drawable,比如抽象图形,Drawable的子类,位图图形等,我们之前用来封装图片的BitmapDrawable就是一种drawable. 本章我们还会看到更多的drawable:state list drawable.shape drawable和layer list drawable. 这三个drawable都定义在XML文件中,可以归为一类,统称为XML drawable. shape drawable 使用ShapeDraw

安卓权威编程指南-笔记(第26章 服务的作用)

1. IntentService IntentService也是一个context(Service是Context的子类),并能够响应intent. 一个最基本的IntentService实例如下: public class PollService extends IntentService { private static final String TAG = "PollService"; public static Intent newIntent(Context context)

安卓权威编程指南-笔记(第27章 broadcast intent)

本章需求:首先,让应用轮询新结果并在有所发现时及时通知用户,即使用户重启设备后还没有打开过应用.其次,保证用户在使用应用时不出现新结果通知. 1. 一般intent和broadcast intent 许多系统组件需要知道某些事件的发生(WIFI信号时有时无,电话的呼入等),为满足这样的需求,Andorid提供了broadcast intent 组件. broadcast intent的工作原理类似于之前学过的intent,但不同的是broadcast intent可以被多个叫做broadcast

安卓权威编程指南-笔记(第25章 搜索)

1. SearchView SearchView是个操作视图,所谓操作视图,就是可以内置在工具栏中的视图.SearchView可以让整个搜索界面完全内置在应用的工具栏中. 1.1 SearchView的配置 <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ap