架构视角:性能是设计出来的

Android性能优化不是事后的修补工作,而是贯穿整个开发周期的系统性工程。从架构设计阶段就要考虑性能因素:合理的模块划分、数据流设计、缓存策略等,都会直接影响最终的用户体验。

性能优化核心维度

  • 启动速度:冷启动、温启动、热启动的优化策略
  • 渲染性能:60fps/120fps流畅度,卡顿检测与优化
  • 内存管理:内存泄漏防治,OOM预防,内存抖动优化
  • 电量优化:后台任务管理,网络请求合并,传感器使用优化
  • 包体积:资源优化,代码精简,动态化交付

启动优化:首屏体验决定留存

启动流程分析

// 启动流程关键节点监控
class StartupTracer {
    
    companion object {
        // Application.onCreate开始
        var applicationOnCreateStart: Long = 0
        // Application.onCreate结束
        var applicationOnCreateEnd: Long = 0
        // 首帧绘制完成
        var firstFrameDrawn: Long = 0
        // 首屏内容可见
        var firstContentfulPaint: Long = 0
        
        fun printMetrics() {
            val applicationInitTime = applicationOnCreateEnd - applicationOnCreateStart
            val firstFrameTime = firstFrameDrawn - applicationOnCreateStart
            val contentfulPaintTime = firstContentfulPaint - applicationOnCreateStart
            
            Log.d("Startup", """
                启动性能指标:
                - Application初始化: ${applicationInitTime}ms
                - 首帧绘制: ${firstFrameTime}ms
                - 首屏内容: ${contentfulPaintTime}ms
            """.trimIndent())
        }
    }
}

// Application优化
class OptimizedApplication : Application() {
    
    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        StartupTracer.applicationOnCreateStart = System.currentTimeMillis()
        
        // 1. 延迟初始化:使用ContentProvider自动初始化
        // 2. 异步初始化:不阻塞主线程的初始化放到后台
        // 3. 懒加载:按需初始化
    }
    
    override fun onCreate() {
        super.onCreate()
        
        // 主线程初始化(关键路径)
        initCriticalComponents()
        
        StartupTracer.applicationOnCreateEnd = System.currentTimeMillis()
        
        // 异步初始化(非关键路径)
        GlobalScope.launch(Dispatchers.Default) {
            initNonCriticalComponents()
        }
        
        // 空闲时初始化
        Looper.myQueue().addIdleHandler {
            initIdleComponents()
            false // 只执行一次
        }
    }
    
    private fun initCriticalComponents() {
        // 只初始化启动必需组件
        CrashReporter.init(this)
        ThemeManager.init(this)
    }
    
    private fun initNonCriticalComponents() {
        // 网络、数据库、分析等可以延迟的初始化
        NetworkManager.init()
        DatabaseManager.init()
        Analytics.init()
    }
    
    private fun initIdleComponents() {
        // 预加载资源、预热WebView等
        ImageLoader.preload()
    }
}

启动优化策略

// 1. 使用Startup库管理初始化依赖
class AnalyticsInitializer : Initializer {
    override fun create(context: Context): Analytics {
        return Analytics.initialize(context)
    }
    
    override fun dependencies(): List>> {
        // 声明依赖,确保按顺序初始化
        return listOf(WorkManagerInitializer::class.java)
    }
}

// 2. 布局优化:减少层级,使用ConstraintLayout
// 优化前:嵌套LinearLayout
<LinearLayout>
    <LinearLayout>
        <TextView />
        <TextView />
    </LinearLayout>
    <LinearLayout>
        <ImageView />
        <TextView />
    </LinearLayout>
</LinearLayout>

// 优化后:扁平化ConstraintLayout
<androidx.constraintlayout.widget.ConstraintLayout>
    <TextView app:layout_constraintTop_toTopOf="parent" />
    <TextView app:layout_constraintTop_toTopOf="parent" />
    <ImageView app:layout_constraintTop_toBottomOf="@id/title" />
    <TextView app:layout_constraintTop_toBottomOf="@id/title" />
</androidx.constraintlayout.widget.ConstraintLayout>

// 3. 异步Inflate
class AsyncLayoutInflater {
    
    fun inflateAsync(
        context: Context,
        layoutRes: Int,
        parent: ViewGroup?,
        callback: (View) -> Unit
    ) {
        AsyncTask.execute {
            val inflater = LayoutInflater.from(context)
            val view = inflater.inflate(layoutRes, parent, false)
            
            Handler(Looper.getMainLooper()).post {
                callback(view)
            }
        }
    }
}

// 4. 类加载优化:使用DexClassLoader或PathClassLoader的优化版本
// 开启Dex预编译(Profile Guided Optimization)
// build.gradle:
// android {
//     buildTypes {
//         release {
//             minifyEnabled true
//             shrinkResources true
//             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
//         }
//     }
// }

启动优化检查清单

  • ✅ Application.onCreate中只初始化必要组件
  • ✅ 使用Startup库管理初始化依赖
  • ✅ 布局层级不超过3层,使用ConstraintLayout
  • ✅ 避免在onCreate中进行IPC调用
  • ✅ 使用WindowBackground避免白屏
  • ✅ 启用R8/ProGuard代码压缩
  • ✅ 使用Baseline Profiles预编译热点代码

渲染优化:流畅的UI体验

卡顿检测与分析

// 卡顿检测工具
class FrameDropDetector {
    
    private val choreographer = Choreographer.getInstance()
    private var lastFrameTime = 0L
    private val frameInterval = 16_666_666 // 60fps: 16.67ms (nanoseconds)
    
    private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
        if (lastFrameTime != 0L) {
            val diff = frameTimeNanos - lastFrameTime
            val droppedFrames = (diff / frameInterval).toInt() - 1
            
            if (droppedFrames > 1) {
                // 检测到掉帧
                Log.w("FrameDrop", "Dropped $droppedFrames frames")
                
                if (droppedFrames > 5) {
                    // 严重卡顿,记录堆栈
                    recordStackTrace()
                }
            }
        }
        lastFrameTime = frameTimeNanos
        choreographer.postFrameCallback(this)
    }
    
    fun start() {
        choreographer.postFrameCallback(frameCallback)
    }
    
    fun stop() {
        choreographer.removeFrameCallback(frameCallback)
    }
    
    private fun recordStackTrace() {
        val stackTrace = Looper.getMainLooper().thread.stackTrace
        // 上报或记录堆栈信息
    }
}

// 使用Profile GPU Rendering检测渲染瓶颈
// 开发者选项 -> GPU渲染配置文件 -> 在屏幕上显示为条形图
// 蓝色:绘制时间 | 红色:执行时间 | 橙色:处理时间 | 紫色:交换缓冲区时间

RecyclerView优化

// RecyclerView高性能实现
class OptimizedAdapter : ListAdapter(DiffCallback()) {
    
    // 1. 使用DiffUtil异步计算差异
    class DiffCallback : DiffUtil.ItemCallback() {
        override fun areItemsTheSame(old: Item, new: Item): Boolean {
            return old.id == new.id
        }
        
        override fun areContentsTheSame(old: Item, new: Item): Boolean {
            return old == new
        }
        
        // 局部更新payload
        override fun getChangePayload(old: Item, new: Item): Any? {
            return when {
                old.title != new.title -> PAYLOAD_TITLE
                old.imageUrl != new.imageUrl -> PAYLOAD_IMAGE
                else -> null
            }
        }
    }
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        // 2. 使用ViewBinding或合成扩展
        val binding = ItemLayoutBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
        return ViewHolder(binding)
    }
    
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position))
    }
    
    // 3. 局部更新
    override fun onBindViewHolder(
        holder: ViewHolder, 
        position: Int, 
        payloads: MutableList
    ) {
        if (payloads.isEmpty()) {
            super.onBindViewHolder(holder, position, payloads)
            return
        }
        
        val item = getItem(position)
        payloads.forEach { payload ->
            when (payload) {
                PAYLOAD_TITLE -> holder.updateTitle(item.title)
                PAYLOAD_IMAGE -> holder.updateImage(item.imageUrl)
            }
        }
    }
    
    class ViewHolder(private val binding: ItemLayoutBinding) : 
        RecyclerView.ViewHolder(binding.root) {
        
        // 4. 预加载图片尺寸,避免布局抖动
        fun bind(item: Item) {
            binding.title.text = item.title
            
            // 使用固定尺寸或aspect ratio
            binding.image.load(item.imageUrl) {
                placeholder(R.drawable.placeholder)
                size(ViewSizeResolver(binding.image))
            }
        }
        
        fun updateTitle(title: String) {
            binding.title.text = title
        }
        
        fun updateImage(url: String) {
            binding.image.load(url)
        }
    }
    
    companion object {
        const val PAYLOAD_TITLE = "title"
        const val PAYLOAD_IMAGE = "image"
    }
}

// RecyclerView配置优化
recyclerView.apply {
    // 1. 固定高度时设置setHasFixedSize(true)
    setHasFixedSize(true)
    
    // 2. 使用合适的LayoutManager
    layoutManager = LinearLayoutManager(context).apply {
        // 预加载优化
        initialPrefetchItemCount = 4
    }
    
    // 3. 设置缓存策略
    setItemViewCacheSize(20)
    recycledViewPool.setMaxRecycledViews(0, 20)
    
    // 4. 使用异步布局计算(API 23+)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        layoutManager?.isItemPrefetchEnabled = true
    }
    
    // 5. 避免频繁requestLayout
    // 批量更新数据
    adapter.submitList(newList) {
        // 更新完成回调
    }
}

自定义View绘制优化

// 高性能自定义View
class OptimizedChartView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val path = Path()
    
    // 1. 缓存计算结果
    private var cachedPath: Path? = null
    private var dataHash: Int = 0
    
    // 2. 使用硬件加速
    init {
        setLayerType(LAYER_TYPE_HARDWARE, null)
    }
    
    fun setData(points: List) {
        val newHash = points.hashCode()
        if (newHash != dataHash) {
            dataHash = newHash
            cachedPath = null // 使缓存失效
            invalidate()
        }
    }
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        // 3. 复用Path对象
        path.reset()
        
        // 4. 使用缓存的Path
        val drawPath = cachedPath ?: generatePath().also { cachedPath = it }
        
        // 5. 批量绘制,减少状态切换
        paint.color = Color.BLUE
        paint.strokeWidth = 4f
        paint.style = Paint.Style.STROKE
        
        canvas.drawPath(drawPath, paint)
    }
    
    private fun generatePath(): Path {
        // 生成Path逻辑
        return path
    }
    
    // 6. 脏矩形刷新
    fun updatePoint(index: Int, point: PointF) {
        // 只刷新变化的区域
        val left = (index - 1) * pointWidth
        val right = (index + 1) * pointWidth
        invalidate(left.toInt(), 0, right.toInt(), height)
    }
    
    // 7. 避免在onDraw中创建对象
    override fun onDraw(canvas: Canvas) {
        // ❌ 错误:在onDraw中创建对象
        // val paint = Paint()
        
        // ✅ 正确:复用成员变量
        paint.color = Color.RED
        canvas.drawCircle(x, y, radius, paint)
    }
}

内存优化:稳定运行的基石

内存泄漏检测与防治

// 常见内存泄漏场景与解决方案

// 1. 匿名内部类持有外部类引用
// ❌ 错误
class LeakyActivity : AppCompatActivity() {
    private val handler = Handler(Looper.getMainLooper()) // 持有Activity引用
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handler.postDelayed({
            // 如果Activity已销毁,这里仍持有引用
            updateUI()
        }, 60000)
    }
}

// ✅ 正确
class FixedActivity : AppCompatActivity() {
    private val handler = Handler(Looper.getMainLooper())
    private val runnable = Runnable { updateUI() }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handler.postDelayed(runnable, 60000)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        handler.removeCallbacks(runnable) // 及时移除回调
    }
}

// 2. 使用WeakReference
class SafeCallback(private val activityRef: WeakReference) {
    fun doSomething() {
        activityRef.get()?.let { activity ->
            // 安全地使用Activity
        }
    }
}

// 3. 使用Lifecycle-aware组件
class LocationManager(lifecycle: Lifecycle) : DefaultLifecycleObserver {
    init {
        lifecycle.addObserver(this)
    }
    
