(BUG已修改,最优化)安卓ListView异步加载网络图片与缓存软引用图片,线程池,只加载当前屏之说明
原文:http://blog.csdn.net/java_jh/article/details/20068915
迟点出更新的.这个还有BUG.因为软引应不给力了.2.3之后
前几天的原文有一个线程管理与加载源过多,造成浪费流量的问题.下面对这进下改进的一些说明(红色为新加)
这两天一直在优化这个问题.google也很多种做法.但发现都是比较不全面.
比如:
一些只实现了异步加载,却没有线程池与软引用.
一些是用AsynTast的,
一些有了线程池但加载所有的图片,这样造成具大资源浪费
一些是用显示当前屏的item,但却用的是线程等待唤醒的方法.(这个不推荐)
AsynTast这个开始我也是用它的.后来发现很不理想呀
.于是今天就总结了一个思路.同时把前两天的代码改一下
部份代码来自网络
最终思路: 子线程加载网络图片并用缓存图片软引用..线程池管理子线程...根据当前屏第一item的potion与最后item的position加载当前屏显示的item图片.
拖动过程中的不加载..根据onScrollListener来监听是否停止拖动. l
istView.getFirstVisiblePosition() 当前屏第一条的下标
listView.getLastVisiblePosition();当前屏最后一条下标
最近开发个应用,里面大量的activity要用到listView这个控件.由于为了更加美观显示,就要自定义一个.
这下问题出来了.因为是获取网络图片.按传统的做法没办法及时加载对应的图片或者图片错位.
在网上找了很久.也抠了一天的源码..发现网上的都没有比较系统的说明.所以这里整理一下.
方便以后自己回看.
========================
先说一下思路: 我的理解为---- 因为要网络操作.所以加载图片在子线程中. 有延迟.但主线程都不等你子线程是否获取结果.它就走下去了.这样setImageDrawable(这里当然是没有了).
所以就会在你到ListView加载完时.看不到图片的原因.
那么.在加载图片的子线程中,如果获取到图片之后.就handler发送一个信息到主线程.让它根据当前行的下标(postion)来更新图片.我管你主线程跟到哪了.管你等不等我.
反正我慢慢地下载图..下载到了我再叫你更新.
========================
首先说自定义的SimpleAdapter..
这里的传统做法大家都应懂的了.就是那个getView() 方法可以有点难理解
简单地说. 就是加载每一行数据(单行ListView).就调一次getView()
public View getView(int position, View convertView, ViewGroup parent){}
position: 这个参数是指当前一行的下标. (从0开始的);
converTiew: 是可以理解为当前一屏..(不知对不对.我是这样理解的.)第一次执行convertView,如果是第一次就进行布局资源的创建操作
如果拖进屏幕时.就可以复用到它了.不用每一屏都新建一个.这里下面代码里有说明
到图片加载了.我们定义一个图片加载的类.用一个静态方法来获取图片的Drawable
但由于优化内存使用,为了ListView加载了太多图片在内存中.那么.我们就进行缓存软引用机制来管理图片.
说得这么绕..无非就是指, 把得到的Drawable变成一个软引用.然后再把它放进map中.让系统自己的决定什么时候回收内存中的图片.
关于软引用...我个人的用法就是.但到一个drawable之后.马上new SoftReference<Drawable> (drawable) 存到map 中...那什么时候变回普通drawable呢
我认为当要从map中取出来之后.第一步就要变回普通的drawable(--softReference.get()--).这样的话.当我回来拖进listView时...就不会因为系统清理我的软引用导致看不到图了
下面上代码..先上异步获取图片的类
图片加载器就加了个线程池.
AsyncImageTask.java
- package com.naxieshu.util;
- import java.io.IOException;
- import java.io.InputStream;
- import java.lang.ref.SoftReference;
- import java.net.URL;
- import java.util.HashMap;
- import java.util.Map;
- import android.graphics.drawable.Drawable;
- import android.os.Handler;
- import android.os.Message;
- /**
- * 异步加截图片类
- * @author Hai Jiang
- * @Email 672335219@qq.ciom
- * @Data 2014-2-26
- */
- public class AsyncImageTask {
- <span style="color:#ff0000;">//开线程池
- ExecutorService executorService = Executors.newCachedThreadPool()</span>;
- //缓存图片 把图片的软引用放到map中
- private Map<String, SoftReference<Drawable>> imageMap;
- //构造器
- public AsyncImageTask() {
- super();
- this.imageMap = new HashMap<String, SoftReference<Drawable>>();
- }
- //ID为标记,标记哪条记录image . 这个ID来自于自定义adapter的getView()方法中其中一个参数position
- public Drawable loadImage(final int id, final String imageUrl,
- final ImageCallback callback){
- //先看缓存(Map)中是否存在
- if(imageMap.containsKey(imageUrl)){
- SoftReference<Drawable> softReference = imageMap.get(imageUrl);
- if( softReference != null){
- Drawable draeable = softReferenct.get();
- if(drawable != null){
- callback.imageLoaded(drawable, id);
- return drawable;
- }
- }
- }
- //主线程更新图片
- final Handler handler = new Handler() {
- public void handleMessage(Message message) {
- callback.imageLoaded((Drawable) message.obj, id);
- }
- };
- //加载图片的线程
- executorService.submit(
- //加载图片的线程
- new Thread() {
- public void run() {
- //加载图片
- Drawable drawable = AsyncImageTask.loadImageByUrl(imageUrl);
- //加入缓存集合中 注意 这里就要把得到的图片变成软引用放到map中了
- imageMap.put(imageUrl, new SoftReference<Drawable>(drawable));
- //通知消息主线程更新UI . 这里就是是否能异步刷新的留意点.
- Message message = handler.obtainMessage(0, drawable);
- handler.sendMessage(message);
- }
- });
- return null;
- //到这里就获取图片的静态方法就完了
- }
- //根据图片地址加载图片,并保存为Drawable
- //这里不用说了吧.都是一些基本的.从API从可以看
- public static Drawable loadImageByUrl(String imageUrl){
- URL url = null;
- InputStream inputStream = null;
- try {
- url = new URL(Constant.TARGETURL+imageUrl);
- inputStream = (InputStream) url.getContent();
- Drawable drawable = Drawable.createFromStream(inputStream,"src");
- return drawable;
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- if(inputStream != null)
- inputStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return null;
- }
- //利用接口回调,更新图片UI
- public interface ImageCallback {
- public void imageLoaded(Drawable obj, int id);
- }
- }
这里是自定义adapter类(这里因为主要是加个停止监听)
MyListAdapter.java
- package com.naxieshu.adapter;
- import java.lang.ref.SoftReference;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import android.content.ClipData.Item;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.drawable.Drawable;
- import android.util.Log;
- 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 android.widget.SimpleAdapter;
- import android.widget.TextView;
- import android.widget.AbsListView.OnScrollListener;
- import com.naxieshu.activity.FindActivity;
- import com.naxieshu.activity.R;
- import com.naxieshu.domain.Book;
- import com.naxieshu.util.AsyncImageTask;
- import com.naxieshu.util.AsyncImageTask.ImageCallback;
- import com.naxieshu.util.ImageUtil;
- /**
- * 自定义List内容控件
- * @author Hai Jiang
- * @Email 672335219@qq.ciom
- * @Data 2014-2-26
- */
- public class MyListAdapter extends SimpleAdapter{
- public List<? extends Map<String, ?>> data;
- private LayoutInflater inflater;
- /**异步加载图片实例*/
- private AsyncImageTask imageTask;
- /**被绑定对象*/
- private ListView listView;
- /**Item对象集*/
- HashMap<String, Object> itemMap = new HashMap<String, Object>();
- public MyListAdapter(final ListView listView,Context context,
- List<? extends Map<String, ?>> data) {
- super(context, data, 0, null, null);
- this.data = data;
- this.listView = listView;
- inflater = LayoutInflater.from(context);
- imageTask = new AsyncImageTask();
- /**注册监听事件*/
- listView.setOnScrollListener(onScrollListener);
- }
- /**
- * 在创建View资源对象的时候提供效率的缓存策略
- */
- class ViewHold{
- //book.cover
- public ImageView image;
- //book.title book.shortIntro
- public TextView namtView,idView,introView;
- }
- ViewHold hold =null;
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- //数据源中与当前item对应的数据
- Book book = (Book) data.get(position).get(position+"");
- //判断是否第一次执行convertView,如果是第一次就进行布局资源的创建操作
- if (convertView == null){
- hold = new ViewHold();
- //填充加载布局资源
- convertView = inflater.inflate(R.layout.activity_find_listview, null);
- hold.image = (ImageView)convertView.findViewById(R.id.bookImage);
- hold.image.setImageDrawable(imageTask.loadImage(position, book.getCover(),imageCallback));
- hold.namtView = (TextView)convertView.findViewById(R.id.bookName);
- hold.idView = (TextView)convertView.findViewById(R.id.bookId);
- hold.introView = (TextView)convertView.findViewById(R.id.bookShortIntro);
- //保存标记
- convertView.setTag(hold);
- } else {
- hold = (ViewHold) convertView.getTag();
- }
- /**获取数据,进行数据填充*/
- // 标记图片视图,注意不能放在上面
- hold.image.setTag(position);
- hold.image.setImageResource(R.drawable.ic_launcher);
- hold.namtView.setText(book.getTitle());
- hold.idView.setText(book.getId());
- hold.idView.setVisibility(View.GONE);
- hold.introView.setText(book.getShortIntro());
- itemMap.put(position+"", hold);
- return convertView;
- }
- /**
- * 屏幕停止滚动监听器
- */
- AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
- Log.i("--", "--");
- int start_index = listView.getFirstVisiblePosition();
- int end_index = listView.getLastVisiblePosition();
- pageImgLoad(start_index,end_index);
- }
- }
- public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
- // TODO Auto-generated method stub
- }
- };
- /**
- * 加载当前屏的图片
- */
- private void pageImgLoad(int start_index, int end_index) {
- for (; start_index < end_index; start_index++) {
- Book book = (Book) data.get(start_index).get(start_index+"");
- imageTask.loadImage(start_index, book.getCover(),imageCallback);
- }
- }
- /**回调函数*/
- AsyncImageTask.ImageCallback imageCallback = new ImageCallback(){
- public void imageLoaded(Drawable image, int position) {
- if (image != null) {
- //获取刚才标识的组件,并更新
- ImageView imageView = (ImageView) listView
- .findViewWithTag(position);
- if (imageView != null) {
- imageView.setImageDrawable(image);
- }
- }
- }
- };
- @Override
- public int getCount() {
- return data.size();
- }
- @Override
- public Object getItem(int position) {
- return itemMap.get(position+"");
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- }
- 到这里关键的都说完了.Activity那里那就贴代码了
- 主要是什么时候实例MyListAdapter要注意一下
- 如果你的是像搜结果显示在listView中的这种.
- 那么MyListAdapter的数据源就要放在Button的点击响应事件里获取..然后通Message把数据源发送到handler中.在这handler中实例MyListAdapter对象.再绑定到listView.
- 顺便说一下.另一种情况
- 得到数据源.但ListView不显示内容.这是为什么 ?
- 一般有两种原因.
- 1, ListView不在handler中绑定数据..因为对组件的更新更改操作.一 定要在主线程中弄
- 2.就是布局问题.你的ListView里的item不指定高度.----这个是最常见的..ListView的item一定要指定高度.
- 就是你定义准备套在ListView中的那个layout_xxxx.xml这个文件中的LinearLayout这些要指定高度(最外面一层)<pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
(BUG已修改,最优化)安卓ListView异步加载网络图片与缓存软引用图片,线程池,只加载当前屏之说明的更多相关文章
- wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)
wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之L ...
- IOS开发之异步加载网络图片并缓存本地实现瀑布流(二)
/* * @brief 图片加载通用函数 * @parma imageName 图片名 */ - (void)imageStartLoading:(NSString *)imageName{ NSUR ...
- 使用jdk自带的线程池。加载10个线程。
在开发中使用线程,经常不经意间就new Thread()一个出来,然后发现,这样做不是很好,特别是很多线程同时处理的时候,会出现CPU被用光导致机器假死,线程运行完成自动销毁后,又复活的情况. 所以在 ...
- spring boot 文件上传工具类(bug 已修改)
以前的文件上传都是之前前辈写的,现在自己来写一个,大家可以看看,有什么问题可以在评论中提出来. 写的这个文件上传是在spring boot 2.0中测试的,测试了,可以正常上传,下面贴代码 第一步:引 ...
- 使用NPOI和线程池快速加载EXCEL数据
private void FilterData() { List<Task> tasks = new List<Task>(); IWorkbook workbook = Cs ...
- Android批量图片加载经典系列——采用二级缓存、异步加载网络图片
一.问题描述 Android应用中经常涉及从网络中加载大量图片,为提升加载速度和效率,减少网络流量都会采用二级缓存和异步加载机制,所谓二级缓存就是通过先从内存中获取.再从文件中获取,最后才会访问网络. ...
- 看起来是线程池的BUG,但是我认为是源码设计不合理。
你好呀,我是歪歪. 前几天看到一个 JDK 线程池的 BUG,我去了解了一下,摸清楚了它的症结所在之后,我觉得这个 BUG 是属于一种线程池方法设计不合理的地方,而且官方在知道这个 BUG 之后表示: ...
- 【软引用】弱引用 图片的加载与缓存 OOM
在java.lang.ref包中提供了几个类:SoftReference类.WeakReference类和PhantomReference类,它们分别代表软引用.弱引用和虚引用. ReferenceQ ...
- android-------非常好的图片加载框架和缓存库(Picasso)
Picasso是Square公司开源的一个Android图形缓存库, 可以实现图片加载(本地和网络)和缓存功能. 地址:http://square.github.io/picasso/ jar包下载: ...
随机推荐
- JS单击隐藏界面元素
1. JS代码 <script type="text/javascript" language="javascript"> // function ...
- 基于msys2工具集,自编译gcc-6.2.0、Qt-5.6.1-1和Qt-4.8.7(有nuwen.net网站提供的脚本)
好久没更新(其实大可不要经常更新吧),一直都是用Qt4,最近想着转向Qt5了,msys2是自带Qt的,但工具链经常会更新,依赖也较多,简便才方便,做了最后一组Qt工具的更新,如题,Qt-4.8.7作为 ...
- RxJava开发精要4 – Observables过滤
原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...
- js 中var that=this
js中经常出现var that=this,为什么这么做? http://stackoverflow.com/questions/4886632/what-does-var-that-this-mean ...
- Python和C|C++的混编(一):Python调用C、C++---Boost库
不使用boost.python库来直接构建dll的话比较繁琐,下面实例是借助boost库实现python对C.C++的调用 1 首先确定已经安装python和boost库,本例测试环境是python2 ...
- hadoop 原理: 浅析Hadoop文件格式
Hadoop 作为MR 的开源实现,一直以动态运行解析文件格式并获得比MPP数据库快上几倍的装载速度为优势.不过,MPP数据库社区也一直批评Hadoop由于文件格式并非 为特定目的而建,因此序列化和反 ...
- BZOJ_1821_[JSOI2010]_部落划分_(贪心,并查集)
描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1821 给出n个点的坐标,将n个点划分成k个部分,使得每个部分之间最小的距离最大. 分析 每两个 ...
- 【转】G++ 处理 /usr/bin/ld: cannot find -lc
原文网址:http://blog.sina.com.cn/s/blog_67bbb71101010tto.html 用g++编译C++程序时显示出:/usr/lib/ld: cannot find - ...
- POJ 3159 Candies 差分约束dij
分析:设每个人的糖果数量是a[i] 最终就是求a[n]-a[1]的最大值 然后给出m个关系 u,v,c 表示a[u]+c>=a[v] 就是a[v]-a[u]<=c 所以对于这种情况,按照u ...
- SDWebImage源码解读之SDWebImageDownloader
SDWebImage源码解读之SDWebImageDownloader 第八篇 前言 SDWebImageDownloader这个类非常简单,作者的设计思路也很清晰,但是我想在这说点题外话. 如果有人 ...