最近遇到一个问题,项目用的图片加载框架是Picasso,网络加载框架是okhttp,项目在加载轮播图时有时可以正常加载,有时,会加载失败,提示decodestream时返回null。

首先,需要确定是哪个环节出了问题。

网上搜了很多关于“decodestream时返回null”的问题,都说需要在decodestream之前reset stream。不过,由于用的是Picasso,不太方便改代码,再换个思路看一下。

要确认是否是Picasso的问题,那我就用其他图片加载框架替换Picasso,这里,我使用Glide替换Picasso。

替换之后,运行程序,图片加载没有任何问题。

由此,可以确认,问题出在Picasso身上。

于是,我又在网上搜索关于Picasso的这类问题,在百度上,完全没有类似问题,google上倒是可以找到几个类似问题,百度在这方面还需要加油啊。。。

google上搜到的都是GitHub上Picasso使用者提的issue,这里也是说是decodestream调用两次的问题。

于是我查看源码:

 static Bitmap decodeStream(InputStream stream, Request request) throws IOException {
MarkableInputStream markStream = new MarkableInputStream(stream);
stream = markStream; long mark = markStream.savePosition(65536); // TODO fix this crap. final BitmapFactory.Options options = RequestHandler.createBitmapOptions(request);
final boolean calculateSize = RequestHandler.requiresInSampleSize(options); boolean isWebPFile = Utils.isWebPFile(stream);
markStream.reset(mark);
// When decode WebP network stream, BitmapFactory throw JNI Exception and make app crash.
// Decode byte array instead
if (isWebPFile) {
byte[] bytes = Utils.toByteArray(stream);
if (calculateSize) {
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
request);
}
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
} else {
if (calculateSize) {
BitmapFactory.decodeStream(stream, null, options);
RequestHandler.calculateInSampleSize(request.targetWidth, request.targetHeight, options,
request); markStream.reset(mark);
}
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
if (bitmap == null) {
// Treat null as an IO exception, we will eventually retry.
throw new IOException("Failed to decode stream.");
}
return bitmap;
}
}

并定位到这个地方,进行单步调试,发现decodestream确实调用了两次,但是,jack已经进行了reset操作。这个地方不应该会出现问题。

继续找资料,在Github找到一个解决方案:在url后面加上“?”+System.currentTimeMillis()。

经测试,这样做可以正确加载图片。

为什么url后加时间戳就能成功呢?

url加时间戳后每次都从网络下载图片,没加的话会从缓存中加载图片。

据此,我有理由怀疑,是缓存问题,导致这个问题出现。

我在原有代码的基础上(url后面不加“?”+时间戳)加上

.memoryPolicy(MemoryPolicy.NO_CACHE)
.networkPolicy(NetworkPolicy.NO_CACHE) 再次调试,发现可以正常加载图片。
由此可以确定:是缓存问题导致加载图片失败。
嗯,接下去该自定义缓存试一下了。
再次到网上搜索资料(爱分享的程序员/媛就是这么招人喜欢)
参考http://www.jianshu.com/p/6241950f9daf写了个自定义缓存。
贴几处关键代码:
OkHttpClient client = new OkHttpClient
.Builder()
.cache(new Cache(BaseApplication.getContext().getCacheDir(), 1000 * 1024))
.addInterceptor(new CaheInterceptor(BaseApplication.getContext()))
.addNetworkInterceptor(new CaheInterceptor(BaseApplication.getContext()))
.build();
mPicasso = new Picasso.Builder(BaseApplication.getContext())
.downloader(new ImageDownLoader(client))
.build();
mPicasso.with(BaseApplication.getContext()).setIndicatorsEnabled(true);//左上角标出颜色,红色为从网络获取,绿色为从内存中获取,蓝色为从硬盘中获取
public class ImageDownLoader implements Downloader {
OkHttpClient client = null; public ImageDownLoader(OkHttpClient client) {
this.client = client;
} @Override
public Response load(Uri uri, int networkPolicy) throws IOException { CacheControl cacheControl = null;
if (networkPolicy != 0) {
if (NetworkPolicy.isOfflineOnly(networkPolicy)) {
cacheControl = CacheControl.FORCE_CACHE;
} else {
CacheControl.Builder builder = new CacheControl.Builder();
if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
builder.noCache();
}
if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
builder.noStore();
}
cacheControl = builder.build();
}
} Request.Builder builder = new Request.Builder().url(uri.toString());
if (cacheControl != null) {
builder.cacheControl(cacheControl);
} okhttp3.Response response = client.newCall(builder.build()).execute();
int responseCode = response.code();
if (responseCode >= 300) {
response.body().close();
throw new ResponseException(responseCode + " " + response.message(), networkPolicy,
responseCode);
} boolean fromCache = response.cacheResponse() != null; ResponseBody responseBody = response.body();
return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength()); } @Override
public void shutdown() { Cache cache = client.cache();
if (cache != null) {
try {
cache.close();
} catch (IOException ignored) {
}
}
}
}
public class CaheInterceptor implements Interceptor {

    private Context context;

    public CaheInterceptor(Context context) {
this.context = context;
} @Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (NetStateUtil.isNetworkAvailable(context)) {
Response response = chain.proceed(request);
// read from cache for 60 s
int maxAge = 300;
String cacheControl = request.cacheControl().toString();
Log.e("Tamic", maxAge + "s load cahe:" + cacheControl);
return response.newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-Control")
.header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else {
Log.e("Tamic", " no network load cahe");
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
Response response = chain.proceed(request);
//set cahe times is 3 days
int maxStale = 60 * 60 * 24 * 3;
return response.newBuilder()
.removeHeader("Pragma")
.removeHeader("Cache-Control")
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
}
}

加入缓存后,删除之前测试加上的代码,再次调试,正常加载图片。

Tips:根源还是在picasso2.5.2底层用的okhttp2,而我在项目中用的是okhttp3,完全不匹配啊,听说picasso2.5.3解决了这个问题,但是

我并没有找到这个版本,所以先用这个方案~~~写出来容易。。。改这个问题也花了我不少时间~~~咱们程序员还是需要多研究、多分享啊~~~

 

Picasso加载网络图片失败,提示decodestream时返回null的更多相关文章

