我使用Volley NetworkImageView
从互联网下载图片并显示在我的listview
中。现在,我希望在没有可用网络的情况下让Volley NetworkImageView
显示已保存的图像。 Volley
已将URL
的图片作为密钥缓存,因为当我使用
Entry entry = SingletonRequestQueue.getInstance(context).getRequestQueue().getCache().get(imageURL);
entry.data
不为空。但我的问题是图像分辨率很高,我不能使用
Bitmap b = BitmapFactory.decodeByteArray(entry.data, 0, entry.data.length);
因为它造成了很多延迟,我不得不重新发明轮子,因为我必须创建asynctask
,看看listview
滚动取消解码,回收bitmap
,创建内存缓存,找到最佳的示例值和...
所以更好的想法只是做一些让Volley NetworkImageView
使用自己的DiskLRUCache 的技巧,以便在没有网络时显示它们。
有什么想法吗?
我的代码:
public class SingletonRequestQueue {
private static SingletonRequestQueue mInstance;
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private static Context mCtx;
private LruBitmapCache mLruBitmapCache;
private SingletonRequestQueue(Context context) {
mCtx = context;
mRequestQueue = getRequestQueue();
mLruBitmapCache = new LruBitmapCache(LruBitmapCache.getCacheSize(context));
mImageLoader = new ImageLoader(mRequestQueue,mLruBitmapCache);
}
public static synchronized SingletonRequestQueue getInstance(Context context) {
if (mInstance == null) {
mInstance = new SingletonRequestQueue(context);
}
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() is key, it keeps you from leaking the
// Activity or BroadcastReceiver if someone passes one in.
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext(),new OkHttpStack());
// mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req) {
getRequestQueue().add(req);
}
public ImageLoader getImageLoader() {
return mImageLoader;
}
public LruBitmapCache getLruBitmapCache() {
return mLruBitmapCache;
}
public void setLruBitmapCache(LruBitmapCache lruBitmapCache) {
mLruBitmapCache = lruBitmapCache;
}
}
并在我的适配器中:
public IssueListAdapter(Context context, int resource, List<Issue> objects) {
super(context, resource, objects);
this.context = context;
this.mIssueList = objects;
mImageLoader = SingletonRequestQueue.getInstance(context).getImageLoader();
}
public static class ViewHolder{
public NetworkImageView mNetworkImageView;
public TextView mFee;
public TextView mName;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null){
holder = new ViewHolder();
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.gridview_issuelist_item, parent, false);
holder.mNetworkImageView = (NetworkImageView)convertView.findViewById(R.id.NetworkImageView_MainActivity_issue_image);
holder.mName = (TextView)convertView.findViewById(R.id.TextView_MainActivity_name);
holder.mFee = (TextView)convertView.findViewById(R.id.TextView_MainActivity_fee);
Utility.settingTypfaceFont(context, holder.mName);
Utility.settingTypfaceFont(context, holder.mFee);
convertView.setTag(holder);
}else{
holder = (ViewHolder)(convertView.getTag());
}
final Issue issue = mIssueList.get(position);
holder.mName.setText(issue.getTitle());
holder.mFee.setText(String.valueOf(issue.getFee()));
String imageURL = issue.getPublicCover();
holder.mNetworkImageView.setImageUrl(imageURL, mImageLoader);
holder.mNetworkImageView.setDefaultImageResId(R.drawable.placeholder2);;
/*
Entry entry = SingletonRequestQueue.getInstance(context).getRequestQueue().getCache().get(imageURL);
if(entry != null && entry.data != null){
byte[] imageByte = entry.data;
loadBitmap(imageByte, holder.mNetworkImageView,imageURL);
}else{
holder.mNetworkImageView.setImageUrl(imageURL, mImageLoader);
}*/
return convertView;
}
@Override
public int getCount() {
if(mIssueList != null){
return mIssueList.size();
}
else{
return 0;
}
}
public List<Issue> getIssueList() {
return mIssueList;
}
}
答案 0 :(得分:3)
我更喜欢使用Android-Universal-Image-Loader / Picasso进行Volley / retrofit,图片加载器libs在加载和缓存图像方面做得非常好。
默认情况下,他们使用一行代码处理所有内容:
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
此外,您还可以设置动画,调整图片大小并在加载时添加占位符。
答案 1 :(得分:2)
当您在离线状态下重新启动应用时,最后一件事就是磁盘缓存(即 DiskBasedCache )。 Volley的本地缓存由网络数据和响应头组成。但在这种情况下,我们只需要关注Cache-Control
标题。例如,如果服务器端返回该标头是“Cache-Control:max-age = 604800”,那就告诉Volley将响应资源缓存604800秒(来源HttpHeaderParser.parseCacheHeaders())。然后下次我们检索相同的url数据时会检查是否超过了缓存过期时间,最后决定从网络或本地检索。
按照您的描述,我想您的服务器端会为您提供类似Cache-Control:must-revalidate|proxy-revalidate|no-cache|no-store
的值,这就是您在离线时无法重复使用上次检索到的数据的原因。
现在有一个问题:一旦我们可以操纵缓存过期时间,我们就能够将时间增加到足够大的值,这样我们就可以确保我们在离线状态下使用这些数据。
不幸的是,Volley不支持我们这样做。那么,如果你能让服务器端为此提供可行的 max-age 呢?
如果没有,我建议你换到另一个满足这个要求的库。实际上有一个可以成为你的朋友,是Netroid。它基于Volley并提供了一些改进,不会让你非常改变你当前的代码。有了它,控制过期时间会容易得多,并且会有更多的功能。
mImageLoader = new SelfImageLoader(mRequestQueue, mLruBitmapCache) {
@Override
public void makeRequest(ImageRequest request) {
// you can manipulate the cache expire time with one line code.
request.setCacheExpireTime(TimeUnit.DAYS, 10);
// you can even according to the different request to
// set up the corresponding expire time.
if (request.getUrl().contains("/for_one_day/")) {
request.setCacheExpireTime(TimeUnit.DAYS, 1);
} else {
request.setCacheExpireTime(TimeUnit.DAYS, 10);
}
}
};
完整的代码在项目的示例模块中,我希望这会有所帮助。
答案 2 :(得分:2)
只需在BasicNetwork类中添加此行
即可if (!ConnectivityUtils.isNetworkEnabled(CardApplication.getContext()) && request instanceof ImageRequest) {
VolleyLog.e("Cached response", "No Network Connectivity for Url=", request.getUrl());
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry().data, responseHeaders, true);
}
并且对于数据请求到期,您可以使用自己的HttpHeaderParser更改Cached.Entry
这是Link详细解释的事情
答案 3 :(得分:1)
Hello @mmlooloo我创建了一个使用DiskLRUCache
和Volley
的项目。这是我的存储库DiskLRUCache using Volley的链接。希望它能帮助您显示已保存的图像。感谢。
答案 4 :(得分:0)
LruCache
)。
if(networkAvailable()){ getFromNetwork()} else { getFromCache()}
逻辑还可以吗?然后试试。
您的缓存impl类似乎是LruBitmapCache
。
public Bitmap getBitmap(String url) {
if(networkAvailable()/* this is your impl */){
// dont use cache
return null;
}
return getFromCache(); // or something like that;
}
答案 5 :(得分:0)
如果我理解正确,如果您ImageLoader
使用的NetworkImageView
类提供的内存缓存将在应用运行之间保留,那么您将受益,而不会失去以下事实:它是内存缓存。
内存缓存在正常操作中保持正确大小的位图 - 即使网络出现故障,您仍可以使用它。
所以这里有一个想法:每次关闭应用程序时,都要保留缓存中的图像文件。下次加载应用程序时,在创建内存缓存时 - 检查磁盘上的持久版本,以及它是否可用 - 从磁盘填充内存缓存。
您可以采用多种方法来决定何时保留图像以及何时删除图像。
以下是一种方法:创建混合内存/磁盘缓存。它的工作原理与您的内存缓存完全相同,但有以下不同之处:
putBitmap()
时,与正常操作一起,在后台线程/ AsyncTask
中将位图的编码版本保存到磁盘。AsyncTask
上从磁盘中删除该文件。您无法解码位图,但是您可以缩小尺寸并处理调整大位图的大小。
这对你有帮助吗?