国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > php开源 > 综合技术 > 异步下载照片墙

异步下载照片墙

来源:程序员人生   发布时间:2016-07-11 08:48:13 阅读次数:2326次

android异步下载照片墙


版权声明:本文出自ShengFQ的博客.
转载请注明出处:http://www.jianshu.com/p/1709ea24ebbb

我的问题

我要实现从服务器端下载1个图片地址列表,并异步下载图片展现在imageview,实现缓存和紧缩。如果要下载紧缩后的图片,应当是由服务器端先紧缩后存储,这里只说客户端范围,不做讨论。

需要豫备的知识点:
1.AsyncTask异步调用方法下载json数据
2.listview,viewadapter的高性能写法
3.HandlerThread,Hander,Message异步消息机制
4.LurCache缓存
5.BitmapFactory的紧缩图片

1.AsyncTask异步调用方法下载json数据
1个AsyncTask和1个Adapter组合,从服务器端拉取包括图片地址的json格式数据。
2.listview,viewadapter的高性能写法

viewadapter.java 部份实现 Object mlock;//异步操作list时,需要对list进行同步锁定 Context mContext;//调用上下文 List<Model> datalist;//list数据源 int itemLayoutId;//adapterview的个性化显示布局id getDataList(); reload(List models); add(model model); addAll(List models); render(Model model,View view,int position);//重复利用ViewHolder提升getView方法的性能 getCount(); getItem(int position); getItemId(int position); class ViewHolder{} getView(int position,View convertView,ViewGroup parent); //将1特定的数据格式添加到view中 protected void render(LinkModel linkmodel,View view,int position){ final LinkModel item=linkmodel; ViewHolder viewHolder=(ViewHolder)view.getTag(); //重复利用视图组件,将视图组件存储到装载对象中 if(viewHolder==null){ viewHolder=new ViewHolder(); viewHolder.app_layout= (FrameLayout)view.findViewById(R.id.iv_app_layout); viewHolder.app_icon_layout=(LinearLayout)view.findViewById(R.id.app_icon_layout); viewHolder.app_log_iv=(ImageView)view.findViewById(R.id.ic_app_ico); viewHolder.app_name_tv=(TextView)view.findViewById(R.id.ic_app_name); view.setTag(viewHolder); }else{ viewHolder=(ViewHolder)view.getTag(); } if(item!=null){ viewHolder.app_log_iv.setImageResource(R.drawable.app_img_app_normal); viewHolder.app_name_tv.setText(item.name); if (ImageCache.getInstance(mCacheSize).isCache(item.photoUrl)) { viewHolder.app_log_iv.setImageBitmap(ImageCache.getInstance(mCacheSize).getBitmapFromMemCache(item.photoUrl)); }else{ //后台线程异步下载图片,Token在这里指定了imageview的实例 mThumbnailThread.queueThumbnail(viewHolder.app_log_iv, item.photoUrl); } } }

在AsyncTask中将后台的json数据封装到实体对象,通过上述的adapter.addAll(List models)注入到adapter中.

3.HandlerThread,Hander,Message异步消息机制
定义ThunbnailDownloader类,用来通过发送下载要求消息,从另外一个线程去后台下载并通过与前台UI线程交互加载.

