1 package org.robolectric.internal; 2 3 import android.annotation.SuppressLint; 4 import java.net.URL; 5 import java.util.LinkedHashMap; 6 import java.util.Map; 7 import java.util.Objects; 8 import javax.annotation.Nonnull; 9 import org.robolectric.internal.bytecode.InstrumentationConfiguration; 10 import org.robolectric.internal.bytecode.SandboxClassLoader; 11 import org.robolectric.internal.dependency.DependencyResolver; 12 13 @SuppressLint("NewApi") 14 public class SandboxFactory { 15 public static final SandboxFactory INSTANCE = new SandboxFactory(); 16 17 /** The factor for cache size. See {@link #CACHE_SIZE} for details. */ 18 private static final int CACHE_SIZE_FACTOR = 3; 19 20 /** We need to set the cache size of class loaders more than the number of supported APIs as different tests may have different configurations. */ 21 private static final int CACHE_SIZE = SdkConfig.getSupportedApis().size() * CACHE_SIZE_FACTOR; 22 23 // Simple LRU Cache. SdkEnvironments are unique across InstrumentationConfiguration and SdkConfig 24 private final LinkedHashMap<SandboxKey, SdkEnvironment> sdkToEnvironment = new LinkedHashMap<SandboxKey, SdkEnvironment>() { 25 @Override 26 protected boolean removeEldestEntry(Map.Entry<SandboxKey, SdkEnvironment> eldest) { 27 return size() > CACHE_SIZE; 28 } 29 }; 30 getSdkEnvironment( InstrumentationConfiguration instrumentationConfig, SdkConfig sdkConfig, boolean useLegacyResources, DependencyResolver dependencyResolver)31 public synchronized SdkEnvironment getSdkEnvironment( 32 InstrumentationConfiguration instrumentationConfig, SdkConfig sdkConfig, 33 boolean useLegacyResources, DependencyResolver dependencyResolver) { 34 SandboxKey key = new SandboxKey(sdkConfig, instrumentationConfig, useLegacyResources); 35 36 SdkEnvironment sdkEnvironment = sdkToEnvironment.get(key); 37 if (sdkEnvironment == null) { 38 URL[] urls = dependencyResolver.getLocalArtifactUrls(sdkConfig.getAndroidSdkDependency()); 39 40 ClassLoader robolectricClassLoader = createClassLoader(instrumentationConfig, urls); 41 sdkEnvironment = createSdkEnvironment(sdkConfig, robolectricClassLoader); 42 43 sdkToEnvironment.put(key, sdkEnvironment); 44 } 45 return sdkEnvironment; 46 } 47 createSdkEnvironment(SdkConfig sdkConfig, ClassLoader robolectricClassLoader)48 protected SdkEnvironment createSdkEnvironment(SdkConfig sdkConfig, 49 ClassLoader robolectricClassLoader) { 50 return new SdkEnvironment(sdkConfig, robolectricClassLoader); 51 } 52 53 @Nonnull createClassLoader(InstrumentationConfiguration instrumentationConfig, URL... urls)54 public ClassLoader createClassLoader(InstrumentationConfiguration instrumentationConfig, URL... urls) { 55 return new SandboxClassLoader(ClassLoader.getSystemClassLoader(), instrumentationConfig, urls); 56 } 57 58 static class SandboxKey { 59 private final SdkConfig sdkConfig; 60 private final InstrumentationConfiguration instrumentationConfiguration; 61 private final boolean useLegacyResources; 62 SandboxKey(SdkConfig sdkConfig, InstrumentationConfiguration instrumentationConfiguration, boolean useLegacyResources)63 public SandboxKey(SdkConfig sdkConfig, 64 InstrumentationConfiguration instrumentationConfiguration, boolean useLegacyResources) { 65 this.sdkConfig = sdkConfig; 66 this.instrumentationConfiguration = instrumentationConfiguration; 67 this.useLegacyResources = useLegacyResources; 68 } 69 70 @Override equals(Object o)71 public boolean equals(Object o) { 72 if (this == o) { 73 return true; 74 } 75 if (o == null || getClass() != o.getClass()) { 76 return false; 77 } 78 SandboxKey that = (SandboxKey) o; 79 return useLegacyResources == that.useLegacyResources 80 && Objects.equals(sdkConfig, that.sdkConfig) 81 && Objects.equals(instrumentationConfiguration, that.instrumentationConfiguration); 82 } 83 84 @Override hashCode()85 public int hashCode() { 86 87 return Objects.hash(sdkConfig, instrumentationConfiguration, useLegacyResources); 88 } 89 } 90 } 91