17 KiB
17 KiB
性能优化最佳实践
性能优化是Android应用开发的重要环节,直接影响用户体验和应用质量。本文档介绍Android应用性能优化的最佳实践。
目录
性能优化策略
1. 性能优化原则
测量优先
// ✅ 好的做法:先测量,再优化
class PerformanceMonitor {
fun measureTime(block: () -> Unit): Long {
val startTime = System.currentTimeMillis()
block()
return System.currentTimeMillis() - startTime
}
fun measureMemory(block: () -> Unit): Long {
val runtime = Runtime.getRuntime()
val beforeMemory = runtime.totalMemory() - runtime.freeMemory()
block()
val afterMemory = runtime.totalMemory() - runtime.freeMemory()
return afterMemory - beforeMemory
}
}
// 使用
val monitor = PerformanceMonitor()
val time = monitor.measureTime {
// 执行代码
}
Log.d("Performance", "执行时间: ${time}ms")
优化关键路径
// 识别关键路径并优化
class AppInitializer {
fun initialize() {
// 关键路径:必须立即初始化
initCriticalComponents()
// 非关键路径:可以延迟初始化
Handler(Looper.getMainLooper()).postDelayed({
initNonCriticalComponents()
}, 100)
}
}
2. 性能指标
关键指标
// 启动时间
class StartupTimeTracker {
private var appStartTime: Long = 0
fun onAppStart() {
appStartTime = System.currentTimeMillis()
}
fun onFirstFrame() {
val startupTime = System.currentTimeMillis() - appStartTime
Log.d("Performance", "启动时间: ${startupTime}ms")
}
}
// 内存使用
class MemoryTracker {
fun getMemoryUsage(): MemoryInfo {
val runtime = Runtime.getRuntime()
val totalMemory = runtime.totalMemory()
val freeMemory = runtime.freeMemory()
val usedMemory = totalMemory - freeMemory
return MemoryInfo(
total = totalMemory,
used = usedMemory,
free = freeMemory
)
}
}
启动优化实践
1. Application优化
延迟初始化
// ✅ 好的做法:延迟初始化非关键组件
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 关键初始化:必须立即执行
initCriticalComponents()
// 非关键初始化:延迟执行
initNonCriticalComponentsAsync()
}
private fun initCriticalComponents() {
// 初始化关键组件
CrashReporting.init(this)
}
private fun initNonCriticalComponentsAsync() {
// 使用后台线程初始化
Thread {
// 初始化非关键组件
Analytics.init(this)
ImageLoader.init(this)
}.start()
}
}
使用App Startup
// 使用App Startup统一管理初始化
class MyInitializer : Initializer<Unit> {
override fun create(context: Context) {
// 初始化组件
Analytics.init(context)
}
override fun dependencies(): List<Class<out Initializer<*>>> {
// 定义依赖关系
return listOf(OtherInitializer::class.java)
}
}
// 在AndroidManifest.xml中注册
// <provider
// android:name="androidx.startup.InitializationProvider"
// android:authorities="${applicationId}.androidx-startup"
// android:exported="false"
// tools:node="merge">
// <meta-data
// android:name="com.example.MyInitializer"
// android:value="androidx.startup" />
// </provider>
2. 首屏优化
减少首屏布局复杂度
<!-- ✅ 好的做法:简化首屏布局 -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 只显示关键内容 -->
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="欢迎"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- ❌ 不好的做法:首屏包含过多内容 -->
使用ViewStub延迟加载
<!-- 使用ViewStub延迟加载非关键视图 -->
<ViewStub
android:id="@+id/viewStub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/expensive_view"/>
// 在需要时加载
class MainActivity : AppCompatActivity() {
private lateinit var viewStub: ViewStub
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewStub = findViewById(R.id.viewStub)
// 延迟加载
button.setOnClickListener {
if (viewStub.parent != null) {
viewStub.inflate()
}
}
}
}
3. 启动时间测量
使用Trace API
// 使用Trace API标记启动流程
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Trace.beginSection("MainActivity.onCreate")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Trace.beginSection("initViews")
initViews()
Trace.endSection()
Trace.beginSection("loadData")
loadData()
Trace.endSection()
Trace.endSection()
}
}
// 使用Systrace分析
// python systrace.py -t 10 -o trace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res
内存优化实践
1. 内存泄漏预防
避免静态引用
// ❌ 不好的做法:静态引用Context
object AppContext {
var context: Context? = null // 可能导致内存泄漏
}
// ✅ 好的做法:使用Application Context
object AppContext {
fun getContext(): Context {
return MyApplication.instance
}
}
正确处理生命周期
// ✅ 好的做法:正确处理生命周期
class MainActivity : AppCompatActivity() {
private var handler: Handler? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 使用静态内部类+WeakReference
handler = MyHandler(this)
}
override fun onDestroy() {
super.onDestroy()
handler?.removeCallbacksAndMessages(null)
handler = null
}
private class MyHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
private val activityRef = WeakReference(activity)
override fun handleMessage(msg: Message) {
activityRef.get()?.let {
// 处理消息
}
}
}
}
2. 内存使用优化
使用对象池
// 对象池:复用对象,减少GC
class ObjectPool<T>(private val factory: () -> T, private val maxSize: Int = 10) {
private val pool = mutableListOf<T>()
fun acquire(): T {
return if (pool.isNotEmpty()) {
pool.removeAt(pool.size - 1)
} else {
factory()
}
}
fun release(obj: T) {
if (pool.size < maxSize) {
pool.add(obj)
}
}
}
// 使用
val viewPool = ObjectPool({ View(context) })
val view = viewPool.acquire()
// 使用view
viewPool.release(view)
优化图片加载
// 使用Glide等图片加载库
Glide.with(context)
.load(imageUrl)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.override(800, 600) // 指定尺寸
.into(imageView)
// 使用WebP格式
// WebP格式比PNG/JPG更小,加载更快
// 使用图片压缩
fun compressBitmap(bitmap: Bitmap, quality: Int = 80): ByteArray {
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.WEBP, quality, stream)
return stream.toByteArray()
}
3. 内存监控
使用LeakCanary
// build.gradle
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
// 自动检测内存泄漏
// LeakCanary会自动检测并报告内存泄漏
手动内存分析
// 获取内存信息
class MemoryAnalyzer {
fun getMemoryInfo(): String {
val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory()
val totalMemory = runtime.totalMemory()
val freeMemory = runtime.freeMemory()
val usedMemory = totalMemory - freeMemory
return """
Max Memory: ${maxMemory / 1024 / 1024}MB
Total Memory: ${totalMemory / 1024 / 1024}MB
Used Memory: ${usedMemory / 1024 / 1024}MB
Free Memory: ${freeMemory / 1024 / 1024}MB
""".trimIndent()
}
fun dumpHeap(): File {
val heapDumpFile = File(cacheDir, "heap_dump.hprof")
Debug.dumpHprofData(heapDumpFile.absolutePath)
return heapDumpFile
}
}
布局优化实践
1. 减少布局层级
使用ConstraintLayout
<!-- ✅ 好的做法:使用ConstraintLayout减少层级 -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标题"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="副标题"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- ❌ 不好的做法:嵌套过多 -->
<LinearLayout>
<LinearLayout>
<TextView/>
</LinearLayout>
</LinearLayout>
2. 避免过度绘制
减少背景绘制
<!-- ✅ 好的做法:移除不必要的背景 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white">
<!-- 子视图不需要再设置背景 -->
</LinearLayout>
<!-- ❌ 不好的做法:多层背景 -->
<LinearLayout android:background="@color/background1">
<LinearLayout android:background="@color/background2">
<!-- 导致过度绘制 -->
</LinearLayout>
</LinearLayout>
使用clipToPadding
<!-- 使用clipToPadding减少绘制区域 -->
<RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="16dp"/>
3. RecyclerView优化
ViewHolder复用
// ✅ 好的做法:正确实现ViewHolder
class UserAdapter : RecyclerView.Adapter<UserAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val nameTextView: TextView = itemView.findViewById(R.id.name)
val emailTextView: TextView = itemView.findViewById(R.id.email)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_user, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val user = users[position]
holder.nameTextView.text = user.name
holder.emailTextView.text = user.email
}
}
使用DiffUtil
// 使用DiffUtil优化列表更新
class UserDiffCallback(
private val oldList: List<User>,
private val newList: List<User>
) : DiffUtil.Callback() {
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
// 使用
val diffResult = DiffUtil.calculateDiff(UserDiffCallback(oldList, newList))
adapter.users = newList
diffResult.dispatchUpdatesTo(adapter)
网络优化实践
1. 请求优化
请求合并
// ✅ 好的做法:合并请求
class ApiService {
@GET("users")
suspend fun getUsers(@Query("ids") ids: String): List<User>
// 一次请求获取多个用户,而不是多次请求
}
// ❌ 不好的做法:多次请求
// for (id in userIds) {
// apiService.getUser(id)
// }
使用缓存
// 使用OkHttp缓存
val client = OkHttpClient.Builder()
.cache(Cache(cacheDir, 10 * 1024 * 1024)) // 10MB缓存
.build()
// 使用Retrofit缓存
@Headers("Cache-Control: max-age=3600")
@GET("users/{id}")
suspend fun getUser(@Path("id") id: String): User
2. 数据压缩
使用Gzip
// 服务器端启用Gzip压缩
// 客户端自动处理Gzip响应
// OkHttp自动支持Gzip解压
数据格式优化
// 使用Protobuf替代JSON
// Protobuf更小、更快
data class User(
val id: String,
val name: String,
val email: String
)
// 转换为Protobuf
val userProto = UserProto.newBuilder()
.setId(user.id)
.setName(user.name)
.setEmail(user.email)
.build()
3. 网络监控
监控网络请求
// 使用OkHttp Interceptor监控
class NetworkMonitorInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val startTime = System.currentTimeMillis()
val response = chain.proceed(request)
val duration = System.currentTimeMillis() - startTime
Log.d("Network", "${request.url} - ${duration}ms")
return response
}
}
val client = OkHttpClient.Builder()
.addInterceptor(NetworkMonitorInterceptor())
.build()
性能监控
1. 性能指标收集
使用Firebase Performance
// build.gradle
dependencies {
implementation 'com.google.firebase:firebase-perf-ktx:20.4.1'
}
// 监控自定义性能指标
val trace = FirebasePerformance.getInstance().newTrace("load_user_data")
trace.start()
// 执行操作
loadUserData()
trace.stop()
自定义性能监控
class PerformanceTracker {
private val metrics = mutableMapOf<String, Long>()
fun startTrace(name: String) {
metrics[name] = System.currentTimeMillis()
}
fun endTrace(name: String) {
val startTime = metrics[name]
if (startTime != null) {
val duration = System.currentTimeMillis() - startTime
Log.d("Performance", "$name: ${duration}ms")
metrics.remove(name)
}
}
}
// 使用
val tracker = PerformanceTracker()
tracker.startTrace("load_data")
// 执行操作
tracker.endTrace("load_data")
2. 性能分析工具
使用Android Profiler
// Android Studio Profiler可以监控:
// - CPU使用率
// - 内存使用情况
// - 网络请求
// - 电量消耗
使用Systrace
# 使用Systrace分析性能
python systrace.py -t 10 -o trace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res
# 在代码中标记
Trace.beginSection("my_section")
// 执行代码
Trace.endSection()
使用Perfetto
// Perfetto是更强大的性能分析工具
// 可以分析:
// - CPU调度
// - 内存分配
// - 文件I/O
// - 网络活动
总结
性能优化是一个持续的过程,需要:
- 测量优先:先测量性能,找出瓶颈
- 优化关键路径:优先优化影响用户体验的关键路径
- 启动优化:减少启动时间,提升用户体验
- 内存优化:避免内存泄漏,优化内存使用
- 布局优化:减少布局层级,避免过度绘制
- 网络优化:优化网络请求,使用缓存
- 性能监控:持续监控性能指标,及时发现问题
通过遵循这些最佳实践,可以显著提升应用的性能和用户体验。