如何使Volley NetworkImageView脱机

时间:2014-10-25 08:22:47

标签: android android-volley

我使用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;
}

}

6 个答案:

答案 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我创建了一个使用DiskLRUCacheVolley的项目。这是我的存储库DiskLRUCache using Volley的链接。希望它能帮助您显示已保存的图像。感谢。

答案 4 :(得分:0)

  1. 搜索互联网&#34; android如何检查互联网连接&#34;
  2. 实现并在缓存实现中检查它(如LruCache)。 if(networkAvailable()){ getFromNetwork()} else { getFromCache()}
  3. 逻辑还可以吗?然后试试。
    您的缓存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类提供的内存缓存将在应用运行之间保留,那么您将受益,而不会失去以下事实:它是内存缓存。

内存缓存在正常操作中保持正确大小的位图 - 即使网络出现故障,您仍可以使用它。

所以这里有一个想法:每次关闭应用程序时,都要保留缓存中的图像文件。下次加载应用程序时,在创建内存缓存时 - 检查磁盘上的持久版本,以及它是否可用 - 从磁盘填充内存缓存。

您可以采用多种方法来决定何时保留图像以及何时删除图像。

以下是一种方法:创建混合内存/磁盘缓存。它的工作原理与您的内存缓存完全相同,但有以下不同之处:

  1. 每次调用putBitmap()时,与正常操作一起,在后台线程/ AsyncTask中将位图的编码版本保存到磁盘。
  2. 每次从缓存中删除位图时(我假设您在缓存上有某种空间限制),请在后台线程/ AsyncTask上从磁盘中删除该文件。
  3. 创建&#34; loadFromDisk&#34;任务,每次创建内存缓存(每个应用程序运行一次)后在后台执行,以使用磁盘中的可用映像填充内存缓存。
  4. 您无法解码位图,但是您可以缩小尺寸并处理调整大位图的大小。

    这对你有帮助吗?