Picasso加载网络图片失败,提示decodestream时返回null

来源:互联网 发布:saa7130 tv card淘宝 编辑:程序博客网 时间:2024/06/05 09:11

最近遇到一个问题,项目用的图片加载框架是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;    }  }
View Code

并定位到这个地方,进行单步调试,发现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);//左上角标出颜色,红色为从网络获取,绿色为从内存中获取,蓝色为从硬盘中获取
View Code
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) {            }        }    }}
View Code
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();        }    }}
View Code

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

 

 

Tips:写出来容易。。。改这个问题也花了我不少时间~~~咱们程序员还是需要多研究、多分享啊~~~

 

 

0 0
原创粉丝点击