Skip to content

Instantly share code, notes, and snippets.

@justingarrick
Last active August 29, 2015 13:57
Show Gist options
  • Save justingarrick/9415166 to your computer and use it in GitHub Desktop.
Save justingarrick/9415166 to your computer and use it in GitHub Desktop.
Fix Fatal Signal 11 when Google Analytics/Picasso is used with Retrofit/OkHttp
Create a method like the following:
/**
* Fix https://github.com/square/okhttp/issues/184
*
* @return an unfucked OkHttpClient for use with a RestAdapter
*/
public static OkClient createClient() {
OkHttpClient client = new OkHttpClient();
SSLContext sslContext;
try {
sslContext = SSLContext.getInstance("TLS"); //$NON-NLS-1$
sslContext.init(null, null, null);
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}
client.setSslSocketFactory(sslContext.getSocketFactory());
return new OkClient(client);
}
When you create your Retrofit RestAdapter, pass this client to the Builder, e.g.
RestAdapter.Builder()
.setLogLevel(LogLevel.FULL)
.setServer(Constants.ENDPOINT)
.setConverter(converter)
.setClient(createClient())
.build();
You must do this for all tools that use OkHttp for transport, e.g. Picasso, Retrofit, etc. Here's an example using Picasso from https://gist.github.com/orip/6061163:
package com.example;
import android.content.Context;
import com.squareup.okhttp.HttpResponseCache;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.picasso.OkHttpDownloader;
import com.squareup.picasso.Picasso;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.GeneralSecurityException;
public class PicassoUtils {
private static Picasso singleton = null;
public static Picasso with(Context context) {
// Mimicking Picasso's new OkHttpLoader(context), but with our custom OkHttpClient
if (singleton == null) {
OkHttpClient client = createClient();
try {
client.setResponseCache(createResponseCache(context));
} catch (IOException ignored) {
}
singleton = new Picasso.Builder(context).downloader(new OkHttpDownloader(client)).build();
}
return singleton;
}
private static OkHttpClient createClient() {
OkHttpClient client = new OkHttpClient();
// Working around the libssl crash: https://github.com/square/okhttp/issues/184
SSLContext sslContext;
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}
client.setSslSocketFactory(sslContext.getSocketFactory());
return client;
}
private static File createDefaultCacheDir(Context context) {
try {
final Class<?> clazz = Class.forName("com.squareup.picasso.Utils");
final Method method = clazz.getDeclaredMethod("createDefaultCacheDir", Context.class);
method.setAccessible(true);
return (File)method.invoke(null, context);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e); // shouldn't happen
} catch (NoSuchMethodException e) {
throw new RuntimeException(e); // shouldn't happen
} catch (InvocationTargetException e) {
throw new RuntimeException(e); // shouldn't happen
} catch (IllegalAccessException e) {
throw new RuntimeException(e); // shouldn't happen
}
}
private static long calculateDiskCacheSize(File dir) {
try {
final Class<?> clazz = Class.forName("com.squareup.picasso.Utils");
final Method method = clazz.getDeclaredMethod("calculateDiskCacheSize", File.class);
method.setAccessible(true);
return (Long)method.invoke(null, dir);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e); // shouldn't happen
} catch (NoSuchMethodException e) {
throw new RuntimeException(e); // shouldn't happen
} catch (InvocationTargetException e) {
throw new RuntimeException(e); // shouldn't happen
} catch (IllegalAccessException e) {
throw new RuntimeException(e); // shouldn't happen
}
}
private static HttpResponseCache createResponseCache(Context context) throws IOException {
File cacheDir = createDefaultCacheDir(context);
long maxSize = calculateDiskCacheSize(cacheDir);
return new HttpResponseCache(cacheDir, maxSize);
}
}
Note that if you use the utility class above, you will need something like the following in your proguard-project.txt
file to prevent a NoSuchMethodException at runtime during the reflective lookup in createDefaultCacheDir. This is
because the recommended Proguard config for Picasso is to not warn and obfuscate the entire library:
# java.lang.NoSuchMethodException: createDefaultCacheDir [class android.content.Context] from PicassoUtils
-keep class com.squareup.picasso.Utils { *; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment