Android Best Practices
Performance Optimization
1. Choose the Right Processing Mode
Video Mode (VIDEO)
- Suitable for real-time video streams and live streaming scenarios
- Better performance and faster processing speed
- Recommended for camera preview, video calls, and similar scenarios
Image Mode (IMAGE)
- Suitable for single image processing
- Higher quality and better effects
- Recommended for photo editing and image beautification scenarios
java
// Real-time video processing
input.type = ImageFrame.FrameType.VIDEO;
ImageFrame output = engine.processImage(input);
// High-quality image processing
input.type = ImageFrame.FrameType.IMAGE;
ImageFrame output = engine.processImage(input);2. Parameter Adjustment Recommendations
Beauty Parameter Adjustment Principles
- Start with smaller values to avoid over-beautification
- Recommend reducing parameter values in real-time scenarios for smooth performance
- Static images can have appropriately higher parameter values
- Adjust parameter ranges based on user groups and scenarios
Recommended Parameter Ranges
java
// Basic beauty parameters (real-time scenarios)
mBeautyEngine.setBeautyParam(BasicParam.SMOOTHING, 0.2f); // Smoothing
mBeautyEngine.setBeautyParam(BasicParam.WHITENING, 0.1f); // Whitening
mBeautyEngine.setBeautyParam(BasicParam.ROSINESS, 0.1f); // Rosiness
// Face reshaping parameters (real-time scenarios)
mBeautyEngine.setBeautyParam(ReshapeParam.FACE_THIN, 0.1f); // Face thinning
mBeautyEngine.setBeautyParam(ReshapeParam.EYE_SIZE, 0.1f); // Eye enlargement3. Memory Optimization
Use Direct Memory Buffers
java
// Recommended: Use direct memory
ByteBuffer data = ByteBuffer.allocateDirect(width * height * 4);
// Avoid: Use heap memory
ByteBuffer data = ByteBuffer.allocate(width * height * 4);Release Resources Timely
java
public class BeautyProcessor {
private ImageFrame mReusableFrame; // Reusable object
public ImageFrame processImage(byte[] imageData) {
// Reuse ImageFrame object
if (mReusableFrame == null) {
mReusableFrame = ImageFrame.createWithRGBA(data, width, height, stride);
}
// Process image
mReusableFrame.type = ImageFrame.FrameType.VIDEO;
ImageFrame output = mBeautyEngine.processImage(mReusableFrame);
return output;
}
public void release() {
if (mReusableFrame != null) {
mReusableFrame.release();
mReusableFrame = null;
}
}
}Architecture Design
1. Singleton Pattern for Engine Management
java
public class BeautyEngineManager {
private static BeautyEngineManager sInstance;
private BeautyEffectEngine mEngine;
private Context mContext;
private BeautyEngineManager(Context context) {
mContext = context.getApplicationContext();
initEngine();
}
public static synchronized BeautyEngineManager getInstance(Context context) {
if (sInstance == null) {
sInstance = new BeautyEngineManager(context);
}
return sInstance;
}
private void initEngine() {
BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig();
config.appId = "your_app_id";
config.appKey = "your_app_key";
mEngine = new BeautyEffectEngine(mContext, config);
}
public BeautyEffectEngine getEngine() {
return mEngine;
}
public void release() {
if (mEngine != null) {
mEngine.release();
mEngine = null;
}
sInstance = null;
}
}2. Asynchronous Image Processing
java
public class AsyncBeautyProcessor {
private ExecutorService mExecutor;
private BeautyEffectEngine mEngine;
public AsyncBeautyProcessor() {
mExecutor = Executors.newSingleThreadExecutor();
}
public void processImageAsync(ImageFrame input, BeautyCallback callback) {
mExecutor.execute(() -> {
try {
input.type = ImageFrame.FrameType.VIDEO;
ImageFrame output = mEngine.processImage(input);
// Switch to main thread for callback
new Handler(Looper.getMainLooper()).post(() -> {
callback.onSuccess(output);
});
} catch (Exception e) {
new Handler(Looper.getMainLooper()).post(() -> {
callback.onError(e);
});
}
});
}
public interface BeautyCallback {
void onSuccess(ImageFrame result);
void onError(Exception error);
}
}Error Handling
1. Comprehensive Error Handling Mechanism
java
public class RobustBeautyProcessor {
private BeautyEffectEngine mEngine;
public boolean processImage(ImageFrame input, ImageFrame output) {
// Parameter validation
if (mEngine == null) {
Log.e("BeautyProcessor", "Engine not initialized");
return false;
}
if (input == null || !input.isValid()) {
Log.e("BeautyProcessor", "Invalid input image");
return false;
}
try {
// Process image
ImageFrame result = mEngine.processImage(input, ProcessMode.VIDEO);
if (result == null) {
Log.e("BeautyProcessor", "Failed to process image");
return false;
}
// Copy result to output
copyImageFrame(result, output);
result.release();
return true;
} catch (Exception e) {
Log.e("BeautyProcessor", "Exception during processing", e);
return false;
}
}
private void copyImageFrame(ImageFrame src, ImageFrame dst) {
// Implement image copying logic
}
}2. Retry Mechanism
java
public class RetryBeautyProcessor {
private static final int MAX_RETRY_COUNT = 3;
private static final long RETRY_DELAY_MS = 100;
public ImageFrame processImageWithRetry(ImageFrame input) {
for (int i = 0; i < MAX_RETRY_COUNT; i++) {
try {
ImageFrame result = mBeautyEngine.processImage(input, ProcessMode.VIDEO);
if (result != null) {
return result;
}
} catch (Exception e) {
Log.w("BeautyProcessor", "Attempt " + (i + 1) + " failed", e);
}
if (i < MAX_RETRY_COUNT - 1) {
try {
Thread.sleep(RETRY_DELAY_MS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
Log.e("BeautyProcessor", "All retry attempts failed");
return null;
}
}Lifecycle Management
WARNING
The beauty engine requires manual release() to free resources. ImageFrame and other objects also need to call release() after use, otherwise it will cause memory leaks.
1. Activity Lifecycle Handling
java
public class BeautyActivity extends AppCompatActivity {
private BeautyEngineManager mBeautyManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize beauty engine
mBeautyManager = BeautyEngineManager.getInstance(this);
}
@Override
protected void onResume() {
super.onResume();
// Resume beauty processing
}
@Override
protected void onPause() {
super.onPause();
// Pause beauty processing
}
@Override
protected void onDestroy() {
super.onDestroy();
// Release beauty engine (if no longer needed)
// mBeautyManager.release();
}
}2. Fragment Lifecycle Handling
java
public class BeautyFragment extends Fragment {
private BeautyEffectEngine mBeautyEngine;
@Override
public void onAttach(Context context) {
super.onAttach(context);
// Initialize beauty engine
initBeautyEngine();
}
@Override
public void onDetach() {
super.onDetach();
// Release beauty engine
if (mBeautyEngine != null) {
mBeautyEngine.release();
mBeautyEngine = null;
}
}
}Performance Monitoring
1. Performance Metrics Monitoring
java
public class BeautyPerformanceMonitor {
private long mStartTime;
private int mFrameCount;
private long mTotalProcessTime;
public void startFrame() {
mStartTime = System.currentTimeMillis();
}
public void endFrame() {
long processTime = System.currentTimeMillis() - mStartTime;
mTotalProcessTime += processTime;
mFrameCount++;
// Calculate average processing time
if (mFrameCount % 30 == 0) { // Calculate every 30 frames
long avgTime = mTotalProcessTime / mFrameCount;
Log.d("BeautyPerformance", "Average process time: " + avgTime + "ms");
// Reset counters
mTotalProcessTime = 0;
mFrameCount = 0;
}
}
public boolean isPerformanceGood() {
return mTotalProcessTime / Math.max(mFrameCount, 1) < 33; // 30fps
}
}2. Memory Usage Monitoring
java
public class MemoryMonitor {
private Runtime mRuntime;
public MemoryMonitor() {
mRuntime = Runtime.getRuntime();
}
public void logMemoryUsage(String tag) {
long totalMemory = mRuntime.totalMemory();
long freeMemory = mRuntime.freeMemory();
long usedMemory = totalMemory - freeMemory;
long maxMemory = mRuntime.maxMemory();
Log.d("MemoryMonitor", tag + " - Used: " + (usedMemory / 1024 / 1024) + "MB, " +
"Max: " + (maxMemory / 1024 / 1024) + "MB");
}
public boolean isMemoryLow() {
long usedMemory = mRuntime.totalMemory() - mRuntime.freeMemory();
long maxMemory = mRuntime.maxMemory();
return usedMemory > maxMemory * 0.8; // Over 80% usage
}
}Configuration Management
1. Beauty Configuration Management
java
public class BeautyConfigManager {
private SharedPreferences mPrefs;
private static final String PREF_NAME = "beauty_config";
public BeautyConfigManager(Context context) {
mPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
}
public void saveBeautyConfig(BeautyConfig config) {
SharedPreferences.Editor editor = mPrefs.edit();
editor.putFloat("smoothing", config.smoothing);
editor.putFloat("whitening", config.whitening);
editor.putFloat("face_thin", config.faceThin);
editor.putBoolean("basic_enabled", config.basicEnabled);
editor.putBoolean("reshape_enabled", config.reshapeEnabled);
editor.apply();
}
public BeautyConfig loadBeautyConfig() {
BeautyConfig config = new BeautyConfig();
config.smoothing = mPrefs.getFloat("smoothing", 0.3f);
config.whitening = mPrefs.getFloat("whitening", 0.2f);
config.faceThin = mPrefs.getFloat("face_thin", 0.1f);
config.basicEnabled = mPrefs.getBoolean("basic_enabled", true);
config.reshapeEnabled = mPrefs.getBoolean("reshape_enabled", false);
return config;
}
public static class BeautyConfig {
public float smoothing = 0.3f;
public float whitening = 0.2f;
public float faceThin = 0.1f;
public boolean basicEnabled = true;
public boolean reshapeEnabled = false;
}
}Testing Recommendations
1. Unit Testing
java
@Test
public void testBeautyEngineInitialization() {
BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig();
config.appId = "test_app_id";
config.appKey = "test_app_key";
BeautyEffectEngine engine = new BeautyEffectEngine(mContext, config);
assertNotNull("Engine should be created", engine);
engine.release();
}
@Test
public void testImageProcessing() {
// Create test image
ByteBuffer data = ByteBuffer.allocateDirect(640 * 480 * 4);
ImageFrame input = ImageFrame.createWithRGBA(data, 640, 480, 640 * 4);
// Process image
ImageFrame output = mBeautyEngine.processImage(input, ProcessMode.VIDEO);
assertNotNull("Output should not be null", output);
assertTrue("Output should be valid", output.isValid());
input.release();
output.release();
}2. Performance Testing
java
@Test
public void testPerformance() {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
// Process test image
processTestImage();
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
long avgTime = totalTime / 100;
assertTrue("Average process time should be less than 50ms", avgTime < 50);
}