    override fun onStart(owner: LifecycleOwner) {
        startLocationUpdates()
    }
    
    override fun onStop(owner: LifecycleOwner) {
        stopLocationUpdates()
    }
}

// 4. 避免静态Context引用
// ❌ 错误
object Singleton {
    lateinit var context: Context // 持有整个应用生命周期
}

// ✅ 正确
object Singleton {
    private lateinit var applicationContext: Context
    
    fun init(context: Context) {
        applicationContext = context.applicationContext
    }
}

Bitmap内存优化

// Bitmap高效加载与管理
object BitmapOptimizer {
    
    // 1. 计算合适的采样率
    fun calculateInSampleSize(
        options: BitmapFactory.Options,
        reqWidth: Int,
        reqHeight: Int
    ): Int {
        val height = options.outHeight
        val width = options.outWidth
        var inSampleSize = 1
        
        if (height > reqHeight || width > reqWidth) {
            val halfHeight = height / 2
            val halfWidth = width / 2
            
            while ((halfHeight / inSampleSize) >= reqHeight &&
                   (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2
            }
        }
        return inSampleSize
    }
    
    // 2. 加载缩放后的Bitmap
    fun decodeSampledBitmapFromResource(
        res: Resources,
        resId: Int,
        reqWidth: Int,
        reqHeight: Int
    ): Bitmap {
        val options = BitmapFactory.Options().apply {
            inJustDecodeBounds = true
            BitmapFactory.decodeResource(res, resId, this)
            inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)
            inJustDecodeBounds = false
            inPreferredConfig = Bitmap.Config.RGB_565 // 减少内存占用
        }
        return BitmapFactory.decodeResource(res, resId, options)
    }
    
    // 3. 使用LruCache
    private val memoryCache: LruCache = object : 
        LruCache((Runtime.getRuntime().maxMemory() / 8).toInt()) {
        override fun sizeOf(key: String, bitmap: Bitmap): Int {
            return bitmap.byteCount / 1024
        }
    }
    
    // 4. Bitmap复用池
    private val bitmapPool = object : LruCache>(10) {
        override fun entryRemoved(
            evicted: Boolean,
            key: Int,
            oldValue: MutableList?,
            newValue: MutableList?
        ) {
            oldValue?.forEach { it.recycle() }
        }
    }
    
    fun getBitmapFromPool(width: Int, height: Int, config: Bitmap.Config): Bitmap? {
        val key = width * height * config.ordinal
        val list = bitmapPool.get(key)
        return list?.removeFirstOrNull()
    }
    
    fun putBitmapToPool(bitmap: Bitmap) {
        if (bitmap.isRecycled) return
        val key = bitmap.width * bitmap.height * bitmap.config.ordinal
        val list = bitmapPool.get(key) ?: mutableListOf()
        list.add(bitmap)
        bitmapPool.put(key, list)
    }
}

内存抖动优化

// 避免内存抖动
class MemoryOptimization {
    
    // 1. 避免在onDraw中创建对象
    // ❌ 错误
    override fun onDraw(canvas: Canvas) {
        for (i in 0..100) {
            val paint = Paint() // 每次绘制都创建新对象
            canvas.drawLine(0f, i * 10f, 100f, i * 10f, paint)
        }
    }
    
    // ✅ 正确
    private val paint = Paint()
    override fun onDraw(canvas: Canvas) {
        for (i in 0..100) {
            canvas.drawLine(0f, i * 10f, 100f, i * 10f, paint)
        }
    }
    
    // 2. 使用对象池
    class ObjectPool(private val factory: () -> T, private val reset: (T) -> Unit) {
        private val pool = ArrayDeque()
        private val maxSize = 10
        
        fun acquire(): T {
            return pool.removeFirstOrNull() ?: factory()
        }
        
        fun release(obj: T) {
            if (pool.size < maxSize) {
                reset(obj)
                pool.addLast(obj)
            }
        }
    }
    
