Android ListView避免多线程加载一个同一资源

当我们的ListView中的Item包含图片,而且这些图片是同一资源,我们用多线程去加载图片,这时候可能就发生了这种情况。

比如线程是人,第一个人去做加载图片到缓存的工作,还没做好时第二个人要这同一张张图,结果缓存还没有这张图,于是第二个人也去加载图片到缓存。。以此类推,同时可能有N个人在加载同一张图,N取决于图片没加载到缓存前,可见的Item数量。

于是针对这种情况进行细化同步,避免发生。

//首先需要一个ConcurrentHashMap<String, Boolean>,String表示资源地址如图片地址,Boolean表示是否有线程正在加载。
//之所以用ConcurrentHashMap,是因为它不但是同步的而且效率高于Synchronized和ReenTrantLock.
private ConcurrentHashMap<String, Boolean> mConcurentMap;
//然后需要一个ReentrantLock锁住线程内的关键判断语句。
private ReentrantLock mLock;

当一个线程没有在缓存找到资源,那么将读取资源,ConcurrentHashMap记录这个资源地址,并且Boolean为true,表示有人在干这个活了,下面的人只要等待干活的人干完就行了。

//这是加载图片的线程,当缓存中没找到图片(mBitmapCache.get(path) == null)进入此线程
Thread thread = new Thread() {
       @Override
       public void run() {
           mLock.lock();//锁住关键判断逻辑,避免同一资源多个线程都进入false逻辑(加载图片)
           if (mConcurentMap.get(path) == false) {
               // mConcurentMap添加键值对,表示path这个资源已经有人在加载了
               mConcurentMap.put(path, true);
               mLock.unlock(); //释放锁,下面进入的线程只能进入true的逻辑(等待第一个人做完工作)
               if (path != null && (!path.equals("null"))                                                        && (!"".equals(path))) {
                   //加载图片
                   item.bm = PictureHelper.getCropImage(path, 400, true, 100,mActivity, 7, true);
                   //图片加载完放入缓存Lrucache
                   mBitmapCache.put(path, item.bm);
                   //设置键值对,false表示path这个资源已经加载完,或者没人加载了
                   mConcurentMap.put(path, false);
                } else {
                   item.bm = null;
                   //图片地址无效
                   mConcurentMap.put(path, false);
                }
                Message msg = new Message();
                msg.what = 1;
                handler.sendMessage(msg);
            } else {
                // 释放锁
                mLock.unlock();
                // 表示有人在加载图片,因此用死循环等待加载完成
                while (true) {
                    // false表示加载完成,此时缓存已有图片,应该跳出循环
                    if (mConcurentMap.get(path) == false) {
                        // Logg.D("other need break cycle");
                        break;
                    }
                }
                // Logg.D("other go out cycle");
                if (mBitmapCache.get(path) != null) {
                    item.bm = mBitmapCache.get(path);
                    Message msg = new Message();
                    msg.what = 2;
                    handler.sendMessage(msg);
                } else {
                    throw new NullPointerException("(mBitmapCache.get(path) == null!");
                }
            }
        }
    };
    executors.execute(thread);
时间: 2024-10-28 11:29:16

Android ListView避免多线程加载一个同一资源的相关文章

android ListView上拉加载更多 下拉刷新功能实现(采用pull-to-refresh)

Android实现上拉加载更多功能以及下拉刷新功能, 采用了目前比较火的PullToRefresh,他是目前实现比较好的下拉刷新的类库. 目前他支持的控件有:ListView, ExpandableListView,GridView,WebView等. 下载地址:https://github.com/chrisbanes/Android-PullToRefresh 首先第一步当然是导入libriay到咱们的项目了,具体导入方式,这里不再赘述. 下面是个例子采用的是ListView,当然其余的和这

Android 编程下 WebView 加载一个网页如何得到网页的 Cookie 值

http://www.cnblogs.com/sunzn/archive/2013/04/03/2998113.html mWebView.setWebViewClient(new MyWebViewClient());mWebView.loadUrl(url); 最近做项目在手机端加载一个 WebView 进行网页第三方登录的时候,认证成功后会跳转到公司的网页中,手机客户端需要从网页中拿到 Cookies 携带的 UID 进行数据交互,这时我们可以在 WebViewClient 的 onPag

Android ListView 图片异步加载和图片内存缓存

开发Android应用经常需要处理图片的加载问题.因为图片一般都是存放在服务器端,需要联网去加载,而这又是一个比较耗时的过程,所以Android中都是通过开启一个异步线程去加载.为了增加用户体验,给用户省流量,一般把加载完的图片先缓存下来,下次加载的时候就不需要再联网去服务器端加载.图片缓存一般分为一级缓存(即内存缓存)和二级缓存(即磁盘缓存).这里只讲一级缓存. 内存缓存就是把加载完的图片先放在手机内存中,等下次加载的时候再从内存中取出来. 优点是速度快,缺点是不能长久保存,用户退出应用程序之

android UI进阶之实现listview的分页加载

 分享了下拉刷新,这是一个用户体验非常好的操作方式.新浪微薄就是使用这种方式的典型. 还有个问题,当用户从网络上读取微薄的时候,如果一下子全部加载用户未读的微薄这将耗费比较长的时间,造成不好的用户体验,同时一屏的内容也不足以显示如此多的内容.这时候,我们就需要用到另一个功能,那就是listview的分页了.通过分页分次加载数据,用户看多少就去加载多少. 通常这也分为两种方式,一种是设置一个按钮,用户点击即加载.另一种是当用户滑动到底部时自动加载.今天我就和大家分享一下这个功能的实现. 首先,

Android Demo之旅 ListView底部添加加载更多按钮实现数据分页

在我们的实际项目中,数据应该说是很多的,我们的ListView不可能一下子把数据全部加载进来,我们可以当滚动条滚动到ListView的底部的时候,给一个更多的提示,当我们点击它即加载下一页的数据,相当与我们的分页效果,参考网上的东西,写了一个小小的demo,并总结了一些知识点,功能图如下:    源代码下载地址:http://download.csdn.net/detail/harderxin/7762625 掌握知识点: 1)自定义Adapter,将数据和ListView绑定起来 2)理解La

Android中ListView上拉加载更多及下拉刷新

做几乎每一个Android应用开发,都少不了用到一个控件,那就是ListView,用于加载多条数据,并用一定的样式展示出来.但是为了性能问题(一次性加载太多数据,比如100000条,耗费时间长,消耗资源多等)及用户体验问题(比如用户只想看最新的10条数据,结果一下子把所有的上万条数据都加载了,不方便用户选择)等原因,所以我们要把ListView的数据进行分页加载,常用的就是ListView的上拉加载更多及下拉刷新最新数据. 我们可以自己封装一个带上下拉功能的ListView,通常就是加上头部He

android自定义控件之ListView上拉加载

自定义控件LoadLayout import android.content.Context; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import andr

android开发之Fragment加载到一个Activity中

Fragments 是android3.0以后添加的.主要是为了方便android平板端的开发.方便适应不同大小的屏幕.此代码是为了最简单的Fragment的使用,往一个Activity中添加Fragment,主要涉及的知识点有:1.Fragment类的创建,2.Fragment的添加3.无UI的 Fragment的添加,根据Tag找回Fragment Fragment对应的Xml布局文件, <LinearLayout xmlns:android="http://schemas.andro

android自定义控件之ListView上拉加载与下拉刷新

自定义控件LoadLayout import android.content.Context; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import andr