项目结构
text
HighPerformanceViewDemo/
├── app/
│ ├── src/main/java/com/example/performance/
│ │ ├── MainActivity.kt
│ │ ├── view/
│ │ │ ├── BadProgressView.kt
│ │ │ ├── OptimizedProgressView.kt
│ │ │ ├── AsyncProgressView.kt
│ │ │ └── HighPerformanceProgressView.kt
│ │ ├── utils/
│ │ │ ├── PerformanceMonitor.kt
│ │ │ ├── BitmapManager.kt
│ │ │ └── FrameRateTracker.kt
│ │ └── performance/
│ │ ├── BenchmarkActivity.kt
│ │ └── PerformancePanel.kt
│ └── build.gradle.kts
└── README.md
1. 主界面实现 (MainActivity.kt)
kotlin
package com.example.performance
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import com.example.performance.databinding.ActivityMainBinding
import com.example.performance.utils.PerformanceMonitor
import com.example.performance.view.*
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val handler = Handler(Looper.getMainLooper())
private var isRunning = false
private var currentProgress = 0f
private val progressUpdater = object : Runnable {
override fun run() {
if (isRunning) {
currentProgress = (currentProgress + 0.01f) % 1f
binding.badProgressView.setProgress(currentProgress)
binding.optimizedProgressView.setProgress(currentProgress)
binding.asyncProgressView.setProgress(currentProgress)
binding.highPerformanceProgressView.setProgress(currentProgress)
handler.postDelayed(this, 16) // ~60fps更新
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setupViews()
setupPerformanceMonitor()
}
private fun setupViews() {
// 手动进度控制
binding.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
val progressFloat = progress / 100f
binding.badProgressView.setProgress(progressFloat)
binding.optimizedProgressView.setProgress(progressFloat)
binding.asyncProgressView.setProgress(progressFloat)
binding.highPerformanceProgressView.setProgress(progressFloat)
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
// 自动动画开关
binding.btnToggleAnimation.setOnClickListener {
isRunning = !isRunning
binding.btnToggleAnimation.text = if (isRunning) "停止动画" else "开始动画"
if (isRunning) {
handler.post(progressUpdater)
} else {
handler.removeCallbacks(progressUpdater)
}
}
// 性能测试按钮
binding.btnBenchmark.setOnClickListener {
BenchmarkActivity.start(this)
}
// 清空缓存按钮
binding.btnClearCache.setOnClickListener {
BitmapManager.clearCache()
binding.tvCacheStatus.text = "缓存已清空"
handler.postDelayed({
binding.tvCacheStatus.text = ""
}, 1500)
}
}
private fun setupPerformanceMonitor() {
PerformanceMonitor.startMonitoring(this)
// 每2秒更新一次性能数据
handler.post(object : Runnable {
override fun run() {
val info = PerformanceMonitor.getPerformanceInfo()
binding.tvFps.text = String.format("FPS: %.1f", info.fps)
binding.tvMemory.text = String.format("内存: %.1fMB", info.usedMemoryMB)
binding.tvCpu.text = String.format("CPU: %.1f%%", info.cpuUsage)
binding.tvGpuTime.text = String.format("GPU: %.1fms", info.gpuTimeMs)
// 设置颜色提示
val fpsColor = when {
info.fps > 55 -> 0xFF4CAF50.toInt() // 绿色
info.fps > 30 -> 0xFFFFC107.toInt() // 黄色
else -> 0xFFF44336.toInt() // 红色
}
binding.tvFps.setTextColor(fpsColor)
handler.postDelayed(this, 2000)
}
})
}
override fun onDestroy() {
super.onDestroy()
isRunning = false
handler.removeCallbacks(progressUpdater)
PerformanceMonitor.stopMonitoring()
}
}
2. 布局文件 (activity_main.xml)
xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- 性能监控面板 -->
<com.example.performance.performance.PerformancePanel
android:id="@+id/performancePanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp" />
<!-- 控制面板 -->
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="控制面板"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="16dp" />
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp" />
<Button
android:id="@+id/btnToggleAnimation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始动画"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnBenchmark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="性能测试"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/btnClearCache"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="清空Bitmap缓存" />
<TextView
android:id="@+id/tvCacheStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/holo_green_dark"
android:layout_marginTop="8dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- 四个对比View -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="❌ 基础实现 (问题版)"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginBottom="8dp" />
<com.example.performance.view.BadProgressView
android:id="@+id/badProgressView"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginBottom="24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="✅ 优化版1:硬件加速"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginBottom="8dp" />
<com.example.performance.view.OptimizedProgressView
android:id="@+id/optimizedProgressView"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginBottom="24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="🚀 优化版2:异步绘制 (API 28+)"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginBottom="8dp" />
<com.example.performance.view.AsyncProgressView
android:id="@+id/asyncProgressView"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginBottom="24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="🏆 优化版3:综合优化"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginBottom="8dp" />
<com.example.performance.view.HighPerformanceProgressView
android:id="@+id/highPerformanceProgressView"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginBottom="24dp" />
<!-- 实时性能数据 -->
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="实时性能数据"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginBottom="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<TextView
android:id="@+id/tvFps"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="FPS: --"
android:textSize="14sp" />
<TextView
android:id="@+id/tvMemory"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="内存: --MB"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tvCpu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="CPU: --%"
android:textSize="14sp" />
<TextView
android:id="@+id/tvGpuTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="GPU: --ms"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>
3. 四个View的完整实现
3.1 BadProgressView.kt (问题版)
kotlin
package com.example.performance.view
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import kotlin.random.Random
class BadProgressView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
// ❌ 问题1:每次onDraw都创建新的Paint对象
// ❌ 问题2:Shader在onDraw中频繁创建
// ❌ 问题3:过度绘制
// ❌ 问题4:对象分配频繁
private var progress = 0f
private var lastUpdateTime = 0L
private var frameCount = 0
init {
// 故意不启用硬件加速,展示问题
setLayerType(LAYER_TYPE_SOFTWARE, null)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// ❌ 每帧都创建新对象
val paint = Paint().apply {
color = Color.RED
style = Paint.Style.FILL
}
// ❌ 在onDraw中创建Shader
val shader = LinearGradient(
0f, 0f, width.toFloat(), 0f,
Color.RED, Color.BLUE, Shader.TileMode.CLAMP
)
paint.shader = shader
// ❌ 每帧都创建RectF
val rect = RectF(0f, 0f, width * progress, height.toFloat())
// 绘制背景
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
// ❌ 过度绘制:先画背景再画进度
paint.color = Color.GREEN
canvas.drawRect(rect, paint)
// ❌ 复杂绘制:圆角效果(软件绘制性能差)
val path = Path().apply {
addRoundRect(rect, 20f, 20f, Path.Direction.CW)
}
paint.color = Color.YELLOW
canvas.drawPath(path, paint)
// ❌ 文本绘制频繁
val textPaint = Paint().apply {
color = Color.WHITE
textSize = 24f
textAlign = Paint.Align.CENTER
}
// ❌ 每帧都计算文本位置
val text = "${(progress * 100).toInt()}%"
val textBounds = Rect()
textPaint.getTextBounds(text, 0, text.length, textBounds)
// ❌ 多次绘制文本
canvas.drawText(text, width / 2f, height / 2f, textPaint)
canvas.drawText("FPS: ${calculateFPS()}", 100f, 30f, textPaint)
// ❌ 不必要的阴影效果
paint.setShadowLayer(10f, 5f, 5f, Color.GRAY)
canvas.drawText("性能差", 200f, 30f, paint)
frameCount++
}
fun setProgress(progress: Float) {
this.progress = progress
invalidate() // ❌ 全量刷新
}
private fun calculateFPS(): Int {
val currentTime = System.currentTimeMillis()
if (currentTime - lastUpdateTime > 1000) {
lastUpdateTime = currentTime
val fps = frameCount
frameCount = 0
return fps
}
return 0
}
// ❌ 内存泄漏风险
private val bitmaps = mutableListOf<Bitmap>()
private fun createMemoryLeak() {
// 故意创建Bitmap但不回收
repeat(10) {
bitmaps.add(
Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
)
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
// ❌ 忘记清理Bitmap
// bitmaps.forEach { it.recycle() }
}
}
3.2 OptimizedProgressView.kt (硬件加速优化版)
kotlin
package com.example.performance.view
import android.content.Context
import android.graphics.*
import android.os.Build
import android.util.AttributeSet
import android.view.View
import androidx.annotation.RequiresApi
class OptimizedProgressView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
// ✅ 优化1:复用Paint对象
private val progressPaint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG).apply {
style = Paint.Style.FILL
color = Color.BLUE
}
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.WHITE
textSize = 32f
textAlign = Paint.Align.CENTER
typeface = Typeface.DEFAULT_BOLD
}
private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.LTGRAY
style = Paint.Style.FILL
}
// ✅ 优化2:复用RectF对象
private val progressRect = RectF()
private val bgRect = RectF()
// ✅ 优化3:Shader缓存
private var gradientShader: LinearGradient? = null
private var lastWidth = 0
// ✅ 优化4:文本边界缓存
private val textBounds = Rect()
private var cachedText = ""
private var progress = 0f
init {
// ✅ 启用硬件加速
setLayerType(LAYER_TYPE_HARDWARE, null)
// ✅ 标记不支持硬件加速的操作
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
forceHasOverlappingRendering(false)
}
// ✅ 初始化Shader
updateShaderIfNeeded()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
updateShaderIfNeeded()
}
private fun updateShaderIfNeeded() {
if (width != lastWidth && width > 0) {
gradientShader = LinearGradient(
0f, 0f, width.toFloat(), 0f,
intArrayOf(
Color.parseColor("#FF6B6B"),
Color.parseColor("#4ECDC4"),
Color.parseColor("#45B7D1")
),
floatArrayOf(0f, 0.5f, 1f),
Shader.TileMode.CLAMP
)
progressPaint.shader = gradientShader
lastWidth = width
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// ✅ 检查硬件加速
if (!canvas.isHardwareAccelerated) {
drawSoftware(canvas)
return
}
// ✅ 绘制背景
bgRect.set(0f, 0f, width.toFloat(), height.toFloat())
canvas.drawRoundRect(bgRect, 12f, 12f, bgPaint)
// ✅ 绘制进度条
if (progress > 0) {
progressRect.set(0f, 0f, width * progress, height.toFloat())
canvas.drawRoundRect(progressRect, 12f, 12f, progressPaint)
// ✅ 缓存文本计算
val text = "${(progress * 100).toInt()}%"
if (text != cachedText) {
textPaint.getTextBounds(text, 0, text.length, textBounds)
cachedText = text
}
// ✅ 计算文本位置
val x = (width * progress).coerceAtLeast(textBounds.width() / 2f + 20)
val y = height / 2f - (textBounds.top + textBounds.bottom) / 2f
// ✅ 只绘制一次文本
canvas.drawText(text, x, y, textPaint)
}
}
private fun drawSoftware(canvas: Canvas) {
// 软件绘制的回退方案
bgRect.set(0f, 0f, width.toFloat(), height.toFloat())
canvas.drawRoundRect(bgRect, 12f, 12f, bgPaint)
if (progress > 0) {
progressRect.set(0f, 0f, width * progress, height.toFloat())
canvas.drawRoundRect(progressRect, 12f, 12f, progressPaint)
}
}
fun setProgress(progress: Float) {
this.progress = progress.coerceIn(0f, 1f)
// ✅ 使用局部刷新
val left = (width * (this.progress - 0.01f)).toInt()
val right = (width * (this.progress + 0.01f)).toInt()
invalidate(left, 0, right, height)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
// ✅ 清理资源
gradientShader = null
}
}
3.3 AsyncProgressView.kt (异步绘制版)
kotlin
package com.example.performance.view
import android.content.Context
import android.graphics.*
import android.os.*
import android.util.AttributeSet
import android.view.View
import androidx.annotation.RequiresApi
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
@RequiresApi(Build.VERSION_CODES.P)
class AsyncProgressView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
// ✅ 异步绘制优化
private val renderThread = RenderThread()
private val mainHandler = Handler(Looper.getMainLooper())
private val asyncExecutor: ExecutorService = Executors.newSingleThreadExecutor()
// ✅ RenderNode API
private var renderNode: RenderNode? = null
private var currentBitmap: Bitmap? = null
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
}
private var progress = 0f
private var targetProgress = 0f
private val animator = ValueAnimator.ofFloat(0f, 1f).apply {
duration = 300
interpolator = android.view.animation.AccelerateDecelerateInterpolator()
addUpdateListener {
progress = it.animatedValue as Float
scheduleRender()
}
}
init {
// ✅ 必须启用硬件加速
setLayerType(LAYER_TYPE_HARDWARE, null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// ✅ 使用RenderNode
renderNode = RenderNode("AsyncProgressView")
}
// ✅ 初始化渐变
paint.shader = LinearGradient(
0f, 0f, 1000f, 0f, // 使用最大宽度
intArrayOf(
Color.parseColor("#667EEA"),
Color.parseColor("#764BA2"),
Color.parseColor("#F093FB")
),
null,
Shader.TileMode.CLAMP
)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
// ✅ 创建离屏Bitmap用于异步绘制
if (w > 0 && h > 0) {
currentBitmap?.recycle()
currentBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
renderNode?.setPosition(0, 0, w, h)
}
}
fun setProgress(progress: Float) {
targetProgress = progress.coerceIn(0f, 1f)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// ✅ 使用RenderThread驱动的动画
animator.setFloatValues(this.progress, targetProgress)
if (!animator.isRunning) {
animator.start()
}
} else {
this.progress = targetProgress
scheduleRender()
}
}
private fun scheduleRender() {
// ✅ 提交异步绘制任务
asyncExecutor.submit {
renderOffscreen()
// ✅ 完成后通知主线程更新
mainHandler.post {
invalidate()
}
}
}
private fun renderOffscreen() {
val bitmap = currentBitmap ?: return
val canvas = Canvas(bitmap)
// ✅ 在后台线程绘制
drawContent(canvas)
// ✅ 使用RenderNode记录绘制命令
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val node = renderNode ?: return
node.beginRecording().let { recorder ->
drawContent(recorder)
node.endRecording()
}
}
}
private fun drawContent(canvas: Canvas) {
// 清空画布
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
// 绘制背景
canvas.drawRoundRect(
0f, 0f, width.toFloat(), height.toFloat(),
16f, 16f, Paint().apply {
color = Color.parseColor("#E0E0E0")
isAntiAlias = true
}
)
// 绘制进度
val progressWidth = width * progress
if (progressWidth > 0) {
canvas.drawRoundRect(
0f, 0f, progressWidth, height.toFloat(),
16f, 16f, paint
)
// 绘制文本
val text = "${(progress * 100).toInt()}%"
val textPaint = Paint().apply {
color = Color.WHITE
textSize = 28f
textAlign = Paint.Align.CENTER
isAntiAlias = true
typeface = Typeface.DEFAULT_BOLD
}
val x = (width * progress / 2).coerceAtLeast(50f)
val y = height / 2f - (textPaint.descent() + textPaint.ascent()) / 2
canvas.drawText(text, x, y, textPaint)
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (!canvas.isHardwareAccelerated) {
// 软件渲染回退
drawContent(canvas)
return
}
// ✅ 绘制预渲染的内容
currentBitmap?.let { bitmap ->
canvas.drawBitmap(bitmap, 0f, 0f, null)
}
// ✅ 使用RenderNode绘制
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
renderNode?.let { node ->
canvas.drawRenderNode(node)
}
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
// ✅ 清理资源
animator.cancel()
asyncExecutor.shutdown()
currentBitmap?.recycle()
renderNode = null
// ✅ 停止渲染线程
renderThread.quit()
}
@RequiresApi(Build.VERSION_CODES.P)
private inner class RenderThread : HandlerThread("RenderThread") {
private lateinit var handler: Handler
init {
start()
handler = Handler(looper)
}
fun render(frame: Long, callback: Runnable) {
handler.postAtTime(callback, frame)
}
fun quit() {
looper.quitSafely()
}
}
}
3.4 HighPerformanceProgressView.kt (综合优化版)
kotlin
package com.example.performance.view
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.os.*
import android.util.AttributeSet
import android.view.Choreographer
import android.view.View
import androidx.annotation.RequiresApi
import com.example.performance.utils.BitmapManager
import kotlin.math.max
import kotlin.math.min
class HighPerformanceProgressView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr), Choreographer.FrameCallback {
// ========== 优化配置 ==========
companion object {
// ✅ 静态Paint对象,避免重复创建
private val PROGRESS_PAINT = Paint().apply {
flags = Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG or Paint.FILTER_BITMAP_FLAG
style = Paint.Style.FILL
color = Color.parseColor("#2196F3")
}
private val TEXT_PAINT = Paint().apply {
flags = Paint.ANTI_ALIAS_FLAG or Paint.SUBPIXEL_TEXT_FLAG
color = Color.WHITE
textSize = 36f
textAlign = Paint.Align.CENTER
typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL)
}
private val BG_PAINT = Paint().apply {
flags = Paint.ANTI_ALIAS_FLAG
color = Color.parseColor("#F5F5F5")
style = Paint.Style.FILL
}
private val BORDER_PAINT = Paint().apply {
flags = Paint.ANTI_ALIAS_FLAG
color = Color.parseColor("#E0E0E0")
style = Paint.Style.STROKE
strokeWidth = 2f
}
}
// ========== 状态管理 ==========
private var currentProgress = 0f
private var animatedProgress = 0f
private var targetProgress = 0f
private var isAnimating = false
// ========== 对象复用池 ==========
private val rectPool = mutableListOf<RectF>()
private val pathPool = mutableListOf<Path>()
private fun obtainRectF(): RectF {
return if (rectPool.isNotEmpty()) {
rectPool.removeAt(0).also { it.setEmpty() }
} else {
RectF()
}
}
private fun recycleRectF(rect: RectF) {
if (rectPool.size < 5) { // 限制池大小
rectPool.add(rect)
}
}
// ========== 缓存管理 ==========
private var cachedWidth = 0
private var cachedHeight = 0
private var gradientShader: LinearGradient? = null
private var lastShaderUpdateTime = 0L
// ========== 路径预计算 ==========
private val cornerPath = Path()
private val clipPath = Path()
private var pathsDirty = true
// ========== 文本缓存 ==========
private val textBounds = Rect()
private var cachedText = ""
private var cachedTextWidth = 0f
// ========== 动画系统 ==========
private val animator = ValueAnimator().apply {
duration = 250L
interpolator = android.view.animation.AccelerateDecelerateInterpolator()
addUpdateListener {
animatedProgress = it.animatedValue as Float
if (useChoreographerSync) {
scheduleFrame() // 使用Choreographer同步
} else {
invalidateOptimized()
}
}
}
// ========== 高刷新率适配 ==========
private var refreshRate = 60f
private var useChoreographerSync = false
private var choreographer: Choreographer? = null
private var vsyncTime = 0L
// ========== 初始化 ==========
init {
// ✅ 启用硬件加速层
setLayerType(LAYER_TYPE_HARDWARE, null)
// ✅ 获取设备刷新率
val display = context.getSystemService(Context.DISPLAY_SERVICE) as? android.view.Display
refreshRate = display?.refreshRate ?: 60f
// ✅ 根据刷新率调整动画时长
if (refreshRate > 60) {
useChoreographerSync = true
choreographer = Choreographer.getInstance()
animator.duration = (1000 * 60f / refreshRate).toLong()
}
// ✅ 标记无重叠渲染
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
forceHasOverlappingRendering(false)
}
// ✅ 禁用默认的clipChildren
clipChildren = false
// ✅ 初始化渐变
updateGradientShader()
}
// ========== 尺寸处理 ==========
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (w != cachedWidth || h != cachedHeight) {
cachedWidth = w
cachedHeight = h
pathsDirty = true
updateGradientShader()
}
}
private fun updateGradientShader() {
if (cachedWidth <= 0) return
val now = System.currentTimeMillis()
// ✅ 限制Shader更新频率(至少间隔500ms)
if (now - lastShaderUpdateTime > 500) {
gradientShader = LinearGradient(
0f, 0f, cachedWidth.toFloat(), 0f,
intArrayOf(
Color.parseColor("#FF6B6B"),
Color.parseColor("#FFD166"),
Color.parseColor("#06D6A0"),
Color.parseColor("#118AB2")
),
floatArrayOf(0f, 0.3f, 0.6f, 1f),
Shader.TileMode.CLAMP
).apply {
setLocalMatrix(Matrix().apply {
setScale(1.5f, 1f) // 扩大渐变范围,避免动画时重复创建
})
}
PROGRESS_PAINT.shader = gradientShader
lastShaderUpdateTime = now
}
}
// ========== 绘制优化 ==========
override fun onDraw(canvas: Canvas) {
if (!canvas.isHardwareAccelerated) {
drawSoftwareFallback(canvas)
return
}
// ✅ 1. 预计算路径(惰性计算)
if (pathsDirty) {
updatePaths()
pathsDirty = false
}
// ✅ 2. 使用离屏缓存(针对复杂效果)
if (shouldUseOffscreenCache()) {
drawWithOffscreenCache(canvas)
return
}
// ✅ 3. 直接硬件加速绘制
drawHardwareAccelerated(canvas)
}
private fun updatePaths() {
cornerPath.reset()
clipPath.reset()
val cornerRadius = min(cachedHeight / 2f, 24f)
// 圆角路径
cornerPath.addRoundRect(
0f, 0f, cachedWidth.toFloat(), cachedHeight.toFloat(),
cornerRadius, cornerRadius, Path.Direction.CW
)
// 裁剪路径(用于进度条)
clipPath.addRect(0f, 0f, cachedWidth.toFloat(), cachedHeight.toFloat(), Path.Direction.CW)
}
private fun shouldUseOffscreenCache(): Boolean {
// 在需要复杂混合效果时使用离屏缓存
return PROGRESS_PAINT.xfermode != null ||
Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
}
private fun drawWithOffscreenCache(canvas: Canvas) {
// 使用BitmapManager获取缓存Bitmap
val bitmap = BitmapManager.getOrCreateBitmap(
"progress_cache_${cachedWidth}x${cachedHeight}",
cachedWidth, cachedHeight
)
val offscreenCanvas = Canvas(bitmap)
// 绘制到离屏Bitmap
drawToCanvas(offscreenCanvas)
// 绘制到实际Canvas
canvas.drawBitmap(bitmap, 0f, 0f, null)
}
private fun drawHardwareAccelerated(canvas: Canvas) {
// ✅ 保存画布状态
canvas.save()
// ✅ 使用clipPath避免过度绘制
canvas.clipPath(cornerPath)
// ✅ 绘制背景
canvas.drawRoundRect(
0f, 0f, cachedWidth.toFloat(), cachedHeight.toFloat(),
12f, 12f, BG_PAINT
)
// ✅ 绘制边框
canvas.drawRoundRect(
0f, 0f, cachedWidth.toFloat(), cachedHeight.toFloat(),
12f, 12f, BORDER_PAINT
)
// ✅ 绘制进度条
if (animatedProgress > 0.001f) {
val progressWidth = cachedWidth * animatedProgress
// 使用对象池获取RectF
val progressRect = obtainRectF().apply {
set(0f, 0f, progressWidth, cachedHeight.toFloat())
}
canvas.drawRoundRect(progressRect, 12f, 12f, PROGRESS_PAINT)
// ✅ 绘制文本(带缓存)
drawOptimizedText(canvas, progressWidth)
// 归还对象到池中
recycleRectF(progressRect)
}
canvas.restore()
}
private fun drawOptimizedText(canvas: Canvas, progressWidth: Float) {
val text = "${(animatedProgress * 100).toInt()}%"
// ✅ 文本缓存优化
if (text != cachedText) {
TEXT_PAINT.getTextBounds(text, 0, text.length, textBounds)
cachedText = text
cachedTextWidth = TEXT_PAINT.measureText(text)
}
// ✅ 计算文本位置
val textX = max(progressWidth / 2, cachedTextWidth / 2 + 16)
val textY = cachedHeight / 2f - (textBounds.top + textBounds.bottom) / 2f
// ✅ 文本阴影效果(硬件加速友好)
if (progressWidth > cachedTextWidth + 32) {
// 只在有足够空间时绘制阴影
TEXT_PAINT.setShadowLayer(4f, 2f, 2f, Color.parseColor("#66000000"))
canvas.drawText(text, textX, textY, TEXT_PAINT)
TEXT_PAINT.clearShadowLayer()
} else {
canvas.drawText(text, textX, textY, TEXT_PAINT)
}
}
private fun drawSoftwareFallback(canvas: Canvas) {
// 软件渲染的简单实现
canvas.drawRoundRect(
0f, 0f, cachedWidth.toFloat(), cachedHeight.toFloat(),
12f, 12f, BG_PAINT
)
if (animatedProgress > 0) {
canvas.drawRoundRect(
0f, 0f, cachedWidth * animatedProgress, cachedHeight.toFloat(),
12f, 12f, PROGRESS_PAINT
)
}
}
private fun drawToCanvas(canvas: Canvas) {
// 通用的绘制逻辑
drawHardwareAccelerated(canvas)
}
// ========== 动画控制 ==========
fun setProgress(progress: Float, animate: Boolean = true) {
targetProgress = progress.coerceIn(0f, 1f)
if (!animate || !useChoreographerSync) {
currentProgress = targetProgress
animatedProgress = targetProgress
invalidateOptimized()
return
}
if (isAnimating) {
animator.cancel()
}
animator.setFloatValues(animatedProgress, targetProgress)
animator.start()
isAnimating = true
}
// ========== 高刷新率同步 ==========
private fun scheduleFrame() {
choreographer?.postFrameCallback(this)
}
override fun doFrame(frameTimeNanos: Long) {
val frameTimeMillis = frameTimeNanos / 1_000_000
// ✅ 基于垂直同步的时间差计算
val delta = (frameTimeMillis - vsyncTime).coerceAtLeast(16L)
vsyncTime = frameTimeMillis
// ✅ 更新动画进度(基于时间插值)
if (isAnimating) {
val fraction = animator.animatedFraction
animatedProgress = currentProgress + (targetProgress - currentProgress) * fraction
if (fraction >= 1f) {
isAnimating = false
currentProgress = targetProgress
choreographer?.removeFrameCallback(this)
} else {
choreographer?.postFrameCallback(this)
}
invalidateOptimized()
}
}
// ========== 刷新优化 ==========
private fun invalidateOptimized() {
// ✅ 计算脏矩形,只刷新需要更新的区域
val progressWidth = cachedWidth * animatedProgress
val dirtyLeft = (progressWidth - 50).toInt().coerceAtLeast(0)
val dirtyRight = (progressWidth + 50).toInt().coerceAtMost(cachedWidth)
invalidate(dirtyLeft, 0, dirtyRight, cachedHeight)
}
// ========== 内存管理 ==========
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
// ✅ 停止所有动画
animator.cancel()
choreographer?.removeFrameCallback(this)
// ✅ 清理渐变
gradientShader = null
PROGRESS_PAINT.shader = null
// ✅ 清理对象池
rectPool.clear()
pathPool.clear()
// ✅ 通知BitmapManager清理相关缓存
BitmapManager.removeCache("progress_cache_${cachedWidth}x${cachedHeight}")
}
// ========== 性能监控 ==========
private var drawStartTime = 0L
override fun dispatchDraw(canvas: Canvas?) {
drawStartTime = System.nanoTime()
super.dispatchDraw(canvas)
val drawTime = (System.nanoTime() - drawStartTime) / 1_000_000f
// 监控绘制时间(生产环境应该移除)
if (drawTime > 16) {
// 绘制时间过长警告
android.util.Log.w("Performance", "HighPerformanceProgressView draw time: ${drawTime}ms")
}
}
}
4. 性能监控工具
4.1 PerformanceMonitor.kt
kotlin
package com.example.performance.utils
import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.os.Build
import android.os.Debug
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.Choreographer
import kotlin.math.roundToInt
data class PerformanceInfo(
val fps: Float,
val usedMemoryMB: Float,
val totalMemoryMB: Float,
val cpuUsage: Float,
val gpuTimeMs: Float,
val jankCount: Int,
val frameTime95th: Float
)
object PerformanceMonitor : Choreographer.FrameCallback {
private var isMonitoring = false
private var frameCount = 0
private var lastFrameTime = 0L
private var frameTimes = mutableListOf<Long>()
private val handler = Handler(Looper.getMainLooper())
private var activity: Activity? = null
private var jankThreshold = 16666667L // 16.67ms in nanoseconds
private var jankCount = 0
private val frameTimeHistory = ArrayDeque<Long>()
private const val HISTORY_SIZE = 240 // 4秒数据(按60fps)
// 性能数据回调
var onPerformanceUpdate: ((PerformanceInfo) -> Unit)? = null
fun startMonitoring(activity: Activity) {
if (isMonitoring) return
this.activity = activity
isMonitoring = true
frameCount = 0
lastFrameTime = System.nanoTime()
frameTimes.clear()
frameTimeHistory.clear()
jankCount = 0
// 根据刷新率调整阈值
val display = activity.getSystemService(Context.DISPLAY_SERVICE)
as android.view.Display
val refreshRate = display.refreshRate
jankThreshold = (1_000_000_000L / refreshRate).toLong()
// 开始监控
Choreographer.getInstance().postFrameCallback(this)
Log.i("PerformanceMonitor", "Started monitoring with refresh rate: ${refreshRate}Hz")
}
fun stopMonitoring() {
isMonitoring = false
Choreographer.getInstance().removeFrameCallback(this)
activity = null
Log.i("PerformanceMonitor", "Stopped monitoring")
}
override fun doFrame(frameTimeNanos: Long) {
if (!isMonitoring) return
// 计算帧时间
val frameTime = frameTimeNanos - lastFrameTime
lastFrameTime = frameTimeNanos
// 记录帧时间
frameTimes.add(frameTime)
frameTimeHistory.addLast(frameTime)
if (frameTimeHistory.size > HISTORY_SIZE) {
frameTimeHistory.removeFirst()
}
// 检测卡顿
if (frameTime > jankThreshold * 2) {
jankCount++
Log.w("PerformanceMonitor", "Jank detected: ${frameTime / 1_000_000}ms")
}
frameCount++
// 每秒计算一次FPS
if (frameCount >= 60) { // 大约每秒(按60fps)
calculatePerformance()
frameCount = 0
frameTimes.clear()
}
// 继续监控下一帧
Choreographer.getInstance().postFrameCallback(this)
}
private fun calculatePerformance() {
val ctx = activity ?: return
// 计算FPS
val fps = if (frameTimes.isNotEmpty()) {
val avgFrameTime = frameTimes.average()
1_000_000_000.0 / avgFrameTime
} else {
0.0
}
// 获取内存使用
val memoryInfo = ActivityManager.MemoryInfo()
val activityManager = ctx.getSystemService(Context.ACTIVITY_SERVICE)
as ActivityManager
activityManager.getMemoryInfo(memoryInfo)
val usedMemory = Runtime.getRuntime().totalMemory() -
Runtime.getRuntime().freeMemory()
val usedMemoryMB = usedMemory / (1024.0 * 1024.0)
val totalMemoryMB = Runtime.getRuntime().totalMemory() / (1024.0 * 1024.0)
// 计算CPU使用率(简化版)
val cpuUsage = calculateCpuUsage()
// 计算GPU时间(估算)
val gpuTimeMs = estimateGpuTime()
// 计算95%帧时间
val sortedTimes = frameTimeHistory.sorted()
val index95th = (sortedTimes.size * 0.95).toInt()
val frameTime95th = if (sortedTimes.isNotEmpty() && index95th < sortedTimes.size) {
sortedTimes[index95th] / 1_000_000.0f
} else {
0f
}
val info = PerformanceInfo(
fps = fps.toFloat(),
usedMemoryMB = usedMemoryMB.toFloat(),
totalMemoryMB = totalMemoryMB.toFloat(),
cpuUsage = cpuUsage,
gpuTimeMs = gpuTimeMs,
jankCount = jankCount,
frameTime95th = frameTime95th
)
// 回调更新
handler.post {
onPerformanceUpdate?.invoke(info)
}
}
private fun calculateCpuUsage(): Float {
return try {
// 读取/proc/stat获取CPU使用率
val reader = java.io.BufferedReader(
java.io.InputStreamReader(
java.lang.ProcessBuilder("top", "-n", "1").start().inputStream
)
)
var cpuUsage = 0f
var line: String?
while (reader.readLine().also { line = it } != null) {
if (line?.contains("CPU") == true) {
val parts = line!!.split("\\s+".toRegex())
if (parts.size > 8) {
val user = parts[2].toFloatOrNull() ?: 0f
val system = parts[4].toFloatOrNull() ?: 0f
cpuUsage = user + system
break
}
}
}
reader.close()
cpuUsage
} catch (e: Exception) {
Log.e("PerformanceMonitor", "Error calculating CPU usage", e)
0f
}
}
private fun estimateGpuTime(): Float {
// 简化估算:基于帧时间和CPU使用率
if (frameTimeHistory.isEmpty()) return 0f
val avgFrameTime = frameTimeHistory.average()
val cpuTime = avgFrameTime * 0.4 // 假设40%是CPU时间
val gpuTime = avgFrameTime - cpuTime
return (gpuTime / 1_000_000).toFloat().coerceAtLeast(0f)
}
fun getPerformanceInfo(): PerformanceInfo {
val ctx = activity ?: return PerformanceInfo(0f, 0f, 0f, 0f, 0f, 0, 0f)
// 实时计算当前性能数据
val currentFps = if (frameTimes.isNotEmpty()) {
val avgFrameTime = frameTimes.average()
(1_000_000_000.0 / avgFrameTime).toFloat()
} else {
0f
}
val usedMemory = Runtime.getRuntime().totalMemory() -
Runtime.getRuntime().freeMemory()
val usedMemoryMB = usedMemory / (1024.0f * 1024.0f)
return PerformanceInfo(
fps = currentFps,
usedMemoryMB = usedMemoryMB,
totalMemoryMB = Runtime.getRuntime().totalMemory() / (1024.0f * 1024.0f),
cpuUsage = calculateCpuUsage(),
gpuTimeMs = estimateGpuTime(),
jankCount = jankCount,
frameTime95th = 0f // 需要完整历史数据
)
}
fun resetJankCount() {
jankCount = 0
}
}
4.2 BitmapManager.kt
kotlin
package com.example.performance.utils
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Build
import android.util.LruCache
import android.util.Size
import kotlin.math.roundToInt
object BitmapManager {
// 内存缓存(使用LRU策略)
private val memoryCache: LruCache<String, Bitmap>
// 尺寸缓存池
private val sizeCache = mutableMapOf<Size, MutableList<Bitmap>>()
init {
// 分配最大内存的1/8作为图片缓存
val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
val cacheSize = maxMemory / 8
memoryCache = object : LruCache<String, Bitmap>(cacheSize) {
override fun sizeOf(key: String, bitmap: Bitmap): Int {
// 返回Bitmap占用的KB数
return bitmap.allocationByteCount / 1024
}
override fun entryRemoved(
evicted: Boolean,
key: String,
oldValue: Bitmap,
newValue: Bitmap?
) {
// 被移除时尝试放入尺寸缓存池
if (evicted && !oldValue.isRecycled) {
cacheBitmapBySize(oldValue)
}
}
}
}
/**
* 加载或获取缓存的Bitmap
*/
fun getOrCreateBitmap(key: String, width: Int, height: Int): Bitmap {
// 1. 尝试从内存缓存获取
memoryCache.get(key)?.let { cachedBitmap ->
if (!cachedBitmap.isRecycled &&
cachedBitmap.width == width &&
cachedBitmap.height == height) {
return cachedBitmap
}
}
// 2. 尝试从尺寸缓存池获取
val size = Size(width, height)
val pool = sizeCache[size]
pool?.let {
synchronized(it) {
if (it.isNotEmpty()) {
val bitmap = it.removeAt(0)
if (!bitmap.isRecycled) {
// 清空Bitmap内容
bitmap.eraseColor(android.graphics.Color.TRANSPARENT)
return bitmap
}
}
}
}
// 3. 创建新的Bitmap
return createOptimizedBitmap(width, height).also { bitmap ->
memoryCache.put(key, bitmap)
}
}
/**
* 创建经过优化的Bitmap
*/
private fun createOptimizedBitmap(width: Int, height: Int): Bitmap {
// 根据需求选择合适的配置
val config = when {
// 需要透明度
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ->
Bitmap.Config.ARGB_8888
// 不需要透明度且节省内存
else -> Bitmap.Config.RGB_565
}
return try {
Bitmap.createBitmap(width, height, config)
} catch (e: OutOfMemoryError) {
// 内存不足时尝试清理缓存
clearCache()
// 尝试使用更小的配置
val fallbackConfig = Bitmap.Config.RGB_565
Bitmap.createBitmap(width, height, fallbackConfig)
}
}
/**
* 根据尺寸缓存Bitmap
*/
private fun cacheBitmapBySize(bitmap: Bitmap) {
if (bitmap.isRecycled) return
val size = Size(bitmap.width, bitmap.height)
val pool = sizeCache.getOrPut(size) { mutableListOf() }
synchronized(pool) {
// 限制池大小,避免占用过多内存
if (pool.size < 3) {
pool.add(bitmap)
} else {
// 池已满,回收Bitmap
bitmap.recycle()
}
}
}
/**
* 解码资源图片并优化
*/
fun decodeResource(resId: Int, reqWidth: Int, reqHeight: Int): Bitmap {
val key = "res_${resId}_${reqWidth}x${reqHeight}"
// 检查缓存
memoryCache.get(key)?.let { return it }
val options = BitmapFactory.Options().apply {
// 只读取尺寸信息
inJustDecodeBounds = true
BitmapFactory.decodeResource(ResourcesProvider.resources, resId, this)
// 计算采样率
inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)
// 配置解码参数
inJustDecodeBounds = false
inPreferredConfig = Bitmap.Config.RGB_565
inDither = true
inScaled = true
// Android 4.4+ 启用位图复用
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
inMutable = true
// 尝试从缓存中获取可复用的Bitmap
val reusableBitmap = findReusableBitmap(reqWidth, reqHeight)
reusableBitmap?.let { inBitmap = it }
}
}
return try {
BitmapFactory.decodeResource(ResourcesProvider.resources, resId, options).also {
memoryCache.put(key, it)
}
} catch (e: IllegalArgumentException) {
// inBitmap复用失败,不使用inBitmap重试
options.inBitmap = null
BitmapFactory.decodeResource(ResourcesProvider.resources, resId, options).also {
memoryCache.put(key, it)
}
}
}
/**
* 计算合适的采样率
*/
private fun calculateInSampleSize(
options: BitmapFactory.Options,
reqWidth: Int,
reqHeight: Int
): Int {
val (width, height) = options.run { outWidth to outHeight }
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
}
}
// 针对高分辨率屏幕进一步优化
val density = ResourcesProvider.resources.displayMetrics.densityDpi
if (density > 320 && inSampleSize == 1) {
inSampleSize = 2
}
return inSampleSize
}
/**
* 查找可复用的Bitmap
*/
private fun findReusableBitmap(reqWidth: Int, reqHeight: Int): Bitmap? {
// 1. 从尺寸缓存池查找
val size = Size(reqWidth, reqHeight)
val pool = sizeCache[size]
pool?.let {
synchronized(it) {
if (it.isNotEmpty()) {
val bitmap = it.removeAt(0)
if (!bitmap.isRecycled &&
bitmap.width >= reqWidth &&
bitmap.height >= reqHeight) {
return bitmap
}
}
}
}
// 2. 从LRU缓存中查找(需要满足inBitmap的条件)
memoryCache.snapshot().forEach { (_, bitmap) ->
if (!bitmap.isRecycled &&
bitmap.isMutable &&
bitmap.config != Bitmap.Config.HARDWARE &&
bitmap.width >= reqWidth &&
bitmap.height >= reqHeight) {
return bitmap
}
}
return null
}
/**
* 清理所有缓存
*/
fun clearCache() {
memoryCache.evictAll()
sizeCache.values.forEach { pool ->
synchronized(pool) {
pool.forEach { it.recycle() }
pool.clear()
}
}
sizeCache.clear()
// 建议系统进行垃圾回收
System.gc()
}
/**
* 移除特定缓存
*/
fun removeCache(key: String) {
memoryCache.remove(key)
}
/**
* 获取缓存统计信息
*/
fun getCacheStats(): String {
val hitCount = memoryCache.hitCount()
val missCount = memoryCache.missCount()
val hitRate = if (hitCount + missCount > 0) {
hitCount.toFloat() / (hitCount + missCount) * 100
} else {
0f
}
val totalSize = memoryCache.size()
val maxSize = memoryCache.maxSize()
val poolSizes = sizeCache.values.sumOf { it.size }
return """
内存缓存: ${totalSize}KB / ${maxSize}KB
命中率: ${"%.1f".format(hitRate)}% (${hitCount}/${hitCount + missCount})
尺寸缓存池: ${poolSizes}个
""".trimIndent()
}
/**
* 资源提供者(避免传递Context)
*/
object ResourcesProvider {
lateinit var resources: android.content.res.Resources
lateinit var context: android.content.Context
fun initialize(context: android.content.Context) {
this.context = context
this.resources = context.resources
}
}
}
4.3 FrameRateTracker.kt
kotlin
package com.example.performance.utils
import android.os.Handler
import android.os.Looper
import android.view.Choreographer
class FrameRateTracker {
interface FrameRateListener {
fun onFrameRateUpdate(fps: Float, droppedFrames: Int)
fun onJankDetected(frameTimeMs: Long)
}
private var choreographer: Choreographer? = null
private var frameCallback: Choreographer.FrameCallback? = null
private var isTracking = false
private var frameCount = 0
private var lastFrameTime = 0L
private var startTime = 0L
private var droppedFrames = 0
private val frameTimes = mutableListOf<Long>()
private var listener: FrameRateListener? = null
private val handler = Handler(Looper.getMainLooper())
private val reportRunnable = Runnable { reportFrameRate() }
// 配置
var reportInterval = 1000L // 报告间隔(毫秒)
var jankThreshold = 16666667L // 卡顿阈值(纳秒),16.67ms
fun startTracking() {
if (isTracking) return
isTracking = true
frameCount = 0
droppedFrames = 0
lastFrameTime = 0
startTime = System.nanoTime()
frameTimes.clear()
choreographer = Choreographer.getInstance()
frameCallback = object : Choreographer.FrameCallback {
override fun doFrame(frameTimeNanos: Long) {
onFrame(frameTimeNanos)
choreographer?.postFrameCallback(this)
}
}
choreographer?.postFrameCallback(frameCallback!!)
handler.postDelayed(reportRunnable, reportInterval)
android.util.Log.d("FrameRateTracker", "Started tracking")
}
fun stopTracking() {
if (!isTracking) return
isTracking = false
frameCallback?.let { choreographer?.removeFrameCallback(it) }
handler.removeCallbacks(reportRunnable)
frameCallback = null
choreographer = null
reportFrameRate() // 最终报告
android.util.Log.d("FrameRateTracker", "Stopped tracking")
}
private fun onFrame(frameTimeNanos: Long) {
if (lastFrameTime > 0) {
val frameTime = frameTimeNanos - lastFrameTime
frameTimes.add(frameTime)
// 检测掉帧
if (frameTime > jankThreshold * 1.5) {
val dropped = ((frameTime - jankThreshold) / jankThreshold).toInt()
droppedFrames += dropped.coerceAtLeast(1)
// 报告卡顿
if (frameTime > jankThreshold * 2) {
listener?.onJankDetected(frameTime / 1_000_000)
}
}
}
lastFrameTime = frameTimeNanos
frameCount++
}
private fun reportFrameRate() {
if (!isTracking) return
val elapsed = (System.nanoTime() - startTime) / 1_000_000_000.0
val fps = if (elapsed > 0) frameCount / elapsed else 0.0
// 计算帧时间统计数据
val stats = calculateFrameTimeStats()
listener?.onFrameRateUpdate(fps.toFloat(), droppedFrames)
// 重置计数器
frameCount = 0
droppedFrames = 0
startTime = System.nanoTime()
frameTimes.clear()
// 安排下一次报告
handler.postDelayed(reportRunnable, reportInterval)
}
private fun calculateFrameTimeStats(): FrameTimeStats {
if (frameTimes.isEmpty()) return FrameTimeStats()
val sorted = frameTimes.sorted()
val average = frameTimes.average()
val p50 = sorted[sorted.size / 2]
val p90 = sorted[(sorted.size * 0.9).toInt()]
val p95 = sorted[(sorted.size * 0.95).toInt()]
val p99 = sorted[(sorted.size * 0.99).toInt()]
return FrameTimeStats(
average = average,
p50 = p50,
p90 = p90,
p95 = p95,
p99 = p99
)
}
fun setFrameRateListener(listener: FrameRateListener) {
this.listener = listener
}
data class FrameTimeStats(
val average: Double = 0.0,
val p50: Long = 0,
val p90: Long = 0,
val p95: Long = 0,
val p99: Long = 0
) {
override fun toString(): String {
return """
Average: ${(average / 1_000_000).format(2)}ms
P50: ${(p50 / 1_000_000).format(2)}ms
P90: ${(p90 / 1_000_000).format(2)}ms
P95: ${(p95 / 1_000_000).format(2)}ms
P99: ${(p99 / 1_000_000).format(2)}ms
""".trimIndent()
}
private fun Double.format(digits: Int) = "%.${digits}f".format(this)
private fun Long.format(digits: Int) = "%.${digits}f".format(this / 1_000_000.0)
}
}
5. 性能测试页面
5.1 BenchmarkActivity.kt
kotlin
package com.example.performance.performance
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.example.performance.R
import com.example.performance.utils.FrameRateTracker
import com.example.performance.utils.PerformanceMonitor
import kotlin.math.roundToInt
class BenchmarkActivity : AppCompatActivity(), FrameRateTracker.FrameRateListener {
private lateinit var tvResults: TextView
private lateinit var btnStartTest: Button
private lateinit var btnStopTest: Button
private val frameRateTracker = FrameRateTracker()
private val handler = Handler(Looper.getMainLooper())
private var isTesting = false
private var testStartTime = 0L
// 测试结果
private val fpsHistory = mutableListOf<Float>()
private val memoryHistory = mutableListOf<Float>()
private var totalJanks = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_benchmark)
initViews()
setupFrameRateTracker()
}
private fun initViews() {
tvResults = findViewById(R.id.tvResults)
btnStartTest = findViewById(R.id.btnStartTest)
btnStopTest = findViewById(R.id.btnStopTest)
btnStartTest.setOnClickListener { startBenchmark() }
btnStopTest.setOnClickListener { stopBenchmark() }
// 初始化PerformanceMonitor
PerformanceMonitor.ResourcesProvider.initialize(this)
PerformanceMonitor.startMonitoring(this)
}
private fun setupFrameRateTracker() {
frameRateTracker.reportInterval = 500L
frameRateTracker.jankThreshold = 16666667L // 60fps阈值
frameRateTracker.setFrameRateListener(this)
}
@SuppressLint("SetTextI18n")
private fun startBenchmark() {
if (isTesting) return
isTesting = true
testStartTime = System.currentTimeMillis()
fpsHistory.clear()
memoryHistory.clear()
totalJanks = 0
frameRateTracker.startTracking()
// 开始收集性能数据
handler.post(object : Runnable {
override fun run() {
if (isTesting) {
val info = PerformanceMonitor.getPerformanceInfo()
memoryHistory.add(info.usedMemoryMB)
// 更新UI
updateResults()
handler.postDelayed(this, 1000)
}
}
})
btnStartTest.isEnabled = false
btnStopTest.isEnabled = true
tvResults.text = "性能测试开始...\n"
}
private fun stopBenchmark() {
if (!isTesting) return
isTesting = false
frameRateTracker.stopTracking()
btnStartTest.isEnabled = true
btnStopTest.isEnabled = false
// 生成最终报告
generateFinalReport()
}
@SuppressLint("SetTextI18n")
private fun updateResults() {
val duration = (System.currentTimeMillis() - testStartTime) / 1000
val currentFps = fpsHistory.lastOrNull() ?: 0f
val currentMemory = memoryHistory.lastOrNull() ?: 0f
val results = """
测试时间: ${duration}秒
当前FPS: ${"%.1f".format(currentFps)}
当前内存: ${"%.1f".format(currentMemory)}MB
卡顿次数: ${totalJanks}
平均FPS: ${calculateAverageFps()}
最大内存: ${"%.1f".format(memoryHistory.maxOrNull() ?: 0f)}MB
""".trimIndent()
tvResults.text = results
}
private fun calculateAverageFps(): String {
return if (fpsHistory.isNotEmpty()) {
"%.1f".format(fpsHistory.average())
} else {
"0.0"
}
}
@SuppressLint("SetTextI18n")
private fun generateFinalReport() {
if (fpsHistory.isEmpty() || memoryHistory.isEmpty()) return
val avgFps = fpsHistory.average()
val minFps = fpsHistory.minOrNull() ?: 0f
val maxFps = fpsHistory.maxOrNull() ?: 0f
val avgMemory = memoryHistory.average()
val minMemory = memoryHistory.minOrNull() ?: 0f
val maxMemory = memoryHistory.maxOrNull() ?: 0f
val duration = (System.currentTimeMillis() - testStartTime) / 1000
val report = """
====== 性能测试报告 ======
测试时长: ${duration}秒
🎯 帧率表现:
平均FPS: ${"%.1f".format(avgFps)}
最低FPS: ${"%.1f".format(minFps)}
最高FPS: ${"%.1f".format(maxFps)}
卡顿次数: ${totalJanks}
💾 内存表现:
平均内存: ${"%.1f".format(avgMemory)}MB
最低内存: ${"%.1f".format(minMemory)}MB
最高内存: ${"%.1f".format(maxMemory)}MB
📊 性能评级:
${getPerformanceRating(avgFps, totalJanks)}
💡 优化建议:
${getOptimizationSuggestions(avgFps, totalJanks, maxMemory)}
""".trimIndent()
tvResults.text = report
}
private fun getPerformanceRating(avgFps: Double, janks: Int): String {
return when {
avgFps >= 55 && janks < 5 -> "✅ 优秀 (流畅)"
avgFps >= 45 && janks < 10 -> "⚠️ 良好 (基本流畅)"
avgFps >= 30 && janks < 20 -> "⚠️ 一般 (偶有卡顿)"
else -> "❌ 较差 (需要优化)"
}
}
private fun getOptimizationSuggestions(
avgFps: Double,
janks: Int,
maxMemory: Float
): String {
val suggestions = mutableListOf<String>()
if (avgFps < 45) {
suggestions.add("- 检查过度绘制,减少不必要的View层级")
suggestions.add("- 启用硬件加速,优化绘制操作")
suggestions.add("- 使用异步绘制或RenderThread")
}
if (janks > 10) {
suggestions.add("- 避免在主线程进行耗时操作")
suggestions.add("- 优化动画,使用ValueAnimator替代Thread")
suggestions.add("- 减少每帧的绘制内容")
}
if (maxMemory > 100) {
suggestions.add("- 优化Bitmap内存使用")
suggestions.add("- 及时回收不再使用的资源")
suggestions.add("- 使用内存缓存和对象池")
}
if (suggestions.isEmpty()) {
suggestions.add("✅ 当前性能表现良好,继续保持!")
}
return suggestions.joinToString("\n")
}
// FrameRateListener实现
override fun onFrameRateUpdate(fps: Float, droppedFrames: Int) {
fpsHistory.add(fps)
totalJanks += droppedFrames
}
override fun onJankDetected(frameTimeMs: Long) {
// 记录详细卡顿信息
android.util.Log.w("Benchmark", "Jank detected: ${frameTimeMs}ms")
}
override fun onDestroy() {
super.onDestroy()
stopBenchmark()
PerformanceMonitor.stopMonitoring()
}
}
5.2 PerformancePanel.kt (自定义性能面板View)
kotlin
package com.example.performance.performance
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import com.example.performance.utils.PerformanceMonitor
import kotlin.math.max
import kotlin.math.min
class PerformancePanel @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
// 绘制配置
private val fpsPaint = Paint().apply {
color = Color.WHITE
textSize = 32f
typeface = Typeface.MONOSPACE
isAntiAlias = true
}
private val memoryPaint = Paint().apply {
color = Color.parseColor("#4CAF50")
style = Paint.Style.FILL
isAntiAlias = true
}
private val cpuPaint = Paint().apply {
color = Color.parseColor("#2196F3")
style = Paint.Style.FILL
isAntiAlias = true
}
private val gpuPaint = Paint().apply {
color = Color.parseColor("#FF9800")
style = Paint.Style.FILL
isAntiAlias = true
}
private val gridPaint = Paint().apply {
color = Color.parseColor("#444444")
style = Paint.Style.STROKE
strokeWidth = 1f
isAntiAlias = true
}
private val bgPaint = Paint().apply {
color = Color.parseColor("#22000000")
style = Paint.Style.FILL
isAntiAlias = true
}
// 数据存储
private val fpsHistory = FloatArray(100)
private val memoryHistory = FloatArray(100)
private val cpuHistory = FloatArray(100)
private val gpuHistory = FloatArray(100)
private var historyIndex = 0
private var historySize = 0
// 性能阈值
private val fpsThresholds = arrayOf(30f, 45f, 55f)
private val memoryThresholds = arrayOf(50f, 100f, 200f)
private val cpuThresholds = arrayOf(30f, 60f, 80f)
private val gpuThresholds = arrayOf(8.3f, 16.6f, 33.3f) // ms
init {
// 注册性能监听
PerformanceMonitor.onPerformanceUpdate = { info ->
synchronized(this) {
fpsHistory[historyIndex] = info.fps
memoryHistory[historyIndex] = info.usedMemoryMB
cpuHistory[historyIndex] = info.cpuUsage
gpuHistory[historyIndex] = info.gpuTimeMs
historyIndex = (historyIndex + 1) % fpsHistory.size
historySize = min(historySize + 1, fpsHistory.size)
postInvalidate()
}
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 绘制背景
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), bgPaint)
// 绘制网格
drawGrid(canvas)
// 绘制性能图表
drawPerformanceCharts(canvas)
// 绘制实时数据
drawRealTimeData(canvas)
// 绘制性能评级
drawPerformanceRating(canvas)
}
private fun drawGrid(canvas: Canvas) {
val gridSize = 50f
// 垂直线
var x = 0f
while (x < width) {
canvas.drawLine(x, 0f, x, height.toFloat(), gridPaint)
x += gridSize
}
// 水平线
var y = 0f
while (y < height) {
canvas.drawLine(0f, y, width.toFloat(), y, gridPaint)
y += gridSize
}
}
private fun drawPerformanceCharts(canvas: Canvas) {
if (historySize < 2) return
val chartHeight = height * 0.6f
val chartTop = height * 0.1f
val chartBottom = chartTop + chartHeight
val chartWidth = width.toFloat()
val pointSpacing = chartWidth / (historySize - 1)
// 绘制FPS曲线
drawCurve(canvas, fpsHistory, historySize, pointSpacing, chartTop,
chartBottom, 0f, 120f, fpsPaint)
// 绘制内存曲线
drawCurve(canvas, memoryHistory, historySize, pointSpacing, chartTop,
chartBottom, 0f, 500f, memoryPaint)
// 绘制CPU曲线
drawCurve(canvas, cpuHistory, historySize, pointSpacing, chartTop,
chartBottom, 0f, 100f, cpuPaint)
// 绘制GPU曲线
drawCurve(canvas, gpuHistory, historySize, pointSpacing, chartTop,
chartBottom, 0f, 50f, gpuPaint)
}
private fun drawCurve(
canvas: Canvas,
data: FloatArray,
size: Int,
pointSpacing: Float,
top: Float,
bottom: Float,
minValue: Float,
maxValue: Float,
paint: Paint
) {
val path = Path()
val range = maxValue - minValue
val chartHeight = bottom - top
// 计算第一个点
var lastIndex = (historyIndex - size + data.size) % data.size
var lastValue = data[lastIndex]
var lastY = bottom - ((lastValue - minValue) / range) * chartHeight
path.moveTo(0f, lastY.coerceIn(top, bottom))
// 绘制曲线
for (i in 1 until size) {
val index = (lastIndex + 1) % data.size
val value = data[index]
val x = i * pointSpacing
val y = bottom - ((value - minValue) / range) * chartHeight
// 使用贝塞尔曲线平滑连接
val controlX = x - pointSpacing / 2
path.cubicTo(
controlX, lastY,
controlX, y,
x, y.coerceIn(top, bottom)
)
lastIndex = index
lastValue = value
lastY = y
}
canvas.drawPath(path, paint)
}
private fun drawRealTimeData(canvas: Canvas) {
val currentIndex = (historyIndex - 1 + fpsHistory.size) % fpsHistory.size
val fps = fpsHistory[currentIndex]
val memory = memoryHistory[currentIndex]
val cpu = cpuHistory[currentIndex]
val gpu = gpuHistory[currentIndex]
val textX = 20f
var textY = 50f
val lineHeight = 35f
// FPS
fpsPaint.color = getColorForValue(fps, fpsThresholds,
arrayOf(Color.GREEN, Color.YELLOW, Color.RED))
canvas.drawText("FPS: ${"%.1f".format(fps)}", textX, textY, fpsPaint)
// 内存
textY += lineHeight
fpsPaint.color = getColorForValue(memory, memoryThresholds,
arrayOf(Color.GREEN, Color.YELLOW, Color.RED))
canvas.drawText("内存: ${"%.1f".format(memory)}MB", textX, textY, fpsPaint)
// CPU
textY += lineHeight
fpsPaint.color = getColorForValue(cpu, cpuThresholds,
arrayOf(Color.GREEN, Color.YELLOW, Color.RED))
canvas.drawText("CPU: ${"%.1f".format(cpu)}%", textX, textY, fpsPaint)
// GPU
textY += lineHeight
fpsPaint.color = getColorForValue(gpu, gpuThresholds,
arrayOf(Color.GREEN, Color.YELLOW, Color.RED))
canvas.drawText("GPU: ${"%.1f".format(gpu)}ms", textX, textY, fpsPaint)
}
private fun drawPerformanceRating(canvas: Canvas) {
val currentIndex = (historyIndex - 1 + fpsHistory.size) % fpsHistory.size
val fps = fpsHistory[currentIndex]
val memory = memoryHistory[currentIndex]
val cpu = cpuHistory[currentIndex]
val rating = calculatePerformanceRating(fps, memory, cpu)
val ratingPaint = Paint().apply {
color = rating.color
textSize = 24f
typeface = Typeface.DEFAULT_BOLD
isAntiAlias = true
}
val text = "性能评级: ${rating.text}"
val textWidth = ratingPaint.measureText(text)
val x = width - textWidth - 20
val y = 50f
canvas.drawText(text, x, y, ratingPaint)
}
private fun getColorForValue(
value: Float,
thresholds: Array<Float>,
colors: Array<Int>
): Int {
return when {
value < thresholds[0] -> colors[0]
value < thresholds[1] -> colors[1]
value < thresholds[2] -> colors[1]
else -> colors[2]
}
}
private fun calculatePerformanceRating(
fps: Float,
memory: Float,
cpu: Float
): PerformanceRating {
var score = 0
// FPS评分
score += when {
fps >= 55 -> 3
fps >= 45 -> 2
fps >= 30 -> 1
else -> 0
}
// 内存评分
score += when {
memory < 50 -> 3
memory < 100 -> 2
memory < 200 -> 1
else -> 0
}
// CPU评分
score += when {
cpu < 30 -> 3
cpu < 60 -> 2
cpu < 80 -> 1
else -> 0
}
return when (score) {
in 8..9 -> PerformanceRating("优秀", Color.GREEN)
in 5..7 -> PerformanceRating("良好", Color.YELLOW)
in 3..4 -> PerformanceRating("一般", Color.parseColor("#FF9800"))
else -> PerformanceRating("较差", Color.RED)
}
}
data class PerformanceRating(val text: String, val color: Int)
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
PerformanceMonitor.onPerformanceUpdate = null
}
}
6. 项目配置文件
6.1 build.gradle.kts (模块级)
kotlin
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "com.example.performance"
compileSdk = 34
defaultConfig {
applicationId = "com.example.performance"
minSdk = 21
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
// 启用多Dex(如果方法数超过限制)
multiDexEnabled = true
}
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
// 启用R8完整模式
isDebuggable = false
isJniDebuggable = false
isRenderscriptDebuggable = false
}
debug {
// 启用调试功能
isDebuggable = true
isJniDebuggable = true
isRenderscriptDebuggable = true
// 调试版本不进行代码优化,便于性能分析
isMinifyEnabled = false
isShrinkResources = false
// 添加调试配置
applicationIdSuffix = ".debug"
versionNameSuffix = "-DEBUG"
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding = true
buildConfig = true
}
// 启用数据绑定
dataBinding {
enable = true
}
// 开启lint检查但不在构建时失败
lint {
abortOnError = false
warningsAsErrors = true
}
}
dependencies {
// AndroidX Core
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
// Material Design
implementation("com.google.android.material:material:1.11.0")
// Constraint Layout
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
// CardView
implementation("androidx.cardview:cardview:1.0.0")
// RecyclerView
implementation("androidx.recyclerview:recyclerview:1.3.2")
// Lifecycle
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
// 多Dex支持
implementation("androidx.multidex:multidex:2.0.1")
// 测试依赖
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
// 性能监控工具
debugImplementation("androidx.profileinstaller:profileinstaller:1.3.1")
}
6.2 AndroidManifest.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- 调试权限 -->
<uses-permission android:name="android.permission.DUMP" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<application
android:name=".PerformanceApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PerformanceDemo"
android:hardwareAccelerated="true"
android:largeHeap="true"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:configChanges="orientation|screenSize|keyboardHidden"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".performance.BenchmarkActivity"
android:exported="false"
android:theme="@style/Theme.PerformanceDemo.NoActionBar" />
<meta-data
android:name="android.max_aspect"
android:value="2.1" />
<meta-data
android:name="android.notch_support"
android:value="true" />
<!-- 启用Profileable(Android 10+) -->
<profileable
android:shell="true"
tools:targetApi="q" />
</application>
</manifest>
6.3 PerformanceApplication.kt
kotlin
package com.example.performance
import android.app.Application
import android.os.StrictMode
import com.example.performance.utils.BitmapManager
import com.example.performance.utils.PerformanceMonitor
class PerformanceApplication : Application() {
override fun onCreate() {
super.onCreate()
// 初始化BitmapManager
BitmapManager.ResourcesProvider.initialize(this)
// 开发模式下启用严格模式检测
if (BuildConfig.DEBUG) {
enableStrictMode()
}
// 初始化性能监控
initializePerformanceMonitoring()
}
private fun enableStrictMode() {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectAll() // 检测所有违规
.penaltyLog() // 记录日志
.penaltyDeathOnNetwork() // 禁止网络在主线程
.penaltyFlashScreen() // 屏幕闪烁提示
.build()
)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.detectLeakedClosableObjects() // 检测未关闭的对象
.detectLeakedRegistrationObjects() // 检测未取消的注册
.detectLeakedSqlLiteObjects() // 检测SQLite泄漏
.build()
)
}
private fun initializePerformanceMonitoring() {
// 可以在Application级别启动全局性能监控
if (BuildConfig.DEBUG) {
// 开发模式下启动基础监控
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
// 记录未捕获的异常
android.util.Log.e("PerformanceApp",
"Uncaught exception in thread: ${thread.name}",
throwable)
// 调用默认处理器
Thread.getDefaultUncaughtExceptionHandler()?.uncaughtException(thread, throwable)
}
}
}
override fun onLowMemory() {
super.onLowMemory()
// 内存不足时清理缓存
BitmapManager.clearCache()
// 通知所有Activity
android.util.Log.w("PerformanceApp", "Low memory warning, clearing caches")
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
// 根据Trim级别清理资源
when (level) {
ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE,
ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> {
// 清理部分缓存
BitmapManager.clearCache()
}
ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL,
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {
// 清理所有可能的内存
BitmapManager.clearCache()
System.gc()
}
}
android.util.Log.i("PerformanceApp", "Trim memory level: $level")
}
}
性能优化要点
1. 绘制优化
-
✅ 启用硬件加速 (setLayerType(LAYER_TYPE_HARDWARE, null))
-
✅ 避免在onDraw中创建对象
-
✅ 使用局部刷新 (invalidate(left, top, right, bottom))
-
✅ 预计算路径和矩形
-
✅ 复用Paint和Shader对象
2. 内存优化
-
✅ 使用LRU缓存管理Bitmap
-
✅ 实现Bitmap复用池
-
✅ 及时回收不再使用的资源
-
✅ 使用对象池避免频繁GC
3. 动画优化
-
✅ 使用ValueAnimator替代Thread
-
✅ 高刷新率设备适配
-
✅ 使用Choreographer同步Vsync
-
✅ 动画插值器优化
4. 异步优化
-
✅ 使用RenderThread异步绘制
-
✅ 离屏渲染缓存
-
✅ 后台线程计算,主线程更新
常见问题排查
问题1: 硬件加速导致崩溃
kotlin
// 解决方案:检查不支持硬件加速的操作
override fun onDraw(canvas: Canvas) {
if (canvas.isHardwareAccelerated) {
// 使用硬件加速绘制路径
drawHardware(canvas)
} else {
// 软件回退
drawSoftware(canvas)
}
}
问题2: 内存泄漏
kotlin
// 在View销毁时清理资源
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
animator?.cancel()
bitmap?.recycle()
handler.removeCallbacksAndMessages(null)
}
问题3: 高刷新率设备掉帧
kotlin
// 适配不同刷新率
val display = context.display
val refreshRate = display?.refreshRate ?: 60f
val frameTime = 1000f / refreshRate
// 根据刷新率调整动画时长
animator.duration = (frameTime * 60).toLong()
这个完整的Demo项目展示了自定义View性能优化的完整解决方案,从问题定位到优化实现,再到性能监控和测试,提供了全方位的性能优化参考。