    // 3. 使用StringBuilder替代字符串拼接
    fun buildString(): String {
        // ❌ 错误:产生大量临时String对象
        // var result = ""
        // for (i in 0..1000) {
        //     result += "item$i,"
        // }
        
        // ✅ 正确
        return buildString {
            for (i in 0..1000) {
                append("item$i,")
            }
        }
    }
    
    // 4. 避免AutoBoxing
    fun avoidBoxing() {
        // ❌ 错误:使用包装类集合
        val list = ArrayList()
        
        // ✅ 正确:使用基本类型特化集合
        val sparseArray = SparseIntArray()
        val intArray = IntArray(100)
    }
}

电量优化:延长续航时间

// 电量优化最佳实践

// 1. 使用WorkManager执行后台任务
class SyncWorker(context: Context, params: WorkerParameters) : 
    CoroutineWorker(context, params) {
    
    override suspend fun doWork(): Result {
        // 执行同步任务
        return try {
            syncData()
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }
    
    companion object {
        fun schedule() {
            val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.UNMETERED) // WiFi下执行
                .setRequiresBatteryNotLow(true) // 电量充足时执行
                .build()
            
            val request = PeriodicWorkRequestBuilder(1, TimeUnit.HOURS)
                .setConstraints(constraints)
                .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.MINUTES)
                .build()
            
            WorkManager.getInstance().enqueueUniquePeriodicWork(
                "sync_work",
                ExistingPeriodicWorkPolicy.KEEP,
                request
            )
        }
    }
}

// 2. 批量网络请求
class NetworkBatchManager {
    private val pendingRequests = mutableListOf()
    private val handler = Handler(Looper.getMainLooper())
    
    fun addRequest(request: Request) {
        pendingRequests.add(request)
        
        // 延迟执行,合并请求
        handler.removeCallbacksAndMessages(null)
        handler.postDelayed({ flush() }, 5000)
    }
    
    private fun flush() {
        if (pendingRequests.isEmpty()) return
        
        // 批量发送请求
        api.batchRequest(pendingRequests.toList())
        pendingRequests.clear()
    }
}

// 3. 传感器优化使用
class SensorOptimizer(context: Context) : SensorEventListener {
    private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
    private var accelerometer: Sensor? = null
    
    fun start() {
        accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        
        // 使用合适的采样率
        sensorManager.registerListener(
            this,
            accelerometer,
            SensorManager.SENSOR_DELAY_UI // 根据需求选择合适的延迟级别
        )
    }
    
    fun stop() {
        sensorManager.unregisterListener(this)
    }
    
    override fun onSensorChanged(event: SensorEvent?) {
        // 处理传感器数据
    }
    
    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}

// 4. 使用Doze模式和白名单
fun checkDozeMode(context: Context) {
    val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val isIgnoringBatteryOptimizations = 
            powerManager.isIgnoringBatteryOptimizations(context.packageName)
        
        if (!isIgnoringBatteryOptimizations) {
            // 引导用户添加到电池优化白名单
            val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
                data = Uri.parse("package:${context.packageName}")
            }
            context.startActivity(intent)
        }
    }
}

架构决策总结

优化维度 关键策略 工具推荐
启动优化 延迟初始化、异步加载、布局优化 App Startup、Baseline Profiles
渲染优化 减少过度绘制、RecyclerView优化、硬件加速 GPU Profiler、Systrace
内存优化 LeakCanary检测、Bitmap优化、对象池 LeakCanary、Memory Profiler
电量优化 WorkManager、批量请求、传感器优化 Battery Historian
包体积 资源压缩、代码混淆、动态交付 APK Analyzer、R8

性能优化误区

  • 过早优化:先实现功能,再针对瓶颈优化
  • 没有基准测试:优化前后要有数据对比
  • 过度优化:牺牲代码可读性换取微小性能提升
  • 忽视低端设备:在多种设备上测试
  • 使用Profile工具:数据驱动优化决策

总结

Android性能优化是一项系统工程,需要在架构设计、编码实现、测试验证等各个环节持续关注。通过建立性能监控体系、使用专业工具分析、遵循最佳实践,可以构建出流畅、稳定、省电的高质量应用。

记住:性能优化没有银弹,关键是建立数据驱动的优化流程,持续监控、持续改进。