Android中图片的三级缓存机制
我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制。
原理:
首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存缓存中,缓存到强引用中也就是LruCache中。如果强引用中空间不足,就会将较早存储的图片对象驱逐到软引用(softReference)中存储,然后将图片缓存到文件(内部存储外部存储)中;读取图片的时候,先读取内存缓存,判断强引用中是否存在图片,如果强引用中存在,则直接读取,如果强引用中不存在,则判断软引用中是否存在,如果软引用中存在,则将软引用中的图片添加到强引用中并且删除软引用中的数据,如果软引用中不存在,则读取文件存储,如果文件存储不存在,则网络加载。
下载:网络--内存--文件
读取:内存--强引用--软引用--文件--网络
也就是这样的一个过程,下面用一个简单地demo来演示一下图片你的三级缓存,此demo中只有一个界面,界面上一个ImageView用来显示图片,一个按钮用来点击的时候加载图片。布局如下:
<?xmlversion="1.0"encoding="utf-8"?> <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/iv_img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher" android:layout_centerInParent="true"/> <Button android:id="@+id/btn_download" android:layout_below="@+id/iv_img" android:layout_centerHorizontal="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="加载图片"/> </RelativeLayout>
因为要从网络下载数据,还要存储到本地sd卡中,所以不要忘了为程序添加网络访问的权限、网络状态访问的权限和向外部存储设备写内容的权限:
<uses-permissionandroid:name="android.permission.INTERNET"/> <uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
接着,创建一个HttpUtils工具类用于访问网络,代码如下:
packagecom.yztc.lx.cashimg; importandroid.content.Context; importandroid.net.ConnectivityManager; importandroid.net.NetworkInfo; importjava.io.ByteArrayOutputStream; importjava.io.IOException; importjava.io.InputStream; importjava.net.HttpURLConnection; importjava.net.URL; /**网络访问工具类 *CreatedbyLxon2016/8/19. */ publicclassHttpUtils{ /** *判断网络连接是否通畅 *@parammContext *@return */ publicstaticbooleanisNetConn(ContextmContext){ ConnectivityManagermanager=(ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfoinfo=manager.getActiveNetworkInfo(); if(info!=null){ returninfo.isConnected(); }else{ returnfalse; } } /** *根据path下载网络上的数据 *@parampath路径 *@return返回下载内容的byte数据形式 */ publicstaticbyte[]getDateFromNet(Stringpath){ ByteArrayOutputStreambaos=newByteArrayOutputStream(); try{ URLurl=newURL(path); HttpURLConnectionconn=(HttpURLConnection)url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setDoInput(true); conn.connect(); if(conn.getResponseCode()==200){ InputStreamis=conn.getInputStream(); byteb[]=newbyte[1024]; intlen; while((len=is.read(b))!=-1){ baos.write(b,0,len); } returnbaos.toByteArray(); } }catch(IOExceptione){ e.printStackTrace(); } returnbaos.toByteArray(); } }
还有操作外部存储的工具类:
packagecom.yztc.lx.cashimg; importandroid.graphics.Bitmap; importandroid.graphics.BitmapFactory; importandroid.os.Environment; importjava.io.ByteArrayOutputStream; importjava.io.File; importjava.io.FileInputStream; importjava.io.FileOutputStream; importjava.io.IOException; /** *CreatedbyLxon2016/8/20. */ publicclassExternalStorageUtils{ /** *将传递过来的图片byte数组存储到sd卡中 *@paramimgName图片的名字 *@parambuffbyte数组 *@return返回是否存储成功 */ publicstaticbooleanstoreToSDRoot(StringimgName,bytebuff[]){ booleanb=false; StringbasePath=Environment.getExternalStorageDirectory().getAbsolutePath(); Filefile=newFile(basePath,imgName); try{ FileOutputStreamfos=newFileOutputStream(file); fos.write(buff); fos.close(); b=true; }catch(IOExceptione){ e.printStackTrace(); } returnb; } /** *从本地内存中根据图片名字获取图片 *@paramimgName图片名字 *@return返回图片的Bitmap格式 */ publicstaticBitmapgetImgFromSDRoot(StringimgName){ Bitmapbitmap=null; StringbasePath=Environment.getExternalStorageDirectory().getAbsolutePath(); Filefile=newFile(basePath,imgName); try{ FileInputStreamfis=newFileInputStream(file); ByteArrayOutputStreambaos=newByteArrayOutputStream(); byteb[]=newbyte[1024]; intlen; while((len=fis.read(b))!=-1){ baos.write(b,0,len); } bytebuff[]=baos.toByteArray(); if(buff!=null&&buff.length!=0){ bitmap=BitmapFactory.decodeByteArray(buff,0,buff.length); } }catch(IOExceptione){ e.printStackTrace(); } returnbitmap; } }
本例中将图片默认存在了sd卡根目录中。
然后是最主要的主函数了:
packagecom.yztc.lx.cashimg; importandroid.graphics.Bitmap; importandroid.graphics.BitmapFactory; importandroid.os.Bundle; importandroid.os.Handler; importandroid.os.Message; importandroid.support.v7.app.AppCompatActivity; importandroid.util.Log; importandroid.util.LruCache; importandroid.view.View; importandroid.widget.Button; importandroid.widget.ImageView; importandroid.widget.Toast; importjava.lang.ref.SoftReference; importjava.util.LinkedHashMap; publicclassMainActivityextendsAppCompatActivityimplementsView.OnClickListener{ privateButtonbtn_download; privateImageViewiv_img; privateMyLruCachemyLruCache; privateLinkedHashMap<String,SoftReference<Bitmap>>cashMap=newLinkedHashMap<>(); privatestaticfinalStringTAG="MainActivity"; privateStringimgPath="http://www.3dmgame.com/UploadFiles/201212/Medium_20121217143424221.jpg"; privateHandlerhandler=newHandler(){ @Override publicvoidhandleMessage(Messagemsg){ Bitmapbitmap=(Bitmap)msg.obj; iv_img.setImageBitmap(bitmap); Toast.makeText(MainActivity.this,"从网络上下载图片",Toast.LENGTH_SHORT).show(); } }; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); inttotalMemory=(int)Runtime.getRuntime().maxMemory(); intsize=totalMemory/8; myLruCache=newMyLruCache(size); btn_download.setOnClickListener(this); } privatevoidinitView(){ btn_download=(Button)findViewById(R.id.btn_download); iv_img=(ImageView)findViewById(R.id.iv_img); } @Override publicvoidonClick(Viewv){ Bitmapb=getImgCache(); if(b!=null){ iv_img.setImageBitmap(b); }else{ newThread(newRunnable(){ @Override publicvoidrun(){ if(HttpUtils.isNetConn(MainActivity.this)){ byteb[]=HttpUtils.getDateFromNet(imgPath); if(b!=null&&b.length!=0){ Bitmapbitmap=BitmapFactory.decodeByteArray(b,0,b.length); Messagemsg=Message.obtain(); msg.obj=bitmap; handler.sendMessage(msg); myLruCache.put(imgPath,bitmap); Log.d(TAG,"run:"+"缓存到强引用中成功"); booleanbl=ExternalStorageUtils.storeToSDRoot("haha.jpg",b); if(bl){ Log.d(TAG,"run:"+"缓存到本地内存成功"); }else{ Log.d(TAG,"run:"+"缓存到本地内存失败"); } }else{ Toast.makeText(MainActivity.this,"下载失败!",Toast.LENGTH_SHORT).show(); } }else{ Toast.makeText(MainActivity.this,"请检查你的网络!",Toast.LENGTH_SHORT).show(); } } }).start(); } } /** *从缓存中获取图片 * *@return返回获取到的Bitmap */ publicBitmapgetImgCache(){ Bitmapbitmap=myLruCache.get(imgPath); if(bitmap!=null){ Log.d(TAG,"getImgCache:"+"从LruCache获取图片"); }else{ SoftReference<Bitmap>sr=cashMap.get(imgPath); if(sr!=null){ bitmap=sr.get(); myLruCache.put(imgPath,bitmap); cashMap.remove(imgPath); Log.d(TAG,"getImgCache:"+"从软引用获取图片"); }else{ bitmap=ExternalStorageUtils.getImgFromSDRoot("haha.jpg"); Log.d(TAG,"getImgCache:"+"从外部存储获取图片"); } } returnbitmap; } /** *自定义一个方法继承系统的LruCache方法 */ publicclassMyLruCacheextendsLruCache<String,Bitmap>{ /** *必须重写的构造函数,定义强引用缓存区的大小 *@parammaxSizeforcachesthatdonotoverride{@link#sizeOf},thisis *themaximumnumberofentriesinthecache.Forallothercaches, *thisisthemaximumsumofthesizesoftheentriesinthiscache. */ publicMyLruCache(intmaxSize){ super(maxSize); } //返回每个图片的大小 @Override protectedintsizeOf(Stringkey,Bitmapvalue){ //获取当前变量每行的字节数和行高度(基本是固定写法,记不住给我背!) returnvalue.getRowBytes()*value.getHeight(); } /** *当LruCache中的数据被驱逐或是移除时回调的函数 * *@paramevicted当LruCache中的数据被驱逐用来给新的value倒出空间的时候变化 *@paramkey用来标示对象的键,一般put的时候传入图片的url地址 *@paramoldValue之前存储的旧的对象 *@paramnewValue存储的新的对象 */ @Override protectedvoidentryRemoved(booleanevicted,Stringkey,BitmapoldValue,BitmapnewValue){ if(evicted){ /** *将旧的值存到软引用中,因为强引用中可能有多个值被驱逐, *所以创建一个LinkedHashMap<String,SoftReference<Bitmap>>来存储软引用 *基本也是固定写法 */ SoftReference<Bitmap>softReference=newSoftReference<Bitmap>(oldValue); cashMap.put(key,softReference); } } } }
基本的思路都在代码注释中写的很详细了,主要就是要自定义一个类,来继承系统的LruCache,实现其中的两个主要的方法sizeOf()和entryRemoved(),还有就是必须重写它的构造函数。
以上所述是小编给大家介绍的Android中图片的三级缓存机制的全部叙述,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的,在此也非常感谢大家对毛票票网站的支持!