RecyclerView卡顿通常由以下原因导致:
onBindViewHolder
执行耗时操作1. 使用DiffUtil进行增量更新 原理:DiffUtil通过比较新旧数据集差异,仅更新变化的Item,避免全局刷新。
// 定义DiffCallback
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(oldPos: Int, newPos: Int) =
oldList[oldPos].id == newList[newPos].id
override fun areContentsTheSame(oldPos: Int, newPos: Int) =
oldList[oldPos] == newList[newPos]
}
// 使用DiffResult更新Adapter
val diffResult = DiffUtil.calculateDiff(UserDiffCallback(oldUsers, newUsers))
diffResult.dispatchUpdatesTo(adapter)
adapter.submitList(newUsers) // 更新数据
2. 优化图片加载(以Glide为例)
Glide.with(context)
.load(imageUrl)
.override(300, 300) // 根据Item大小调整
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView)
3. 解决嵌套滑动冲突
NestedScrollView
:重写onInterceptTouchEvent
RecyclerView.setRecycledViewPool()
:共享子RecyclerView的缓存池// 父RecyclerView中设置共享池
val sharedPool = RecyclerView.RecycledViewPool()
childRecyclerView1.setRecycledViewPool(sharedPool)
childRecyclerView2.setRecycledViewPool(sharedPool)
内存泄漏常见场景:
1. 使用LeakCanary自动检测
集成步骤:
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
}
2. Handler内存泄漏解决方案
class SafeHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
private val weakActivity = WeakReference(activity)
override fun handleMessage(msg: Message) {
weakActivity.get()?.run {
// 操作UI
textView.text = "Updated"
}
}
}
// 在Activity中清理消息
override fun onDestroy() {
handler.removeCallbacksAndMessages(null)
super.onDestroy()
}
3. 匿名内部类泄漏案例
// 错误示例:匿名Runnable持有Activity引用
object : Runnable {
override fun run() {
// 直接访问Activity方法导致泄漏
updateUI()
}
}
// 正确做法:使用弱引用或分离生命周期
class SafeRunnable(activity: WeakReference<MainActivity>) : Runnable {
override fun run() {
activity.get()?.updateUI()
}
}
冷启动耗时主要分布在:
1. 异步初始化(使用协程)
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// 主线程只初始化必要组件
initCoreComponents()
// 异步初始化非关键组件
CoroutineScope(Dispatchers.IO).launch {
initAnalytics() // 如Firebase
initCrashReporting() // 如Sentry
}
}
}
2. 启动主题优化
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<activity
android:name=".MainActivity"
android:theme="@style/AppTheme.Launcher"> <!-- 启动时使用轻量主题 -->
</activity>
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme) // 恢复正常主题
super.onCreate(savedInstanceState)
}
3. 使用App Startup库集中初始化
// 定义Initializer
class FirebaseInitializer : Initializer<FirebaseApp> {
override fun create(context: Context): FirebaseApp {
return Firebase.initialize(context)
}
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
// 在AndroidManifest中配置
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup">
<meta-data
android:name="com.example.FirebaseInitializer"
android:value="androidx.startup" />
</provider>
1. 主线程耗时操作迁移到协程
// 错误示例:在主线程执行数据库查询
fun loadData() {
val data = database.query() // 阻塞主线程
updateUI(data)
}
// 正确做法:使用协程
fun loadData() {
CoroutineScope(Dispatchers.Main).launch {
val data = withContext(Dispatchers.IO) {
database.query()
}
updateUI(data)
}
}
2. 布局优化(ConstraintLayout替代多层嵌套)
<LinearLayout>
<LinearLayout>
<ImageView />
<TextView />
</LinearLayout>
<LinearLayout>...</LinearLayout>
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout>
<ImageView app:layout_constraintTop_toTopOf="parent" .../>
<TextView app:layout_constraintStart_toEndOf="@id/image" .../>
</androidx.constraintlayout.widget.ConstraintLayout>
3. 避免过度绘制
canvas.clipRect()
限制绘制区域1. 代码混淆与优化(R8)
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
2. 资源混淆与压缩(AndResGuard)
plugins {
id 'com.tencent.mm.andresguard'
}
andResGuard {
mappingFile = null
use7zip = true
keepRoot = false
whiteList = ["R.drawable.icon"] // 保留不混淆的资源
}
3. 动态下发SO库(ABI拆分 + 动态加载)
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a'
universalApk false
}
}
// 从服务器下载对应ABI的SO文件
val abi = Build.SUPPORTED_ABIS[0]
downloadSoFile("https://example.com/${abi}/libnative.so")
// 加载自定义路径SO
System.load("/data/data/com.example/app_lib/libnative.so")
1. 使用WorkManager调度任务
// 定义Worker
class SyncWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// 执行同步任务
return Result.success()
}
}
// 配置周期性任务(每6小时,仅在充电+网络连接时执行)
val constraints = Constraints.Builder()
.setRequiresCharging(true)
.setRequiredNetworkType(NetworkType.UNMETERED)
.build()
val request = PeriodicWorkRequestBuilder<SyncWorker>(6, TimeUnit.HOURS)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueue(request)
2. 合并网络请求(Retrofit + RxJava)
// 使用RxJava的zip操作符合并多个请求
Observable.zip(
apiService.getUserInfo(),
apiService.getNotifications(),
BiFunction { user, notifications ->
CombinedData(user, notifications)
}
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { data -> updateUI(data) }