Picasso Okhttp3 cache optimization

Picasso itself does not "implement" the local cache function, but lets the network request layer cache the http response. Its network request logic corresponds to the implementation of DownLoader interface in Picasso.

  • Configure gradle
    compile 'com.squareup.okhttp3:okhttp:3.9.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'
    compile 'com.squareup.picasso:picasso:2.5.2'
  • Define DownLoader for OKhttp3.0 +
import android.net.Uri;

import com.squareup.picasso.Downloader;
import com.squareup.picasso.NetworkPolicy;

import java.io.IOException;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.ResponseBody;

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)
            {
            }
        }
    }
}
  • Set interceptor

Add cache control such as cache control to the response header of the server:
Max age = 36003600 means that the effective time of cache is one hour, that is, if the same url is requested again within one hour, the network will not be accessed, and the cache will be read when there is a network or not. At this time, if you want to request the latest content, you should use the noCache of CacheControl to realize the request construction

We can use okhttp Interceptor to modify the content of response header locally, and even judge wifi,4g,3g in it to implement different caching strategies.

import android.content.Context;
import android.util.Log;

import com.gaoyy.mishop.util.NetworkUtils;

import java.io.IOException;

import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by gaoyy on 2017/12/18 0018.
 */

public class CacheInterceptor implements Interceptor
{

    private Context context;

    public CacheInterceptor(Context context)
    {
        this.context = context;
    }

    @Override
    public Response intercept(Chain chain) throws IOException
    {
        Log.d("tag", "Intercept respone");

        Request request = chain.request();
        //If there is no network, enable force? Cache
        if (!NetworkUtils.isNetworkConnected(context))
        {
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();
        }

        Response originalResponse = chain.proceed(request);
        if (NetworkUtils.isNetworkConnected(context))
        {
            //Read the configuration in @ Headers on the interface when there is a network
            Log.d("tag", "code " + originalResponse.code());
            String cacheControl = request.cacheControl().toString();
            return originalResponse.newBuilder()
                    .header("Cache-Control", cacheControl)
                    .removeHeader("Pragma")
                    .build();
        }
        else
        {
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=3600")
                    .removeHeader("Pragma")
                    .build();
        }
    }
}
  • Configure in Application

    private void initPicasso()
    {
        File file = new File(this.getCacheDir(),"Picasso");
        OkHttpClient client =new OkHttpClient
                .Builder()
                .addInterceptor(new CacheInterceptor(this))
                .cache(new Cache(file, 1024 * 1024 * 100))
                .build();
    
        Picasso picasso = new Picasso.Builder(this)
                .defaultBitmapConfig(Bitmap.Config.RGB_565)
                .downloader(new ImageDownLoader(client))
                .indicatorsEnabled(true)
                .build();
        Picasso.setSingletonInstance(picasso);
    }

    You can also use Retrofit

    public static void init(Context context)
    {
        // Specify cache path, cache size100Mb
        Cache cache = new Cache(new File(context.getCacheDir(), "HttpCache"),
                1024 * 1024 * 100);
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .cache(cache)
                .retryOnConnectionFailure(true)
                .connectTimeout(1500000, TimeUnit.MILLISECONDS)
                .writeTimeout(2000000, TimeUnit.MILLISECONDS)
                .readTimeout(2000000, TimeUnit.MILLISECONDS)
                .addInterceptor(new CacheInterceptor(context))
                .addNetworkInterceptor(new CacheInterceptor(context))
                .build();
    
    
        Retrofit apiRetrofit = new Retrofit.Builder()
                .baseUrl(Constant.API_BASE)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    
        sApiService = apiRetrofit.create(Api.class);
    
    }

reference resources:
http://www.jianshu.com/p/6241950f9daf
http://www.jianshu.com/p/093ca3c1447d

Tags: network Android Retrofit OkHttp

Posted on Tue, 05 May 2020 13:56:36 -0400 by rob.weaver