Files
mkdocs/docs/android面试/项目经验/性能优化实践.md
2026-01-15 11:53:37 +08:00

14 KiB
Raw Permalink Blame History

性能优化实践

目录


启动优化实践

案例1Application 优化

问题

// ❌ 优化前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 性能优化

优化前问题

  1. 启动时间3.5s
  2. 内存占用150MB
  3. 卡顿频率:每 10s 一次
  4. 网络请求:平均 2s

优化方案

1. 启动优化
// Application 异步初始化
// 启动窗口优化
// 布局优化
2. 内存优化
// 修复内存泄漏
// 图片内存优化
// 对象池优化
3. 布局优化
// 使用 ConstraintLayout
// 使用 ViewStub
// 避免过度绘制
4. 网络优化
// 请求合并
// 请求缓存
// 图片加载优化

优化效果

  1. 启动时间1.2s(减少 65.7%
  2. 内存占用80MB减少 46.7%
  3. 卡顿频率:每 60s 一次(减少 83.3%
  4. 网络请求:平均 0.5s(减少 75%

面试常见问题

Q1: 如何优化应用启动速度?

答案:

  1. Application 优化:异步初始化、延迟初始化
  2. 启动窗口:使用启动画面
  3. 布局优化:减少布局层级、使用 ViewStub
  4. 资源优化:压缩资源、延迟加载

Q2: 如何优化内存使用?

答案:

  1. 避免内存泄漏:注意 Context、Handler、监听器的使用
  2. 图片优化:压缩图片、使用合适格式
  3. 对象池:复用对象,减少创建
  4. 及时释放:及时释放不再使用的资源

Q3: 如何优化布局性能?

答案:

  1. 减少层级:使用 ConstraintLayout
  2. 延迟加载:使用 ViewStub
  3. 避免过度绘制:移除不必要的背景
  4. 使用硬件加速:启用硬件加速

Q4: 如何优化网络性能?

答案:

  1. 请求合并:批量请求
  2. 请求缓存:三级缓存
  3. 图片优化:使用图片加载库
  4. 数据压缩Gzip 压缩

Q5: 性能优化的流程?

答案:

  1. 问题定位:使用工具定位性能问题
  2. 制定方案:制定优化方案
  3. 实施优化:实施优化措施
  4. 效果验证:验证优化效果
  5. 持续监控:持续监控性能

Q6: 性能优化的工具?

答案:

  1. 启动优化TraceView、Systrace
  2. 内存优化LeakCanary、Memory Profiler
  3. 布局优化Layout Inspector、Hierarchy Viewer
  4. 网络优化Network Profiler、Charles

总结

性能优化是一个持续的过程,需要从启动、内存、布局、网络等多个方面进行优化。通过合理的优化措施,可以显著提升应用性能,改善用户体验。


最后更新2024年