- 浏览: 409350 次
- 性别:
- 来自: 福州
最新评论
-
野牛精:
感谢楼主分享,帮大忙了。
android ftp 客户端编写(ftp4j) -
happytianqiu:
你好,我最近也在搞这个,能发个demo吗,邮箱是:624951 ...
开发android机顶盒应用 事件,焦点处理 -
zhunanfengfeimeng:
http://www.iteye.comhttp://www. ...
android ftp 客户端编写(ftp4j) -
icyttea:
好棒!感谢楼主分享
vlc for android录制视频与截图 -
clwwlc:
有demo吗
开发android机顶盒应用 事件,焦点处理
一个网络程序下载图片通常是一个大麻烦,如何处理好下载,才是关键的问题,这关系到程序的性能,甚至崩溃,出现oome.
如果你还在使用ui线程下载图片,赶紧看看如何在另一个线程下载图片的相关文章吧,ui线程要做的事只是显示.
看上去使用AsyncTask是个好办法,方便操作,一般不会有非ui线程处理ui的问题.虽然它有线程池的概念,但是我也发现,还是会发起上千次甚至w次的线程请求,在一个ListView滚动过程中,然而需要下载的图片却只有不到二十张,这显然是内存的浪费了.出现oome也是必然的,一方面可能是图片本身占用内存较多,一方面是线程占用的内存资源.
所以在AsyncTask里做好缓存检测是很有必要的,检测到已经下载过的文件 就不需要下载.但是这样的问题在于,doInBackground里面检查的话,已经是新建了一个线程了.虽然这样可以减少下载量,但是新建一个线程也是要消耗资源的.
现在介绍一种自定义线程池的办法来处理下载的问题.
图片下载一直是比较麻烦的问题,如何缓存,如何处理图片都需要小心操作,因为图片的可用内存有限,太多时oome容易出现.
我的微博程序用上面的线程池处理后普通下载,gif图片另外处理,即使同时下载与解析gif动态图2m左右,帧数大约150的也没有出现oome.
线程池下载图片只是通用的一个方法.如果你需要下载一张大图,而下载不是同时进行的,建议还是单独写一个下载的方法,容易控制.因为下载8m或更大的图片也是个问题,还有下载后的存储,不宜用这种方法.
更多时候不是抱怨系统为何只提供这么少的内存供图片使用,先反省下自己的处理方式.
希望可以帮助别更多的人.
2011.12.11.
如果你还在使用ui线程下载图片,赶紧看看如何在另一个线程下载图片的相关文章吧,ui线程要做的事只是显示.
看上去使用AsyncTask是个好办法,方便操作,一般不会有非ui线程处理ui的问题.虽然它有线程池的概念,但是我也发现,还是会发起上千次甚至w次的线程请求,在一个ListView滚动过程中,然而需要下载的图片却只有不到二十张,这显然是内存的浪费了.出现oome也是必然的,一方面可能是图片本身占用内存较多,一方面是线程占用的内存资源.
所以在AsyncTask里做好缓存检测是很有必要的,检测到已经下载过的文件 就不需要下载.但是这样的问题在于,doInBackground里面检查的话,已经是新建了一个线程了.虽然这样可以减少下载量,但是新建一个线程也是要消耗资源的.
现在介绍一种自定义线程池的办法来处理下载的问题.
/** * 图片下载线程池,暂时可允许最多三个线程同时下载。 * * @author archko */ public class DownloadPool extends Thread{ public static final int MAX_THREAD_COUNT=3;定义最大同时下载线程 private int mActiveThread=0;当前活动的线程数 private App mApp;继承了Application,在manifest文件里配置的. private List<DownloadPiece> mQuery;下载队列. } 先看看App里如何处理的:public DownloadPool mDownloadPool = null; onCreate()方法里对线程池初始化. if (this.mDownloadPool != null) { return; } Log.d(TAG, "initDownloadPool."); DownloadPool downloadPool = new DownloadPool(this); this.mDownloadPool = downloadPool; this.mDownloadPool.setPriority(Thread.MIN_PRIORITY); this.mDownloadPool.setName("DownloadPool"); this.mDownloadPool.start(); 需要注意的是mDownloadPool是在程序启动后一直在运行的,然后就是它的构造方法了: public DownloadPool(App app) { this.mQuery=new ArrayList<DownloadPiece>(); this.mApp=app; } 关于DownloadPiece内容:一个内容类: public class DownloadPiece { Handler handler; public String name; //md5加密过的名字, public String uri;//图片的url public int type;// public String dir;//存储目录 } 这样一个线程池构造一半了.它如何处理下载事宜呢?接着就是定义它的run方法了. while (true) {//正常状态下一直运行. synchronized (this) { notifyAll(); if ((GetCount()!=0)&&(GetThreadCount()!=MAX_THREAD_COUNT)) {如果队列不为空,且当前下载线程数量不等于最大线程数量就新建下载线程下载图片 ,如果条件不满足,就等待,其它线程notify后它就可以下载了,这避免了一次性建太多的线程. DownloadPiece piece=Pop(); Handler handler=piece.handler; FrechImg_Impl(handler, piece.name, piece.type, piece.uri, piece.dir); } else { Log.d(TAG, "wait."+GetThreadCount()); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } 它处理下载的方法是FrechImg_Impl(),这个方法会建一个线程来下载图片的.而线程池只是管理下载线程用的. if (uri==null||name==null) { Log.d(TAG, "名字不存在。"); return; } String filename=dir+name; File file=new File(filename); if (file.exists()) { Log.d(TAG, "文件已经存在了不需要下载:"+uri); Bundle bundle=new Bundle(); bundle.putString("name", filename); FetchImage.SendMessage(handler, type, bundle, uri); return; } synchronized (this) { mApp.mDownloadPool.ActiveThread_Push(); String str3=Uri.encode(uri, ":/"); HttpGet httpGet=new HttpGet(str3); httpGet.setHeader("User-Agent", Twitter.USERAGENT); FetchImage fetchImage=new FetchImage(mApp, handler, httpClient, httpGet, type, name, uri, dir); fetchImage.setPriority(2); fetchImage.start(); } FetchImage是一个下载图片的线程.FrechImg_Impl做的事从代码来看,先检查下载的url是否合法,然后检查下载的文件是否存在,不满足下再下载,但先push,把当前的线程数量加1,然后启动线程下载. public void ActiveThread_Push() { synchronized (this) { mActiveThread++; } } 你也许会问,下载完了如何处理.这里有一个handler,这个是在你将url放入下载队列时带来的回调方法,这样也可以避免了非ui线程的问题,下载完成后就可以在handler中处理你的ui了. 其它的同步方法: public DownloadPiece Get(int paramInt) { synchronized (this) { int size=this.mQuery.size(); if (paramInt<=size) { return mQuery.get(paramInt); } } return null; } public int GetCount() { synchronized (this) { return mQuery.size(); } } public int GetThreadCount() { synchronized (this) { return mActiveThread; } } public DownloadPiece Pop() { synchronized (this) { DownloadPiece downloadPiece=(DownloadPiece) this.mQuery.get(0); this.mQuery.remove(0); return downloadPiece; } } public void ActiveThread_Pop() { synchronized (this) { int i=this.mActiveThread-1; this.mActiveThread=i; notifyAll(); } } 看到这,已经比较明朗了,你可以不关心队列中的数据哪里来的,因为上面只处理了如何下载. 下载需要一个url队列,所以需要提供一个public方法: public void Push(Handler handler, String uri, int type, String dir) { } String name=Util().getMd5(uri);//文件存储的名字自定义. synchronized (this) { for(DownloadPiece piece:mQuery) { if(piece.uri.equals(uri)) { notifyAll(); Log.d(TAG, "已经存在url:"+uri); return; } } //在这里检查了一次下载队列中的url,如果存在,就不需要再下载了,前面提到会检查一次文件是否已经下载的问题,如果你有存储,但我觉得在这里检查一次会比检查文件快一些. DownloadPiece piece=new DownloadPiece(handler, uri, name, type, dir); mQuery.add(piece);把数据放入队列. notifyAll(); } } 以上就是线程池的全部内容了.至于 下载线程,你可以自定义处理了. 需要FetchImage构造方法.把一些参数传过去, 继承extends Thread. public void run() { App app=(App) this.mContext.getApplicationContext(); HttpResponse response; /*synchronized (app.mDownloadPool) { if(app.mDownloadPool.GetThreadCount()==DownloadPool.MAX_THREAD_COUNT &&app.mDownloadPool.GetCount()>8) { Log.d(TAG, "当前的线程数为3,且等待下载的数量大于8,清除数据."); app.mDownloadPool.PopPiece(); } }*/如果这段没有,也可以,因为我觉得,当ListView滚动时,下载线程中可能有不再可见的内容,这时优先想看到的应该是当前显示的内容,所以把队列中的其它内容清除了,保留一小部分,可以再快地看到当前的ListView可见部分的图片内容.因为多线程,所以时刻记着同步处理操作. try { HttpParams httpParameters=new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, MicroBlog.CONNECT_TIMEOUT); HttpConnectionParams.setSoTimeout(httpParameters, MicroBlog.READ_TIMEOUT); DefaultHttpClient httpClient=new DefaultHttpClient(httpParameters); response=httpClient.execute(httpget); int code=response.getStatusLine().getStatusCode(); if (code==200) { byte[] bytes=EntityUtils.toByteArray(response.getEntity()); String filePath=SaveIconToFile(mName, bytes); Bundle bundle=new Bundle(); bundle.putString("name", filePath); FetchImage.SendMessage(mHandler, mType, bundle, uri); } else { Log.d(TAG, "下载图片失败:"+uri); } } catch (IOException e) { Log.d(TAG, "uri:"+uri+" exception:"+e.toString()); //e.printStackTrace(); } finally { // 默认把它移出,下载失败后不再下载。 app.mDownloadPool.ActiveThread_Pop(); } } SendMessage发送消息下载完成 .这里需要handler,就是构造时传来的参数了. DownloadPool: public void PopPiece() { synchronized (this) { int size=mQuery.size(); mQuery=mQuery.subList(size-5, size); } }为什么选择8和这里的保留5个url,因为我的一个ListView显示的图片可能一般情况下可见区会有5张图片,所以保留5个,不至于第一张不下载.如果上限数量变大,就是需要等待更多的图片下载完成后才会下载当前的图片,8这个上限没有太多 的根据,暂时定义的. SaveIconToFile()就是保存图片了. public String SaveIconToFile(String name, byte[] data) { String str2=dir+"/"+name; //Log.d(TAG, "str2:"+str2); FileOutputStream outputStream=null; try { outputStream=new FileOutputStream(str2); /*Options options=new Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeByteArray(data, 0, data.length, options); int heightRatio=(int) Math.ceil(options.outHeight/(float) 800); int widthRatio=(int) Math.ceil(options.outWidth/(float) 480); if (heightRatio>1&&widthRatio>1) { if (heightRatio>widthRatio) { // Height ratio is larger, scale according to it options.inSampleSize=heightRatio; } else { // Width ratio is larger, scale according to it options.inSampleSize=widthRatio; } } options.inDither=true; options.inJustDecodeBounds=false; options.inPreferredConfig=Config.RGB_565; Bitmap bitmap=BitmapFactory.decodeByteArray(data, 0, data.length, options);*/ 这段注释了,我不需要下载的图片压缩存储,因为压缩了图片的质量就少了许多,也可以压缩处理,但你控制好你的图片质量,如果你需要显示高清的原图,就不要压缩,上面的代码是宽高大约是480*800以上时会压缩,现在主流手机分辨率就是这个,我觉得只有超过了才需要压缩.当然,你可以通过传来更多的参数来处理是否压缩,压缩的质量等. Bitmap bitmap=BitmapFactory.decodeByteArray(data, 0, data.length); bitmap.compress(CompressFormat.PNG, 100, outputStream); outputStream.flush(); bitmap.recycle(); } catch (Exception e) { }finally { if(outputStream!=null){ try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return str2; } 外部调用:Activity中 ((App) mContext.getApplicationContext()).mDownloadPool.Push( mHandler, url, mCacheDir+"/picture/");至于其它参数可以自己看着办吧,处理的事务不同需要的东西不同.不必太在意我传的是什么.但是mHandler回调还是必须的. Handler mHandler=new Handler() { @Override public void handleMessage(Message msg) { int what=msg.what; Bundle bundle=msg.getData(); String imgUrl=(String) msg.obj; Bitmap bitmap=BitmapFactory.decodeFile(bundle.getString("name")); if (bitmap!=null&&!bitmap.isRecycled()) { 这样就可以处理了.如果你的图片不需要存储在文件系统中,你可以直接把下载的流解析成Bitmap,然后通过Handler传过来. } } 这里的bitmap可以存储在一个map中,这样你就有了一个内存缓存了,然后在调用((App) mContext.getApplicationContext()).mDownloadPool.Push( mHandler, url, mCacheDir+"/picture/");前可以先检查下内存缓存中是否已经存在了图片. BmpCache bmpCache=BmpCache.getInstance(); bmpCache.save(imgUrl, bitmap);这样保存,缓存就不列出了,网上搜索下到处是,可以用一个简单点的.
图片下载一直是比较麻烦的问题,如何缓存,如何处理图片都需要小心操作,因为图片的可用内存有限,太多时oome容易出现.
我的微博程序用上面的线程池处理后普通下载,gif图片另外处理,即使同时下载与解析gif动态图2m左右,帧数大约150的也没有出现oome.
线程池下载图片只是通用的一个方法.如果你需要下载一张大图,而下载不是同时进行的,建议还是单独写一个下载的方法,容易控制.因为下载8m或更大的图片也是个问题,还有下载后的存储,不宜用这种方法.
更多时候不是抱怨系统为何只提供这么少的内存供图片使用,先反省下自己的处理方式.
希望可以帮助别更多的人.
2011.12.11.
发表评论
-
android 批量打渠道包
2014-09-16 17:27 5905打包,是一个经常会遇到的问题,写个脚本就可以解决了.不同的脚本 ... -
vlc for android录制视频与截图
2014-09-08 18:31 8031首先说明,这不算原 ... -
android百度地图转为高德地图
2014-08-11 11:09 2964使用百度地图也不少时间了,但是一直出现无法解决的问题,在官 ... -
编译 i9000的cm系统
2014-08-04 13:10 1596[color=red][b]此文 并不 ... -
mp4v2 保存h264流
2014-05-01 21:15 11827大侠已经完成了很多操作了,唯一不足的是,工程完整性差一些,而且 ... -
baidumap的缩放到看到所有点
2014-04-09 21:03 1464之前使用1.3.5版本的sdk,通过调用mapviewcont ... -
ViewPager 查看图片
2014-02-13 12:59 4525一个图片查看器 , app到处都是 , 但那是别人的. 现在的 ... -
机顶盒 页面 选中后的 动画
2013-12-05 09:39 2184前面已经说过了,对于机顶盒的焦点的处理. 相信有不少人看 ... -
引爆你的图片浏览, ListView 大图片
2013-11-05 07:58 1314之前发现,ListView里面的图片资源越占越大,特别是当 ... -
微博开放源码
2013-10-23 12:09 1035微博程序已经发布不少时间了,但一直也没有很多用户使用,主要在用 ... -
Mupdf 缩小apk包,减少字体
2013-10-20 15:53 3723在以前的apv中,字体占了很大的一部分,如果去除cjk字体 ... -
ActionBar appcompat 解决碎片化问题
2013-09-15 16:58 2178actionbarsherlock 这个在api11以下的系统 ... -
android机顶盒获取有线mac
2013-09-02 16:09 5503直接上代码: public static String ... -
android竖着的seekbar
2013-08-22 20:33 5534以前网上有位虾士发过一篇文章是关于竖着的seekbar,但是也 ... -
新浪微博oauth2.0 自动认证
2013-08-13 09:01 0oauth2.0作者认为它不先进,都放弃了.但是oauth2. ... -
android 颜表情.
2013-07-20 21:16 1858在使用TagsViewGroup 流布局后,我替换了原来的Gr ... -
flow 流布局.
2013-07-13 20:06 1306在git上看到一个FlowingViewGroup,代码有点旧 ... -
开发android机顶盒应用 事件,焦点处理
2013-07-13 19:58 19838机顶盒应用不同于手机 ... -
android 磁盘缓存.
2013-07-13 19:30 5291开发一个app,特别是图片的app,免不了要存储图片,内存缓存 ... -
TextView 文字淡入效果
2013-04-15 13:34 3498一个文本渐渐地从左到右的显示。 几步就可以了实现了。利用的是V ...
相关推荐
总结一下,Android下载图片到相册涉及的主要步骤包括:请求权限、发起网络请求下载图片、保存图片到公共外部存储目录、以及通知媒体库更新。在实现这些功能时,可以利用Volley、OkHttp等网络库,以及Android的文件...
Android 下载图片 简单 例子 Android Image DownLoader
至此,你已经学会了如何在Android应用中下载图片并保存到SDcard的特定位置。这个过程包括了网络请求、数据流处理、文件操作和权限管理等关键环节。请确保在实际项目中根据具体需求进行调整,如错误处理、进度显示、...
- **异步下载**:Android中通常使用异步方式下载图片,避免阻塞主线程,影响用户界面。可以使用`AsyncTask`、`IntentService`或者第三方库如`Volley`、`OkHttp`来实现。 - **HTTP请求**:发送HTTP GET请求获取图片...
本实例,"android下载图片、音频实例",提供了一个可运行的解决方案,帮助开发者实现这一功能。下面我们将深入探讨实现这一功能所涉及的关键知识点。 1. **Android权限管理**: 在Android中,自6.0(API级别23)...
总结来说,实现“android下载图片保存SD卡”涉及的关键知识点包括: 1. 网络请求:使用`HttpURLConnection`或第三方库(如OkHttp)发起HTTP GET请求。 2. 文件操作:创建输出流,将网络数据写入SD卡指定路径的文件。...
"便捷下载 for Android" 是一款专为Android用户设计的应用程序,旨在简化图片、音频和视频内容的下载过程。它提供了一键式批量下载功能,让用户能够高效地从多个平台获取媒体资源,尤其适合那些希望节省时间和精力的...
在Android开发中,将网络上...总之,Android异步下载图片到相册是一个涉及网络请求、文件操作、权限管理和系统交互的过程,需要对Android基础知识有扎实的理解。通过不断实践和学习,开发者可以更熟练地处理这类问题。
在Android开发中,实现图片浏览的全屏缩放效果是一项常见的需求,特别是在社交应用中,如QQ好友动态和微信朋友圈。这种功能不仅需要提供良好的用户体验,还需要考虑性能和内存优化,因为图片通常较大,处理不当可能...
本教程将详细讲解如何在Android应用中实现批量下载图片并进行高效缓存,以实现非常流畅的用户体验。我们将主要关注LruCache技术,这是一种内存管理策略,有助于优化内存使用。 首先,我们需要理解Android中的图片...
2. **创建网络请求**: 使用Volley的`ImageLoader`类来下载图片。首先,创建一个自定义的`RequestQueue`实例,并初始化`ImageLoader`。在`Application`类或者某个初始化的地方: ```java public class MyApplication ...
本文将详细讲解如何在Android中实现图片的选取、上传至服务器以及从服务器下载图片,并将其存储到MySQL数据库的过程。 首先,我们需要处理图片的选取。Android提供了多种方式让用户选择图片,如使用系统的相册或...
在Android应用开发中,图片处理是一项常见的需求,无论是社交应用中的个人头像设置,还是电商应用的商品图片上传,都需要对图片进行选择、裁剪、预览以及下载等操作。本项目"Android图片选择,裁剪,预览,下载"正是...
在Android开发中,从网络下载图片是一项常见的任务,特别是在构建应用程序时,比如社交应用、电商应用等。本文将深入探讨三种不同的方法来实现这一功能:Handler、AsyncTask以及线程池。 1. Handler机制下载图片: ...
本示例将围绕"android多线程下载图片例子"这一主题,深入探讨Android中异步加载图片的机制,并结合"AndroidUploadImages"这一文件名,推测这是关于图片上传或下载的实践案例。 1. **AsyncTask** Android提供了一个...
在Android开发中,从服务器端下载图片并保存到本地SD卡是常见的需求,尤其是在构建一个包含大量图片的应用,如新闻阅读、社交应用或者电商应用。这个过程涉及到网络请求、文件操作以及UI显示等多个方面。以下将详细...
在Android开发中,异步批量下载图片并缓存是一个常见的需求,特别是在开发涉及大量图片展示的应用时,如社交应用、电商应用等。本教程将基于提供的Android例子源码,深入探讨如何实现这一功能。 首先,我们需要理解...
在Android应用开发中,图片加载框架扮演着至关重要的角色,它们负责高效、流畅地加载和显示图片,同时处理内存和CPU资源的优化。本篇文章将深入探讨“Android图片加载框架”,特别是与Glide相关的知识。 Glide是一...
在Android开发中,处理网络下载大量图片是一项常见的任务,尤其在构建内容丰富的应用程序时,如社交媒体应用或电商平台。本文将详细解析如何实现异步加载大量图片,并探讨关键知识点,包括缓存策略、硬盘缓存、容错...