14 KiB
14 KiB
性能优化实践
目录
启动优化实践
案例1:Application 优化
问题
// ❌ 优化前:Application onCreate 耗时过长
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 同步初始化多个 SDK
initCrashHandler(); // 100ms
initAnalytics(); // 200ms
initPushService(); // 300ms
initImageLoader(); // 150ms
// 总耗时:750ms
}
}
优化方案
// ✅ 优化后:异步初始化 + 延迟初始化
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 必要初始化(立即执行)
initCrashHandler(); // 必须立即初始化
// 非必要初始化(延迟执行)
new Handler(Looper.getMainLooper()).postDelayed(() -> {
initAnalytics();
initPushService();
}, 100);
// 异步初始化
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(() -> {
initImageLoader(); // 可以异步初始化
});
}
}
优化效果
- 优化前:启动时间 750ms
- 优化后:启动时间 100ms(主线程)
- 提升:减少 650ms
案例2:启动窗口优化
问题
// ❌ 优化前:启动白屏时间长
// 没有启动窗口,用户看到白屏
优化方案
<!-- styles.xml -->
<style name="LaunchTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@drawable/launch_screen</item>
<item name="android:windowFullscreen">true</item>
</style>
// MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme); // 恢复正常主题
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
优化效果
- 优化前:白屏时间 800ms
- 优化后:显示启动画面,用户体验更好
案例3:布局优化
问题
<!-- ❌ 优化前:布局层级过深 -->
<LinearLayout>
<LinearLayout>
<LinearLayout>
<LinearLayout>
<TextView />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
优化方案
<!-- ✅ 优化后:使用 ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView />
</androidx.constraintlayout.widget.ConstraintLayout>
优化效果
- 优化前:布局加载时间 50ms
- 优化后:布局加载时间 20ms
- 提升:减少 30ms
内存优化实践
案例1:内存泄漏修复
问题
// ❌ 优化前:Handler 内存泄漏
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// Handler 持有 Activity 引用
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
// 如果 Activity 已销毁,但 Handler 还在处理消息
}
}, 10000);
}
}
优化方案
// ✅ 优化后:使用静态内部类 + WeakReference
public class MainActivity extends AppCompatActivity {
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mActivity;
MyHandler(MainActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = mActivity.get();
if (activity == null || activity.isFinishing()) {
return;
}
// 处理消息
}
}
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new MyHandler(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
优化效果
- 优化前:Activity 无法被回收,内存泄漏
- 优化后:Activity 正常回收,无内存泄漏
案例2:图片内存优化
问题
// ❌ 优化前:加载大图导致 OOM
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
imageView.setImageBitmap(bitmap);
优化方案
// ✅ 优化后:压缩图片
public Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
int halfHeight = height / 2;
int halfWidth = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
优化效果
- 优化前:加载 4MB 图片,占用 16MB 内存
- 优化后:压缩后加载,占用 2MB 内存
- 提升:减少 87.5% 内存占用
案例3:对象池优化
问题
// ❌ 优化前:频繁创建对象
for (int i = 0; i < 1000; i++) {
StringBuilder sb = new StringBuilder(); // 每次创建新对象
sb.append("item").append(i);
}
优化方案
// ✅ 优化后:使用对象池
public class StringBuilderPool {
private static final int MAX_POOL_SIZE = 10;
private Queue<StringBuilder> pool = new LinkedList<>();
public StringBuilder acquire() {
StringBuilder sb = pool.poll();
if (sb == null) {
sb = new StringBuilder();
}
return sb;
}
public void release(StringBuilder sb) {
if (pool.size() < MAX_POOL_SIZE) {
sb.setLength(0); // 清空
pool.offer(sb);
}
}
}
// 使用
StringBuilderPool pool = new StringBuilderPool();
for (int i = 0; i < 1000; i++) {
StringBuilder sb = pool.acquire();
sb.append("item").append(i);
// 使用 sb
pool.release(sb);
}
优化效果
- 优化前:创建 1000 个对象
- 优化后:复用 10 个对象
- 提升:减少 99% 对象创建
布局优化实践
案例1:减少布局层级
问题
<!-- ❌ 优化前:嵌套过深 -->
<LinearLayout>
<LinearLayout>
<LinearLayout>
<TextView />
</LinearLayout>
</LinearLayout>
</LinearLayout>
优化方案
<!-- ✅ 优化后:使用 ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView />
</androidx.constraintlayout.widget.ConstraintLayout>
优化效果
- 优化前:布局层级 4 层,测量时间 30ms
- 优化后:布局层级 2 层,测量时间 10ms
- 提升:减少 66.7% 测量时间
案例2:使用 ViewStub
问题
<!-- ❌ 优化前:一次性加载所有布局 -->
<LinearLayout>
<TextView />
<HeavyLayout /> <!-- 重量级布局,但可能不显示 -->
<Button />
</LinearLayout>
优化方案
<!-- ✅ 优化后:使用 ViewStub 延迟加载 -->
<LinearLayout>
<TextView />
<ViewStub
android:id="@+id/view_stub"
android:layout="@layout/heavy_layout" />
<Button />
</LinearLayout>
// 需要时才加载
ViewStub viewStub = findViewById(R.id.view_stub);
if (shouldShowHeavyLayout()) {
viewStub.inflate();
}
优化效果
- 优化前:初始布局加载时间 50ms
- 优化后:初始布局加载时间 20ms
- 提升:减少 60% 初始加载时间
案例3:避免过度绘制
问题
<!-- ❌ 优化前:多层背景 -->
<LinearLayout
android:background="@drawable/bg1">
<LinearLayout
android:background="@drawable/bg2">
<TextView
android:background="@drawable/bg3" />
</LinearLayout>
</LinearLayout>
优化方案
<!-- ✅ 优化后:移除不必要的背景 -->
<LinearLayout>
<TextView />
</LinearLayout>
优化效果
- 优化前:过度绘制 3 层
- 优化后:过度绘制 1 层
- 提升:减少 66.7% 绘制时间
网络优化实践
案例1:请求合并
问题
// ❌ 优化前:频繁请求
for (int i = 0; i < 100; i++) {
api.getUserInfo(i).enqueue(callback); // 100 个请求
}
优化方案
// ✅ 优化后:批量请求
List<Integer> userIds = new ArrayList<>();
for (int i = 0; i < 100; i++) {
userIds.add(i);
}
api.getBatchUserInfo(userIds).enqueue(callback); // 1 个请求
优化效果
- 优化前:100 个请求,总耗时 10s
- 优化后:1 个请求,总耗时 1s
- 提升:减少 90% 请求时间
案例2:请求缓存
问题
// ❌ 优化前:每次都请求网络
api.getUserInfo(userId).enqueue(callback);
优化方案
// ✅ 优化后:三级缓存
public class UserRepository {
private MemoryCache memoryCache;
private DiskCache diskCache;
private ApiService apiService;
public void getUserInfo(String userId, Callback callback) {
// 1. 检查内存缓存
User user = memoryCache.get(userId);
if (user != null) {
callback.onSuccess(user);
return;
}
// 2. 检查磁盘缓存
user = diskCache.get(userId);
if (user != null) {
memoryCache.put(userId, user);
callback.onSuccess(user);
return;
}
// 3. 从网络加载
apiService.getUserInfo(userId).enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
User user = response.body();
memoryCache.put(userId, user);
diskCache.put(userId, user);
callback.onSuccess(user);
}
});
}
}
优化效果
- 优化前:每次请求网络,耗时 500ms
- 优化后:命中缓存,耗时 10ms
- 提升:减少 98% 响应时间
案例3:图片加载优化
问题
// ❌ 优化前:直接加载原图
ImageView imageView = findViewById(R.id.imageView);
imageView.setImageBitmap(BitmapFactory.decodeFile(imagePath));
优化方案
// ✅ 优化后:使用 Glide 自动优化
Glide.with(context)
.load(imageUrl)
.override(200, 200) // 指定尺寸
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(imageView);
优化效果
- 优化前:加载 2MB 图片,耗时 200ms
- 优化后:加载压缩后图片,耗时 50ms
- 提升:减少 75% 加载时间
性能优化案例
案例:电商 App 性能优化
优化前问题
- 启动时间:3.5s
- 内存占用:150MB
- 卡顿频率:每 10s 一次
- 网络请求:平均 2s
优化方案
1. 启动优化
// Application 异步初始化
// 启动窗口优化
// 布局优化
2. 内存优化
// 修复内存泄漏
// 图片内存优化
// 对象池优化
3. 布局优化
// 使用 ConstraintLayout
// 使用 ViewStub
// 避免过度绘制
4. 网络优化
// 请求合并
// 请求缓存
// 图片加载优化
优化效果
- 启动时间:1.2s(减少 65.7%)
- 内存占用:80MB(减少 46.7%)
- 卡顿频率:每 60s 一次(减少 83.3%)
- 网络请求:平均 0.5s(减少 75%)
面试常见问题
Q1: 如何优化应用启动速度?
答案:
- Application 优化:异步初始化、延迟初始化
- 启动窗口:使用启动画面
- 布局优化:减少布局层级、使用 ViewStub
- 资源优化:压缩资源、延迟加载
Q2: 如何优化内存使用?
答案:
- 避免内存泄漏:注意 Context、Handler、监听器的使用
- 图片优化:压缩图片、使用合适格式
- 对象池:复用对象,减少创建
- 及时释放:及时释放不再使用的资源
Q3: 如何优化布局性能?
答案:
- 减少层级:使用 ConstraintLayout
- 延迟加载:使用 ViewStub
- 避免过度绘制:移除不必要的背景
- 使用硬件加速:启用硬件加速
Q4: 如何优化网络性能?
答案:
- 请求合并:批量请求
- 请求缓存:三级缓存
- 图片优化:使用图片加载库
- 数据压缩:Gzip 压缩
Q5: 性能优化的流程?
答案:
- 问题定位:使用工具定位性能问题
- 制定方案:制定优化方案
- 实施优化:实施优化措施
- 效果验证:验证优化效果
- 持续监控:持续监控性能
Q6: 性能优化的工具?
答案:
- 启动优化:TraceView、Systrace
- 内存优化:LeakCanary、Memory Profiler
- 布局优化:Layout Inspector、Hierarchy Viewer
- 网络优化:Network Profiler、Charles
总结
性能优化是一个持续的过程,需要从启动、内存、布局、网络等多个方面进行优化。通过合理的优化措施,可以显著提升应用性能,改善用户体验。
最后更新:2024年