public class ThumbnailDownLoader<Token> extends HandlerThread { private static final String TAG="ThumbnailDownLoader"; private static final int MESSAGE_DOWNLOAD=0; private Handler mHandler; private String requestToken; private int mCacheSize; Map<Token,String> requestMap= Collections.synchronizedMap(new HashMap<Token,String>()); /** * 主线程中的Handler对象 * */ Handler mRespoonseHandler; //回调接口变量 Listener<Token> mListener; /** * 用于通讯的监听器接口 * 当下载完要履行的事情:将图片加载到UI线程的ImageView * */ public interface Listener<Token>{ /** * 后台线程的输出,将下载的图片指定给前台的ImageView * @param token 寄存图片的容器 * @param thumbnail 图片格式 * */ void onThumbnailDownloaded(Token token,Bitmap thumbnail); } public void setListener(Listener<Token> listener){ mListener=listener; } public ThumbnailDownLoader(String requesttoken){ super(TAG); this.requestToken=requesttoken; } /** * 主线程传递的Handler * @param responseHandler 前台UI线程handler * @param requesttoken 远程下载要求的token * @param cacheSize 缓存大小 * */ public ThumbnailDownLoader(Handler responseHandler,String requesttoken,int cacheSize){ super(TAG); mRespoonseHandler=responseHandler; this.requestToken=requesttoken; this.mCacheSize=cacheSize; } /** * 该方法的调用产生在Looper第1次检查消息队列之前 * */ @SuppressLint("handlerLeak") @Override protected void onLooperPrepared(){ mHandler=new Handler(){ //looper获得消息队列中的特定消息,回调方法根据消息what属性进行处理 public void handleMessage(Message msg){ if(msg.what==MESSAGE_DOWNLOAD){ @SuppressWarnings("unchecked") Token token=(Token)msg.obj;//Handler.obtainMessage(msg,obj);通过Handler发送Message传递了message.obj,这里处理消息时,可以获得obj,属于约定内容。 Log.i(TAG,"Got a request for url:"+requestMap.get(token)); handleRequest(token,requestToken); } } }; } /** * 发送message要求下载图片,将URL和Token传递到同步hashMap中 * 在调用getView()的时候要求下载 * @param token 前台交互的UI控件 * @param url 前台指定的下载地址 * */ public void queueThumbnail(Token token,String url){ Log.i(TAG,"Got to URL:"+url); requestMap.put(token,url);//调用getview()的时候调用 Message message=mHandler.obtainMessage(MESSAGE_DOWNLOAD,token);//创建信息并传入消息字段,自动完成目标handler的设置 message.sendToTarget();//将消息压入消息队列 } /** * 远程下载图片,将后台下载的图片加载到前台UI的ImageView中 * @param token 泛型参数,这里指前台的ImageView * */ private void handleRequest(final Token token,final String requestToken){ try{ final String url=requestMap.get(token); if(url==null) return; byte[] bitmapBytes=new ImageLoaderUtils().getUrlBytes(url,requestToken); // final Bitmap bitmap= BitmapFactory.decodeByteArray(bitmapBytes,0,bitmapBytes.length); final Bitmap bitmap= ImageCompress.decodeSampleBitmapFromBytes(bitmapBytes, 54, 54);//图片紧缩 ImageCache.getInstance(mCacheSize).addBitmapToMemoryCache(url, bitmap); Log.i(TAG,"Bitmap created"); //此处定义了主线程在后台线程交互操作的UI处理 mRespoonseHandler.post(new Runnable(){ public void run(){ if(requestMap.get(token)!=url) return; requestMap.remove(token);// Bitmap cachebitmap =ImageCache.getInstance(mCacheSize).getBitmapFromMemCache(url); if(cachebitmap!=null) mListener.onThumbnailDownloaded(token,cachebitmap); else{ mListener.onThumbnailDownloaded(token,bitmap); } } }); }catch(IOException ioe){ Log.e(TAG,"Error downloading image",ioe); } } public void clearQueue(){ mHandler.removeMessages(MESSAGE_DOWNLOAD); requestMap.clear(); } }

说明:HandlerThread是1个消息通知处理线程类
onLooperPrepared();//初始化Handler的地方,由于此处在HandlerThread初始化后,进入Loop()之前就会调用的方法,在这里初始化Handler有助于在消息发送之前handler已初始化.

Handler
handlleMessage(Message msg);//根据消息的种别和消息附带的参数进行异步下载
handler.obtainMessage(String msg,object) //构建1个Message对象
message.sendToTarget();//将消息发送到消息队列
handler.removeMessages(String msg);//清除该消息,释放资源

如何与前台UI线程交互

已将image下载下来了,要填充到UI线程的Imageview中,这就需要与前台交互,没错,前台UI线程也有Handler,而且只有1个,将前台的handler传递到后台同享

/** * 主线程中的Handler对象 * Handler mRespoonseHandler; /** * 主线程传递的Handler * @param responseHandler 前台UI线程handler * @param requesttoken 远程下载要求的token * @param cacheSize 缓存大小 * */ public ThumbnailDownLoader(Handler responseHandler,String requesttoken,int cacheSize){ super(TAG); mRespoonseHandler=responseHandler; this.requestToken=requesttoken; this.mCacheSize=cacheSize; }

在下载完图片后,通过handler.post();方法与前台UI交互

//此处定义了主线程在后台线程交互操作的UI处理 mRespoonseHandler.post(new Runnable(){ public void run(){ if(requestMap.get(token)!=url) return; requestMap.remove(token);// Bitmap cachebitmap =ImageCache.getInstance(mCacheSize).getBitmapFromMemCache(url); if(cachebitmap!=null) mListener.onThumbnailDownloaded(token,cachebitmap);//回调接口方法 else{ mListener.onThumbnailDownloaded(token,bitmap); } } });

Listener mListener;//回调接口对象,下载图片后,定义行动将图片设置到imageview

/** * 用于通讯的监听器接口 * 当下载完要履行的事情:将图片加载到UI线程的ImageView * */ public interface Listener<Token>{ /** * 后台线程的输出,将下载的图片指定给前台的ImageView * @param token 寄存图片的容器 * @param thumbnail 图片格式 * */ void onThumbnailDownloaded(Token token,Bitmap thumbnail); } public void setListener(Listener<Token> listener){ mListener=listener; }

回到主线程UI

因此在UI线程中,做两件事,初始化HandlerThread,实现回调接口

//初始化后台线程下载图片 mThumbnailThread = new ThumbnailDownLoader<ImageView>(new Handler(),token,mCacheSize);//与主线程Looper绑定的Handler mThumbnailThread.setListener(new ThumbnailDownLoader.Listener<ImageView>() { public void onThumbnailDownloaded(ImageView imageView, Bitmap thumbnail) { if (true) { //给ImageView设置图片Bitmap imageView.setImageBitmap(thumbnail); } } }); mThumbnailThread.start(); mThumbnailThread.getLooper();

如何触发异步下载

我们要实现的效果是,下拉gridview时,自动下载图片并加载到gridview里的imageview视图,所以我们将焦点回到adapter.getView()方法里,要实现这个功能,需要两个重要参数传递到HandlerThread中
当前的Imageview实例和需要下载的图片URL

mThumbnailThread.queueThumbnail(viewHolder.app_log_iv, item.photoUrl);

4.LurCache缓存,对图片进行缓存处理
主要使用了LruCache

int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // 使用最大可用内存值的1/8作为缓存的大小。 mCacheSize = maxMemory / 8;
/** * 图片缓存 * */ public class ImageCache { private static ImageCache instance; /** * 实例化,缓存大小 */ public static ImageCache getInstance(int cacheSize) { if (instance == null) { synchronized (ImageCache.class) { if (instance == null) { instance = new ImageCache(cacheSize); } } } return instance; } private LruCache<String,Bitmap> mMemoryCache; private ImageCache(int cacheSize){ mMemoryCache=new LruCache<String,Bitmap>(cacheSize){ protected int sizeOf(String key,Bitmap bitmap){ return bitmap.getByteCount()/1024; } }; } /** * 加载到缓存 * */ public void addBitmapToMemoryCache(String key,Bitmap bitmap){ if(getBitmapFromMemCache(key) ==null){ mMemoryCache.put(key, bitmap); } } /** * 从缓存中读取 * */ public Bitmap getBitmapFromMemCache(String key){ return mMemoryCache.get(key); } /** * 检查是不是缓存 * */ public boolean isCache(String key){ Bitmap map=mMemoryCache.get(key); return map!=null; } }

5.BitmapFactory的紧缩图片

ImageCompress.java /** * * 加载输入流图片 网络*/ public static Bitmap decodeSampleBitmapFromBytes(byte[] data,int reqWidth,int reqHeight){ final BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds=true; //BitmapFactory.decodeResource(resource, resId, options); BitmapFactory.decodeByteArray(data, 0, data.length, options); options.inSampleSize=calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds=false; return BitmapFactory.decodeByteArray(data, 0, data.length, options); } //怎样紧缩呢,依照缩放比例inSamplesize,这个值是可要计算出来的 public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){ // final int height=options.outHeight; final int width=options.outWidth; int inSampleSize=1; if(height>reqHeight || width>reqWidth){ final int heightRatio=Math.round((float)height/(float)reqHeight); final int widthRatio=Math.round((float)width/(float)reqWidth); inSampleSize=heightRatio<widthRatio ?heightRatio:widthRatio; } return inSampleSize; }

参考资料

1.http://blog.csdn.net/guolin_blog/article/details/9526203 Android照片墙利用实现,再多的图片也不怕崩溃
2 http://blog.csdn.net/guolin_blog/article/details/9316683 Android高效加载大图、多图解决方案,有效避免程序OOM
3.android开发编程权威指南
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生