架构视角:性能是设计出来的
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性能优化是一项系统工程,需要在架构设计、编码实现、测试验证等各个环节持续关注。通过建立性能监控体系、使用专业工具分析、遵循最佳实践,可以构建出流畅、稳定、省电的高质量应用。
记住:性能优化没有银弹,关键是建立数据驱动的优化流程,持续监控、持续改进。