您的位置:首页 > 移动开发 > Android开发

Android 的图片异步请求加三级缓存 ACE

2016-02-19 00:18 495 查看
使用xUtils等框架是很方便,但今天要用代码实现bitmapUtils的功能,很简单,

1AsyncTask请求一张图片

####AsyncTask

#####AsyncTask是线程池+handler的封装第一个泛型:传参的参数类型类型(和doInBackground一致)第二个泛型:
#####更新进度的参数类型(和onProgressUpdate一致)第三个泛型:返回结果的参数类型(和onPostExecute一致,
#####和doInBackground返回类型一致)

看AsyncTask源码:

publicabstractclassAsyncTask<Params,Progress,Result>{
privatestaticfinalStringLOG_TAG="AsyncTask";

privatestaticfinalintCORE_POOL_SIZE=5;
privatestaticfinalintMAXIMUM_POOL_SIZE=128;
privatestaticfinalintKEEP_ALIVE=1;

privatestaticfinalThreadFactorysThreadFactory=newThreadFactory(){
privatefinalAtomicIntegermCount=newAtomicInteger(1);

publicThreadnewThread(Runnabler){
returnnewThread(r,"AsyncTask#"+mCount.getAndIncrement());
}
};


核心线程5最大线程128这是AsyncTask的线程池然后通过handler发送消息,它内部实例化了一个静态的自定义类InternalHandler,这个类是继承自Handler的,在这个自定义类中绑定了一个叫做AsyncTaskResult的对象,每次子线程需要通知主线程,就调用sendToTarget发送消息给handler自己。然后在handler的handleMessage中AsyncTaskResult根据消息的类型不同(例如MESSAGE_POST_PROGRESS会更新进度条,MESSAGE_POST_CANCEL取消任务)而做不同的操作,值得一提的是,这些操作都是在UI线程进行的,意味着,从子线程一旦需要和UI线程交互,内部自动调用了handler对象把消息放在了主线程了。

privatestaticfinalInternalHandlersHandler=newInternalHandler();

mFuture=newFutureTask<Result>(mWorker){
@Override
protectedvoidMore...done(){
Messagemessage;
Resultresult=null;
try{
result=get();
}catch(InterruptedExceptione){
android.util.Log.w(LOG_TAG,e);
}catch(ExecutionExceptione){
thrownewRuntimeException("AnerroroccuredwhileexecutingdoInBackground()",
e.getCause());
}catch(CancellationExceptione){
message=sHandler.obtainMessage(MESSAGE_POST_CANCEL,
newAsyncTaskResult<Result>(AsyncTask.this,(Result[])null));
message.sendToTarget();
return;
}catch(Throwablet){
thrownewRuntimeException("Anerroroccuredwhileexecuting"
+"doInBackground()",t);
}

message=sHandler.obtainMessage(MESSAGE_POST_RESULT,
newAsyncTaskResult<Result>(AsyncTask.this,result));
message.sendToTarget();
}
};
privatestaticclassInternalHandlerextendsHandler{
@SuppressWarnings({"unchecked","RawUseOfParameterizedType"})
@Override
publicvoidMore...handleMessage(Messagemsg){
AsyncTaskResultresult=(AsyncTaskResult)msg.obj;
switch(msg.what){
caseMESSAGE_POST_RESULT:
//Thereisonlyoneresult
result.mTask.finish(result.mData[0]);
break;
caseMESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
caseMESSAGE_POST_CANCEL:
result.mTask.onCancelled();
break;
}
}
}


下面看代码第一步我们先请求一张图片并解析注释写的很详细了.

NetCacheUtils.java

importjava.io.InputStream;
importjava.net.HttpURLConnection;
importjava.net.URL;

importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importandroid.os.AsyncTask;
importandroid.widget.ImageView;

/**
*网络缓存
*
*@authorAce
*@date2016-02-18
*/
publicclassNetCacheUtils{

privateLocalCacheUtilsmLocalUtils;
privateMemoryCacheUtilsmMemoryUtils;

publicNetCacheUtils(LocalCacheUtilslocalUtils,
MemoryCacheUtilsmemoryUtils){
mLocalUtils=localUtils;
mMemoryUtils=memoryUtils;
}

publicvoidgetBitmapFromNet(ImageViewimageView,Stringurl){
BitmapTasktask=newBitmapTask();
task.execute(imageView,url);
}

/**
*AsyncTask是线程池+handler的封装第一个泛型:传参的参数类型类型(和doInBackground一致)第二个泛型:
*更新进度的参数类型(和onProgressUpdate一致)第三个泛型:返回结果的参数类型(和onPostExecute一致,
*和doInBackground返回类型一致)
*/
classBitmapTaskextendsAsyncTask<Object,Integer,Bitmap>{

privateImageViewmImageView;
privateStringurl;

//主线程运行,预加载
@Override
protectedvoidonPreExecute(){
super.onPreExecute();
}

//子线程运行,异步加载逻辑在此方法中处理
@Override
protectedBitmapdoInBackground(Object...params){
mImageView=(ImageView)params[0];
url=(String)params[1];
mImageView.setTag(url);//将imageView和url绑定在一起
//publishProgress(values)//通知进度

//下载图片
returndownload(url);
}

//主线程运行,更新进度
@Override
protectedvoidonProgressUpdate(Integer...values){
super.onProgressUpdate(values);
}

//主线程运行,更新主界面
@Override
protectedvoidonPostExecute(Bitmapresult){
if(result!=null){
//判断当前图片是否就是imageView要的图片,防止listview重用导致的图片错乱的情况出现
StringbindUrl=(String)mImageView.getTag();
if(bindUrl.equals(url)){
//给imageView设置图片
mImageView.setImageBitmap(result);
//将图片保存在本地
mLocalUtils.setBitmapToLocal(result,url);

//将图片保存在内存
mMemoryUtils.setBitmapToMemory(url,result);
}
}
}

}

/**
*下载图片
*
*@paramurl
*/
publicBitmapdownload(Stringurl){
HttpURLConnectionconn=null;
try{
conn=(HttpURLConnection)(newURL(url).openConnection());

conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");

conn.connect();

intresponseCode=conn.getResponseCode();
if(responseCode==200){
InputStreamin=conn.getInputStream();
//将流转化为bitmap对象
Bitmapbitmap=BitmapFactory.decodeStream(in);
returnbitmap;
}

}catch(Exceptione){
e.printStackTrace();
}finally{
if(conn!=null){
conn.disconnect();
}
}

returnnull;
}

}


MemoryCacheUtils.java用到了LruCache很简单
我简单翻译下文档:


*Acachethatholdsstrongreferencestoalimitednumberofvalues.Eachtime

*avalueisaccessed,itismovedtotheheadofaqueue.Whenavalueis

*addedtoafullcache,thevalueattheendofthatqueueisevictedandmay
*becomeeligibleforgarbagecollection.

*Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。
*当cache已满的时候加入新的item时,在队列尾部的item会被回收。

*<p>Ifyourcachedvaluesholdresourcesthatneedtobeexplicitlyreleased,
*override{@link#entryRemoved}.
*如果你cache的某个值需要明确释放,重写entryRemoved()

*<p>Bydefault,thecachesizeismeasuredinthenumberofentries.Override
*{@link#sizeOf}tosizethecacheindifferentunits.Forexample,thiscache
*islimitedto4MiBofbitmaps:默认cache大小是测量的item的数量,重写sizeof计算不同item的
*大小。

{@code

*intcacheSize=4*1024*1024;//4MiB

*LruCache<String,Bitmap>bitmapCache=newLruCache<String,Bitmap>(cacheSize){

*protectedintsizeOf(Stringkey,Bitmapvalue){

*returnvalue.getByteCount();

*}

*}}

-------------------------------------------------------------------

<p>Thisclassisthread-safe.Performmultiplecacheoperationsatomicallyby
*synchronizingonthecache:<pre>{@code
*synchronized(cache){
*if(cache.get(key)==null){
*cache.put(key,value);
*}
*}}</pre>
*他是线程安全的,自动地执行多个缓存操作并且加锁

-------------------------

<p>Thisclassdoesnotallownulltobeusedasakeyorvalue.Areturn
*valueofnullfrom{@link#get},{@link#put}or{@link#remove}is
*unambiguous:thekeywasnotinthecache.
*不允许key或者value为null
*当get(),put(),remove()返回值为null时,key相应的项不在cache中

最重要的大概就是以上几点:使用很简单
来看代码


importandroid.graphics.Bitmap;
importandroid.support.v4.util.LruCache;

/**
*内存缓存工具类
*
*@authorAce
*@date2016-02-19
*/
publicclassMemoryCacheUtils{

//Android2.3(APILevel
//9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠,建议用LruCache,它是强引用

privateLruCache<String,Bitmap>mCache;

publicMemoryCacheUtils(){
intmaxMemory=(int)Runtime.getRuntime().maxMemory();//获取虚拟机分配的最大内存
//16M
//LRU最近最少使用,通过控制内存不要超过最大值(由开发者指定),来解决内存溢出,就像上面翻译的所说如果cache满了会清理最近最少使用的缓存对象
mCache=newLruCache<String,Bitmap>(maxMemory/8){
@Override
protectedintsizeOf(Stringkey,Bitmapvalue){
//计算一个bitmap的大小
intsize=value.getRowBytes()*value.getHeight();//每一行的字节数乘以高度
returnsize;
}
};
}

publicBitmapgetBitmapFromMemory(Stringurl){

returnmCache.get(url);
}

publicvoidsetBitmapToMemory(Stringurl,Bitmapbitmap){

mCache.put(url,bitmap);
}

}



最后一级缓存本地缓存把网络下载的图片文件名以MD5的形式保存到内存卡的制定目录

/**
*本地缓存工具类
*
*@authorAce
*@date2016-02-19
*/
publicclassLocalCacheUtils{

//图片缓存的文件夹
publicstaticfinalStringDIR_PATH=Environment
.getExternalStorageDirectory().getAbsolutePath()
+"/ace_bitmap_cache";

publicBitmapgetBitmapFromLocal(Stringurl){
try{
Filefile=newFile(DIR_PATH,MD5Encoder.encode(url));

if(file.exists()){
Bitmapbitmap=BitmapFactory.decodeStream(newFileInputStream(
file));
returnbitmap;
}

}catch(Exceptione){
e.printStackTrace();
}

returnnull;
}

publicvoidsetBitmapToLocal(Bitmapbitmap,Stringurl){
FiledirFile=newFile(DIR_PATH);

//创建文件夹文件夹不存在或者它不是文件夹则创建一个文件夹.mkdirs,mkdir的区别在于假如文件夹有好几层路径的话,前者会创建缺失的父目录后者不会创建这些父目录
if(!dirFile.exists()||!dirFile.isDirectory()){
dirFile.mkdirs();
}

try{
Filefile=newFile(DIR_PATH,MD5Encoder.encode(url));
//将图片压缩保存在本地,参1:压缩格式;参2:压缩质量(0-100);参3:输出流
bitmap.compress(CompressFormat.JPEG,100,
newFileOutputStream(file));
}catch(Exceptione){
e.printStackTrace();
}
}

}


MD5Encoder

importjava.security.MessageDigest;

publicclassMD5Encoder{

publicstaticStringencode(Stringstring)throwsException{
byte[]hash=MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
StringBuilderhex=newStringBuilder(hash.length*2);
for(byteb:hash){
if((b&0xFF)<0x10){
hex.append("0");
}
hex.append(Integer.toHexString(b&0xFF));
}
returnhex.toString();
}
}


最后新建一个工具类来使用我们上面的三个缓存工具类

/**
*三级缓存工具类
*
*@authorAce
*@date2016-02-19
*/
publicclassMyBitmapUtils{

//网络缓存工具类
privateNetCacheUtilsmNetUtils;
//本地缓存工具类
privateLocalCacheUtilsmLocalUtils;
//内存缓存工具类
privateMemoryCacheUtilsmMemoryUtils;

publicMyBitmapUtils(){
mMemoryUtils=newMemoryCacheUtils();
mLocalUtils=newLocalCacheUtils();
mNetUtils=newNetCacheUtils(mLocalUtils,mMemoryUtils);
}

publicvoiddisplay(ImageViewimageView,Stringurl){
//设置默认加载图片
imageView.setImageResource(R.drawable.news_pic_default);

//先从内存缓存加载
Bitmapbitmap=mMemoryUtils.getBitmapFromMemory(url);
if(bitmap!=null){
imageView.setImageBitmap(bitmap);
System.out.println("从内存读取图片啦...");
return;
}

//再从本地缓存加载
bitmap=mLocalUtils.getBitmapFromLocal(url);
if(bitmap!=null){
imageView.setImageBitmap(bitmap);
System.out.println("从本地读取图片啦...");
//给内存设置图片
mMemoryUtils.setBitmapToMemory(url,bitmap);
return;
}

//从网络缓存加载
mNetUtils.getBitmapFromNet(imageView,url);
}

}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: