717 lines
17 KiB
Markdown
717 lines
17 KiB
Markdown
# 性能优化最佳实践
|
||
|
||
性能优化是Android应用开发的重要环节,直接影响用户体验和应用质量。本文档介绍Android应用性能优化的最佳实践。
|
||
|
||
## 目录
|
||
|
||
- [性能优化策略](#性能优化策略)
|
||
- [启动优化实践](#启动优化实践)
|
||
- [内存优化实践](#内存优化实践)
|
||
- [布局优化实践](#布局优化实践)
|
||
- [网络优化实践](#网络优化实践)
|
||
- [性能监控](#性能监控)
|
||
|
||
---
|
||
|
||
## 性能优化策略
|
||
|
||
### 1. 性能优化原则
|
||
|
||
#### 测量优先
|
||
|
||
```kotlin
|
||
// ✅ 好的做法:先测量,再优化
|
||
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")
|
||
```
|
||
|
||
#### 优化关键路径
|
||
|
||
```kotlin
|
||
// 识别关键路径并优化
|
||
class AppInitializer {
|
||
fun initialize() {
|
||
// 关键路径:必须立即初始化
|
||
initCriticalComponents()
|
||
|
||
// 非关键路径:可以延迟初始化
|
||
Handler(Looper.getMainLooper()).postDelayed({
|
||
initNonCriticalComponents()
|
||
}, 100)
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 性能指标
|
||
|
||
#### 关键指标
|
||
|
||
```kotlin
|
||
// 启动时间
|
||
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优化
|
||
|
||
#### 延迟初始化
|
||
|
||
```kotlin
|
||
// ✅ 好的做法:延迟初始化非关键组件
|
||
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
|
||
|
||
```kotlin
|
||
// 使用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. 首屏优化
|
||
|
||
#### 减少首屏布局复杂度
|
||
|
||
```xml
|
||
<!-- ✅ 好的做法:简化首屏布局 -->
|
||
<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延迟加载
|
||
|
||
```xml
|
||
<!-- 使用ViewStub延迟加载非关键视图 -->
|
||
<ViewStub
|
||
android:id="@+id/viewStub"
|
||
android:layout_width="match_parent"
|
||
android:layout_height="wrap_content"
|
||
android:layout="@layout/expensive_view"/>
|
||
```
|
||
|
||
```kotlin
|
||
// 在需要时加载
|
||
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
|
||
|
||
```kotlin
|
||
// 使用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. 内存泄漏预防
|
||
|
||
#### 避免静态引用
|
||
|
||
```kotlin
|
||
// ❌ 不好的做法:静态引用Context
|
||
object AppContext {
|
||
var context: Context? = null // 可能导致内存泄漏
|
||
}
|
||
|
||
// ✅ 好的做法:使用Application Context
|
||
object AppContext {
|
||
fun getContext(): Context {
|
||
return MyApplication.instance
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 正确处理生命周期
|
||
|
||
```kotlin
|
||
// ✅ 好的做法:正确处理生命周期
|
||
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. 内存使用优化
|
||
|
||
#### 使用对象池
|
||
|
||
```kotlin
|
||
// 对象池:复用对象,减少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)
|
||
```
|
||
|
||
#### 优化图片加载
|
||
|
||
```kotlin
|
||
// 使用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
|
||
|
||
```gradle
|
||
// build.gradle
|
||
dependencies {
|
||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
|
||
}
|
||
```
|
||
|
||
```kotlin
|
||
// 自动检测内存泄漏
|
||
// LeakCanary会自动检测并报告内存泄漏
|
||
```
|
||
|
||
#### 手动内存分析
|
||
|
||
```kotlin
|
||
// 获取内存信息
|
||
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
|
||
|
||
```xml
|
||
<!-- ✅ 好的做法:使用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. 避免过度绘制
|
||
|
||
#### 减少背景绘制
|
||
|
||
```xml
|
||
<!-- ✅ 好的做法:移除不必要的背景 -->
|
||
<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
|
||
|
||
```xml
|
||
<!-- 使用clipToPadding减少绘制区域 -->
|
||
<RecyclerView
|
||
android:layout_width="match_parent"
|
||
android:layout_height="match_parent"
|
||
android:clipToPadding="false"
|
||
android:padding="16dp"/>
|
||
```
|
||
|
||
### 3. RecyclerView优化
|
||
|
||
#### ViewHolder复用
|
||
|
||
```kotlin
|
||
// ✅ 好的做法:正确实现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
|
||
|
||
```kotlin
|
||
// 使用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. 请求优化
|
||
|
||
#### 请求合并
|
||
|
||
```kotlin
|
||
// ✅ 好的做法:合并请求
|
||
class ApiService {
|
||
@GET("users")
|
||
suspend fun getUsers(@Query("ids") ids: String): List<User>
|
||
// 一次请求获取多个用户,而不是多次请求
|
||
}
|
||
|
||
// ❌ 不好的做法:多次请求
|
||
// for (id in userIds) {
|
||
// apiService.getUser(id)
|
||
// }
|
||
```
|
||
|
||
#### 使用缓存
|
||
|
||
```kotlin
|
||
// 使用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
|
||
|
||
```kotlin
|
||
// 服务器端启用Gzip压缩
|
||
// 客户端自动处理Gzip响应
|
||
// OkHttp自动支持Gzip解压
|
||
```
|
||
|
||
#### 数据格式优化
|
||
|
||
```kotlin
|
||
// 使用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. 网络监控
|
||
|
||
#### 监控网络请求
|
||
|
||
```kotlin
|
||
// 使用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
|
||
|
||
```gradle
|
||
// build.gradle
|
||
dependencies {
|
||
implementation 'com.google.firebase:firebase-perf-ktx:20.4.1'
|
||
}
|
||
```
|
||
|
||
```kotlin
|
||
// 监控自定义性能指标
|
||
val trace = FirebasePerformance.getInstance().newTrace("load_user_data")
|
||
trace.start()
|
||
|
||
// 执行操作
|
||
loadUserData()
|
||
|
||
trace.stop()
|
||
```
|
||
|
||
#### 自定义性能监控
|
||
|
||
```kotlin
|
||
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
|
||
|
||
```kotlin
|
||
// Android Studio Profiler可以监控:
|
||
// - CPU使用率
|
||
// - 内存使用情况
|
||
// - 网络请求
|
||
// - 电量消耗
|
||
```
|
||
|
||
#### 使用Systrace
|
||
|
||
```bash
|
||
# 使用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
|
||
|
||
```kotlin
|
||
// Perfetto是更强大的性能分析工具
|
||
// 可以分析:
|
||
// - CPU调度
|
||
// - 内存分配
|
||
// - 文件I/O
|
||
// - 网络活动
|
||
```
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
性能优化是一个持续的过程,需要:
|
||
|
||
1. **测量优先**:先测量性能,找出瓶颈
|
||
2. **优化关键路径**:优先优化影响用户体验的关键路径
|
||
3. **启动优化**:减少启动时间,提升用户体验
|
||
4. **内存优化**:避免内存泄漏,优化内存使用
|
||
5. **布局优化**:减少布局层级,避免过度绘制
|
||
6. **网络优化**:优化网络请求,使用缓存
|
||
7. **性能监控**:持续监控性能指标,及时发现问题
|
||
|
||
通过遵循这些最佳实践,可以显著提升应用的性能和用户体验。
|