  1. SDWebImage 加载网络图片失败,重新运行,就能加载成功。

    现象: 使用SDWebImage 加载网络图片,偶尔会有一两张图片就是显示不出来.重新运行有时又可以了. 这个问题的原因是: 当SDWebImage 在加载图片的时候 我用的是- (void)sd_s ...

  2. img 加载网络图片失败 显示默认图片

    1. 概述 当从网络加载图片失败 希望显示默认图 img 标签有个 onerror属性 2. 代码 2.1 java服务端组织标签整个返回前端 String imgUrl = "javasc ...

  3. Android 使用Picasso加载网络图片等比例缩放

    在做android图片加载的时候,由于手机屏幕受限,很多大图加载过来的时候,我们要求等比例缩放,比如按照固定的宽度,等比例缩放高度,使得图片的尺寸比例得到相应的缩放,但图片没有变形.显然按照andro ...

  4. Android加载网络图片学习过程

    好多应用,像我们公司的<乘友>还有其他的<飞鸽><陌陌><啪啪>这些,几乎每一款应用都需要加载网络图片,那ToYueXinShangWan,这是比须熟练 ...

  5. 【转载】一行代码加载网络图片到ImageView——Android Picasso

    原文链接:一句代码加载网络图片到ImageView——Android Picasso  注意:此处使用下面代码需要先配置一下gradle,下载所需包. 具体操作如下图: compile 'com.sq ...

  6. Android Volley入门到精通:使用Volley加载网络图片

    在上一篇文章中,我们了解了Volley到底是什么,以及它的基本用法.本篇文章中我们即将学习关于Volley更加高级的用法,如何你还没有看过我的上一篇文章的话,建议先去阅读Android Volley完 ...

  7. 用Volley让GridView加载网络图片

    一.布局文件 总共两个布局文件,一个是GridView,还有一个是GridView的item,是NetworkImageView和TextView activity_main.xml <Rela ...

  8. Android中用双缓存技术,加载网络图片

    最近在学校参加一个比赛,写的一个Android应用,里面要加载大量的网络图片,可是用传统的方法图片一多就会造成程序出现内存溢出而崩溃.因为自己也在学习中,所以看了很多博客和视频,然后参照这些大神的写源 ...

  9. Swift - 表格图片加载优化(拖动表格时不加载,停止时只加载当前页图片)

    列表的单元格中包含有图片在开发中很常见.通常我们可以直接在tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIn ...

随机推荐

  1. 最大连续子序列 -- hdu -- 1231

    http://acm.hdu.edu.cn/showproblem.php?pid=1231 最大连续子序列 Time Limit: 2000/1000 MS (Java/Others)    Mem ...

  2. C/C++中如何获取数组的长度?

    C/C++中如何获取数组的长度? 收藏 C.C++中没有提供 直接获取数组长度的函数,对于存放字符串的字符数组提供了一个strlen函数获取长度,那么对于其他类型的数组如何获取他们的长度呢?其中一种方 ...

  3. springMVC ModelAndView 作用与功能解析 【转】

    Spring mvc视图机制 所有的web应用的mvc框架都有它定位视图的方式.Spring提供了视图解析器供你在浏览器中显示模型数据,而不必被拘束在特定的视图技术上. Spring的控制器Contr ...

  4. Eclipse技巧

    1 alt + / 提示 2 ctrl + shift + g 查找方法被谁调用 3 ctrl + t 查看某个类的继承关系 4 alt + 上/下 移动当前行上或者下 5 ctrl + / 行注释 ...

  5. [翻译]NUnit---RequiresSTA and RequiresThread Attributes(十七)

    RequiresSTAAttribute (NUnit 2.5) RequiresSTA特性用于测试方法.类.程序集中指定测试应该在单线程中运行.如果父测试不在单线程中运行则会创建一个新的线程. No ...

  6. onsrcoll和scrollTop兼容与实现

    对于onscroll事件的支持 各浏览器 document.document.body.document.documentElement 对象的 onscroll 事件的支持存在差异. 所谓的支持性存 ...

  7. vhosetuser 和 vhostuservlient 差异

    Open vSwitch支持的vHost-user类型 在Open vSwitch中vHost User通过socket进行通信,模式为client-server,其中server端负责创建/管理/销 ...

  8. 程序媛计划——python数据库

    #实例:用数据库存储日记,实现日记本功能 #流程 #创建数据库 #coding:utf-8 import sqlite3 connect=sqlite3.connect('test.db') conn ...

  9. brew - 更换国内源

    brew如果不换成国内源,安装软件时候可能会出问题,不是安装不了就是速度很慢,所以使用它,更换国内游是比较好的选择! 我更换的是清华大学开源软件镜像站,打开shell窗口,依次执行下面命令: cd & ...

  10. 【DC010沙龙年度合集】顶尖Hacking技术盛宴(文末福利)

    岁末盘点,让我们一起回顾2017年DEFCON GROUP 010带来的那些最前端的Hacker技术,体验原汁原味的mini DEFCON黑客大会,满满的干货帮你开启Hacker技术大门     &g ...