Android library for rendering Adobe After Effects animations exported as JSON through Bodymovin with native performance and extensive customization options.
—
Global configuration, render mode settings, performance optimization options, and monitoring tools for Lottie Android applications.
System for configuring global Lottie settings including network fetching, caching, and debugging options.
/**
* Global configuration class
*/
public class Lottie {
/**
* Initialize Lottie with global configuration
* @param config Configuration instance
*/
public static void initialize(LottieConfig config);
}
/**
* Configuration builder for global Lottie settings
*/
public class LottieConfig {
// Internal configuration
final LottieNetworkFetcher networkFetcher;
final LottieNetworkCacheProvider cacheProvider;
final boolean enableSystraceMarkers;
// Private constructor - use Builder
private LottieConfig(LottieNetworkFetcher networkFetcher,
LottieNetworkCacheProvider cacheProvider,
boolean enableSystraceMarkers);
/**
* Builder for LottieConfig
*/
public static final class Builder {
// Network configuration
public Builder setNetworkFetcher(LottieNetworkFetcher fetcher);
public Builder setNetworkCacheDir(File file);
public Builder setNetworkCacheProvider(LottieNetworkCacheProvider fileCacheProvider);
// Performance configuration
public Builder setEnableSystraceMarkers(boolean enable);
// Build configuration
public LottieConfig build();
}
}Usage Examples:
// Basic global configuration
LottieConfig config = new LottieConfig.Builder()
.setNetworkCacheDir(new File(getCacheDir(), "lottie_animations"))
.setEnableSystraceMarkers(BuildConfig.DEBUG)
.build();
Lottie.initialize(config);
// Custom network fetcher configuration
LottieConfig config = new LottieConfig.Builder()
.setNetworkFetcher(new CustomNetworkFetcher())
.setNetworkCacheDir(new File(getExternalCacheDir(), "lottie_cache"))
.build();
Lottie.initialize(config);
// Production configuration
LottieConfig prodConfig = new LottieConfig.Builder()
.setNetworkCacheProvider(() -> new File(getCacheDir(), "lottie_network_cache"))
.setEnableSystraceMarkers(false) // Disable in production
.build();
Lottie.initialize(prodConfig);
// Development configuration with debugging
LottieConfig devConfig = new LottieConfig.Builder()
.setNetworkFetcher(new LoggingNetworkFetcher())
.setNetworkCacheDir(new File(getCacheDir(), "dev_lottie_cache"))
.setEnableSystraceMarkers(true) // Enable for performance analysis
.build();
Lottie.initialize(devConfig);System for configuring rendering performance by choosing between hardware acceleration, software rendering, or automatic selection.
/**
* Rendering mode configuration
*/
public enum RenderMode {
AUTOMATIC, // Automatic selection based on animation characteristics
HARDWARE, // Force hardware acceleration
SOFTWARE; // Force software rendering
/**
* Determine if software rendering should be used
* @param sdkInt Android SDK version
* @param hasDashPattern Whether animation has dash patterns
* @param numMasksAndMattes Number of masks and mattes
* @return true if software rendering should be used
*/
public boolean useSoftwareRendering(int sdkInt, boolean hasDashPattern, int numMasksAndMattes);
}Usage Examples:
// Set render mode on view
animationView.setRenderMode(RenderMode.HARDWARE); // Force hardware acceleration
drawable.setRenderMode(RenderMode.SOFTWARE); // Force software rendering
// Automatic mode (default) - chooses based on animation characteristics
animationView.setRenderMode(RenderMode.AUTOMATIC);
// Check current render mode
RenderMode currentMode = animationView.getRenderMode();
Log.d("Lottie", "Current render mode: " + currentMode);
// Dynamic render mode selection based on device capabilities
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// Newer devices handle hardware acceleration better
animationView.setRenderMode(RenderMode.HARDWARE);
} else {
// Older devices may perform better with software rendering
animationView.setRenderMode(RenderMode.SOFTWARE);
}
// Conditional render mode based on animation complexity
LottieCompositionFactory.fromAsset(context, "complex_animation.json")
.addListener(composition -> {
animationView.setComposition(composition);
// Choose render mode based on complexity
if (composition.getMaskAndMatteCount() > 5 || composition.hasDashPattern()) {
animationView.setRenderMode(RenderMode.SOFTWARE);
} else {
animationView.setRenderMode(RenderMode.HARDWARE);
}
animationView.playAnimation();
});
// Custom render mode logic
public class SmartRenderModeHelper {
public static RenderMode selectOptimalRenderMode(LottieComposition composition) {
int sdkInt = Build.VERSION.SDK_INT;
boolean hasDashPattern = composition.hasDashPattern();
int masksAndMattes = composition.getMaskAndMatteCount();
// Use RenderMode's built-in logic
if (RenderMode.AUTOMATIC.useSoftwareRendering(sdkInt, hasDashPattern, masksAndMattes)) {
return RenderMode.SOFTWARE;
} else {
return RenderMode.HARDWARE;
}
}
}Tools for monitoring and analyzing animation performance, including render time tracking and performance metrics.
/**
* Performance tracking utility
*/
public class PerformanceTracker {
// Enable/disable tracking
public void setEnabled(boolean enabled);
public boolean isEnabled();
// Record performance data
public void recordRenderTime(String layerName, float renderTimeMs);
// Retrieve performance data
public Map<String, Float> getSortedRenderTimes();
public void clearRenderTimes();
// Listener for performance events
public void addFrameListener(FrameListener frameListener);
public void removeFrameListener(FrameListener frameListener);
// Internal methods
@RestrictTo(RestrictTo.Scope.LIBRARY)
public void startTimer(String layerName);
@RestrictTo(RestrictTo.Scope.LIBRARY)
public float endTimer(String layerName);
}
/**
* Frame performance listener
*/
public interface FrameListener {
void onFrameRendered(float renderTimeMs);
}Usage Examples:
// Enable performance tracking
PerformanceTracker tracker = animationView.getPerformanceTracker();
tracker.setEnabled(true);
// Add frame listener for real-time monitoring
tracker.addFrameListener(renderTimeMs -> {
if (renderTimeMs > 16.67f) { // Slower than 60fps
Log.w("Lottie", "Slow frame detected: " + renderTimeMs + "ms");
}
});
// Analyze performance after animation
animationView.addAnimatorListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
Map<String, Float> renderTimes = tracker.getSortedRenderTimes();
float totalTime = 0f;
for (Map.Entry<String, Float> entry : renderTimes.entrySet()) {
String layerName = entry.getKey();
Float renderTime = entry.getValue();
totalTime += renderTime;
Log.d("Lottie", "Layer '" + layerName + "': " + renderTime + "ms");
}
Log.d("Lottie", "Total render time: " + totalTime + "ms");
// Clear for next analysis
tracker.clearRenderTimes();
}
});
// Performance monitoring helper
public class LottiePerformanceMonitor {
private static final float TARGET_FRAME_TIME = 16.67f; // 60fps
private final PerformanceTracker tracker;
private final List<Float> frameTimes = new ArrayList<>();
public LottiePerformanceMonitor(LottieAnimationView animationView) {
this.tracker = animationView.getPerformanceTracker();
this.tracker.setEnabled(true);
this.tracker.addFrameListener(frameTime -> {
frameTimes.add(frameTime);
// Alert on consistently slow frames
if (frameTimes.size() >= 10) {
float averageTime = calculateAverage(frameTimes.subList(frameTimes.size() - 10, frameTimes.size()));
if (averageTime > TARGET_FRAME_TIME * 1.5f) {
Log.w("Lottie", "Performance warning: Average frame time " + averageTime + "ms");
}
}
});
}
public PerformanceReport generateReport() {
if (frameTimes.isEmpty()) {
return new PerformanceReport();
}
float minTime = Collections.min(frameTimes);
float maxTime = Collections.max(frameTimes);
float avgTime = calculateAverage(frameTimes);
int droppedFrames = (int) frameTimes.stream()
.mapToDouble(Float::doubleValue)
.filter(time -> time > TARGET_FRAME_TIME)
.count();
return new PerformanceReport(minTime, maxTime, avgTime, droppedFrames, frameTimes.size());
}
private float calculateAverage(List<Float> times) {
return (float) times.stream().mapToDouble(Float::doubleValue).average().orElse(0.0);
}
public static class PerformanceReport {
public final float minFrameTime;
public final float maxFrameTime;
public final float avgFrameTime;
public final int droppedFrames;
public final int totalFrames;
public PerformanceReport() {
this(0, 0, 0, 0, 0);
}
public PerformanceReport(float minFrameTime, float maxFrameTime, float avgFrameTime,
int droppedFrames, int totalFrames) {
this.minFrameTime = minFrameTime;
this.maxFrameTime = maxFrameTime;
this.avgFrameTime = avgFrameTime;
this.droppedFrames = droppedFrames;
this.totalFrames = totalFrames;
}
public float getFrameRate() {
return avgFrameTime > 0 ? 1000f / avgFrameTime : 0f;
}
public float getDroppedFramePercentage() {
return totalFrames > 0 ? (droppedFrames * 100f) / totalFrames : 0f;
}
}
}Various settings for optimizing animation performance and behavior.
// Animation view optimization methods
public class LottieAnimationView extends AppCompatImageView {
// Merge paths optimization (API 19+)
public void enableMergePathsForKitKatAndAbove(boolean enable);
// Opacity optimization
public void setApplyingOpacityToLayersEnabled(boolean isApplyingOpacityToLayersEnabled);
// Clipping optimization
public void setClipToCompositionBounds(boolean clipToCompositionBounds);
// System animation handling
public void setIgnoreDisabledSystemAnimations(boolean ignore);
}
// Drawable optimization methods
public class LottieDrawable extends Drawable {
// Merge paths optimization
public void enableMergePathsForKitKatAndAbove(boolean enable);
// Opacity optimization
public void setApplyingOpacityToLayersEnabled(boolean isApplyingOpacityToLayersEnabled);
// Clipping optimization
public void setClipToCompositionBounds(boolean clipToCompositionBounds);
// System animation handling
public void setSystemAnimationsAreEnabled(boolean areSystemAnimationsEnabled);
public void setIgnoreDisabledSystemAnimations(boolean ignore);
// Safe mode for problematic animations
public void setSafeMode(boolean safeMode);
// Performance tracking
public void setPerformanceTrackingEnabled(boolean enabled);
}Usage Examples:
// Enable optimizations
animationView.enableMergePathsForKitKatAndAbove(true);
animationView.setApplyingOpacityToLayersEnabled(true);
animationView.setClipToCompositionBounds(true);
// Optimize for older devices
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
animationView.setRenderMode(RenderMode.SOFTWARE);
animationView.enableMergePathsForKitKatAndAbove(false);
}
// Handle system animation settings
animationView.setIgnoreDisabledSystemAnimations(false); // Respect user's animation settings
// Safe mode for problematic animations
drawable.setSafeMode(true); // Disable some optimizations for stability
// Performance-optimized setup
public void setupOptimizedLottieView(LottieAnimationView animationView, boolean isLowEndDevice) {
if (isLowEndDevice) {
// Conservative settings for low-end devices
animationView.setRenderMode(RenderMode.SOFTWARE);
animationView.enableMergePathsForKitKatAndAbove(false);
animationView.setApplyingOpacityToLayersEnabled(false);
} else {
// Aggressive optimizations for high-end devices
animationView.setRenderMode(RenderMode.HARDWARE);
animationView.enableMergePathsForKitKatAndAbove(true);
animationView.setApplyingOpacityToLayersEnabled(true);
animationView.setClipToCompositionBounds(true);
}
// Common optimizations
animationView.setIgnoreDisabledSystemAnimations(false);
}
// Memory optimization helper
public class LottieMemoryManager {
private static final int MAX_CACHE_SIZE = 20;
public static void optimizeForMemory() {
// Limit composition cache size
LottieCompositionFactory.setMaxCacheSize(MAX_CACHE_SIZE);
}
public static void clearAllCaches(Context context) {
// Clear composition cache
LottieCompositionFactory.clearCache(context);
// Clear network cache
File cacheDir = new File(context.getCacheDir(), "lottie_network_cache");
if (cacheDir.exists()) {
deleteRecursively(cacheDir);
}
}
private static void deleteRecursively(File file) {
if (file.isDirectory()) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
deleteRecursively(child);
}
}
}
file.delete();
}
}Configuration for network-based animation loading with custom fetchers and cache providers.
/**
* Interface for custom network fetching
*/
public interface LottieNetworkFetcher {
LottieResult<LottieComposition> fetchSync(String url, String cacheKey);
}
/**
* Interface for custom cache providers
*/
public interface LottieNetworkCacheProvider {
File getCacheDir();
}
/**
* Network fetch result interface
*/
public interface LottieFetchResult {
boolean isSuccessful();
InputStream bodyByteStream();
String contentType();
}
/**
* Default network fetcher implementation
*/
public class DefaultLottieNetworkFetcher implements LottieNetworkFetcher {
public DefaultLottieNetworkFetcher();
public LottieResult<LottieComposition> fetchSync(String url, String cacheKey);
}
/**
* Default fetch result implementation
*/
public class DefaultLottieFetchResult implements LottieFetchResult {
public DefaultLottieFetchResult(HttpURLConnection connection);
public boolean isSuccessful();
public InputStream bodyByteStream();
public String contentType();
}Usage Examples:
// Custom network fetcher with authentication
public class AuthenticatedNetworkFetcher implements LottieNetworkFetcher {
private final String authToken;
public AuthenticatedNetworkFetcher(String authToken) {
this.authToken = authToken;
}
@Override
public LottieResult<LottieComposition> fetchSync(String url, String cacheKey) {
try {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestProperty("Authorization", "Bearer " + authToken);
connection.setRequestMethod("GET");
connection.connect();
DefaultLottieFetchResult fetchResult = new DefaultLottieFetchResult(connection);
if (fetchResult.isSuccessful()) {
InputStream inputStream = fetchResult.bodyByteStream();
return LottieCompositionFactory.fromJsonInputStreamSync(inputStream, cacheKey);
} else {
return new LottieResult<>(new IOException("HTTP " + connection.getResponseCode()));
}
} catch (IOException e) {
return new LottieResult<>(e);
}
}
}
// Custom cache provider
public class CustomCacheProvider implements LottieNetworkCacheProvider {
private final File customCacheDir;
public CustomCacheProvider(Context context) {
this.customCacheDir = new File(context.getExternalCacheDir(), "custom_lottie_cache");
if (!customCacheDir.exists()) {
customCacheDir.mkdirs();
}
}
@Override
public File getCacheDir() {
return customCacheDir;
}
}
// Configure custom network handling
LottieConfig config = new LottieConfig.Builder()
.setNetworkFetcher(new AuthenticatedNetworkFetcher(userToken))
.setNetworkCacheProvider(new CustomCacheProvider(context))
.build();
Lottie.initialize(config);public interface LottieLogger {
void debug(String message);
void debug(String message, Throwable exception);
void warning(String message);
void warning(String message, Throwable exception);
void error(String message, Throwable exception);
}
public class LogcatLogger implements LottieLogger {
public static final LogcatLogger INSTANCE = new LogcatLogger();
@Override
public void debug(String message);
@Override
public void debug(String message, Throwable exception);
@Override
public void warning(String message);
@Override
public void warning(String message, Throwable exception);
@Override
public void error(String message, Throwable exception);
}Install with Tessl CLI
npx tessl i tessl/maven-com-airbnb-android--lottie