Last active
August 29, 2015 13:57
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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