37

I have tried setting a custom OkHttpClient with a custom Authenticator, however as the doc says: "Responds to authentication challenges from the remote web or proxy server." I have to make 2 requests for each image, and that is not ideal.

Is there a request interceptor like Retrofit does? Or am I missing something in the OkHttpClient?

I'm using the latest versions:

compile 'com.squareup.picasso:picasso:2.3.2'
compile 'com.squareup.okhttp:okhttp:2.0.+'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.+'
compile 'com.squareup.okio:okio:1.0.0'

Thanks!

aromero
  • 25,681
  • 6
  • 57
  • 79
gonzalomelov
  • 971
  • 1
  • 13
  • 30

5 Answers5

97

Since Picasso 2.5.0 OkHttpDownloader class has been changed, assuming you are using OkHttp3 (and so picasso2-okhttp3-downloader), so you have to do something like this:

OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request newRequest = chain.request().newBuilder()
                        .addHeader("X-TOKEN", "VAL")
                        .build();
                return chain.proceed(newRequest);
            }
        })
        .build();

Picasso picasso = new Picasso.Builder(context)
        .downloader(new OkHttp3Downloader(client))
        .build();

Source: https://github.com/square/picasso/issues/900

bryant1410
  • 5,540
  • 4
  • 39
  • 40
  • 2
    In first line you have missed 'new' before 'OkHttpClient();' – Mieszko Sep 03 '15 at 10:20
  • 2
    Note that the newest okhttpclient version is at 3 and not compatible with picasso 2. If you want to use this solution you will need okhttpclient 2. – user1354603 Apr 07 '16 at 11:37
  • 2
    `networkInterceptors` is an immutable list in OkHttp3, you can't add to it directly, you need to use a builder. `OkHttpClient.Builder builder = new OkHttpClient().newBuilder(); builder.networkInterceptors().add(...) ; client = builder.build()`. – pwightman Apr 14 '16 at 20:34
  • 3
    You can get the OkHttp3Downloader from https://github.com/JakeWharton/picasso2-okhttp3-downloader – darwin Apr 22 '16 at 06:46
  • 6
    Also just as a note, when you use the new picasso instance, don't use .with(context), as this will undo the custom interceptor. – Donavan White May 17 '16 at 18:15
  • lets say i want to add the headers to only the image web request not others. how do i do that?? because the way you suggested above will add the header to all the request going through the okhttp client. and there might be json request passing through it. please do suggest. – Sagar Nayak Jan 16 '18 at 05:24
  • @Sagar Nayak you should then use different okhttp clients depending on the type of request, one adding the auth headers and the other one without. – bryant1410 Jan 16 '18 at 11:39
  • Any idea how to do this with Xamarin? I don't see the same properties / methods available. – Justin Aug 01 '18 at 17:54
9

See bryant1410's answer for a more up-to-date solution.


Something like this does the job for setting an API-key header:

public class CustomPicasso {

    private static Picasso sPicasso;

    private CustomPicasso() {
    }

    public static Picasso getImageLoader(final Context context) {
        if (sPicasso == null) {
            Picasso.Builder builder = new Picasso.Builder(context);
            builder.downloader(new CustomOkHttpDownloader());
            sPicasso = builder.build();
        }
        return sPicasso;
    }

    private static class CustomOkHttpDownloader extends OkHttpDownloader {

        @Override
        protected HttpURLConnection openConnection(final Uri uri) throws IOException {
            HttpURLConnection connection = super.openConnection(uri);
            connection.setRequestProperty(Constants.HEADER_X_API_KEY, "MY_API_KEY");
            return connection;
        }
    }
}
Community
  • 1
  • 1
nhaarman
  • 98,571
  • 55
  • 246
  • 278
  • Will try tomorrow!! Thanks, it looks a pretty neat solution! – gonzalomelov Jun 18 '14 at 23:34
  • Are you sure this is working? I get NoSuchMethodError: No static method source(Ljava/io/File;)Lokio/Source; in class Lokio/Okio; or its super classes (declaration of 'okio.Okio' appears in /system/framework/okhttp.jar) when trying this. – David Corsalini Jul 14 '14 at 13:42
  • 1
    Sorry for the late response but your solution worked great! Thanks! – gonzalomelov Sep 01 '14 at 18:24
  • doesn't work because the method _openConnection_ cannot be overriden anymore... :/ – martyglaubitz Sep 03 '15 at 16:31
  • @martyonair Which is why I specifically mentioned [bryant1410's answer](http://stackoverflow.com/questions/24273783/android-picasso-library-how-to-add-authentication-headers/24294324#). – nhaarman Sep 03 '15 at 16:36
4

You can also add Authentication as suggested in the documentation of OkHttp

Just add this client

final OkHttpClient client = new OkHttpClient.Builder()
                .authenticator(new Authenticator() {
                    @Override
                    public Request authenticate(Route route, Response response) throws IOException {
                        String credential = okhttp3.Credentials.basic("user", "pw");
                        return response.request().newBuilder()
                                .header("Authorization", credential)
                                .build();
                    }
                })
                .build();

to Picasso like this:

final Picasso picasso = new Picasso.Builder(this)
                .downloader(new OkHttp3Downloader(client))
                .build();
Picasso.setSingletonInstance(picasso);

The only dependency needed is:

compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.0.2'
f4b
  • 868
  • 1
  • 8
  • 10
  • This one works, but only problem which I notice on server logs is that when I request to download an image, first tries without authentication header and after the server return 401 it repeats the call with my authenticator. On app side logs all looks good. When I switched to Interceptor instead of Authenticator, I don't see 401 on server logs anymore. – Galeen Nov 12 '18 at 14:25
4

It's working

        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                           .authenticator(new Authenticator()
                           {
                               @Override
                               public Request authenticate(Route route, Response response) throws IOException
                               {
                                   String credential =  Credentials.basic("username","password");
                                   return response.request().newBuilder()
                                           .header("Authorization", credential)
                                           .build();
                               }
                           }).build();

                   Picasso picasso = new Picasso.Builder(OnDemandImageCaptureActivity.this)
                           .downloader(new OkHttp3Downloader(okHttpClient))
                           .build();
                        picasso.load("http://example.com/abc.jpeg").into(ivcamera);

dependency:

compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
0

A simple way like this will keep default OkHttpClient timeout and cache configurations:

private class MyOkHttpDownloader extends OkHttpDownloader {

    public MyOkHttpDownloader(final Context context) {
        super(context);
        getClient().interceptors().add(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request newRequest = chain.request().newBuilder()
                        .addHeader("X-TOKEN", "VAL")
                        .build();
                return chain.proceed(newRequest);
            }
        });
    }
}

Picasso picasso = new Picasso.Builder(context).downloader(new MyOkHttpDownloader(context)).build();
xuxu
  • 6,374
  • 1
  • 17
  • 11