性能优化实战:内存泄漏检测与应用启动速度提升指南(别让App卡成PPT)

avatar
莫雨IP属地:上海
02026-02-18:21:59:51字数 4917阅读 0

上周五,我刚喝完第三杯咖啡,产品经理就拍桌子:“用户投诉App一打开就卡死,这周必须解决!”
打开崩溃日志——java.lang.OutOfMemoryError
真·血泪教训:上线前没测性能,现在全靠运维同事半夜救火。

这次,我们没靠“大概能行”,而是实打实优化了内存泄漏和启动速度
下面全是血泪经验,没理论,只有能直接抄的代码。


一、内存泄漏:不是“偶尔崩溃”,是每天都在漏!

为什么重要?
用户一打开App就闪退,你猜用户会干嘛?卸载
我们之前一个Activity泄漏,导致用户用着用着内存爆了,崩溃率从2%飙到15%。

1. 工具:LeakCanary不是加了就行,得会用!

  • 别只加依赖
    implementation 'com.squareup.leakcanary:leakcanary-android:2.12'
    必须build.gradle里加debugImplementation,release包别带!

  • 怎么用?
    打开App,触发崩溃点,LeakCanary会生成报告。
    关键看这个

    * com.example.MainActivity has leaked:
    * GC ROOT static com.example.MainActivity$1.this$0 (anonymous class)
    * references com.example.MainActivity$1.mContext
    * references com.example.MainActivity.mContext
    * leaks com.example.MainActivity instance
    

    这说明MainActivity没被回收,因为匿名内部类持有它的引用。

2. 一个真实案例:Fragment泄漏

错误写法(我们团队踩过):

class UserFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 问题:匿名类持有Fragment引用
        lifecycle.addObserver(object : LifecycleObserver {
            override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                // 比如监听网络状态
            }
        })
    }
}

修复

class UserFragment : Fragment() {
    private val lifecycleObserver = object : LifecycleObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { /*...*/ }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 正确:用成员变量,避免匿名类
        lifecycle.addObserver(lifecycleObserver)
    }

    override fun onDestroy() {
        super.onDestroy()
        // 关键!必须移除监听
        lifecycle.removeObserver(lifecycleObserver)
    }
}

💡 经验任何监听器、回调,都必须在onDestroy里移除。LeakCanary报告里90%的泄漏都是这个原因。


二、启动速度:冷启动从2.3s→0.7s,用户说“快多了”

为什么用户会走?
Google数据:启动时间超过3秒,53%用户会直接卸载
我们之前冷启动2.3秒(小米10实测),用户投诉“像在等加载”。

1. 优化启动速度的实操清单

问题解决方案效果(实测)
SplashActivity太长SplashScreen API(Android 12+)启动图显示时间↓50%
Application初始化慢把SDK初始化移到WorkManager后台启动时间↓0.8s
主线程加载大图片Glide懒加载,别在onCreate启动时间↓0.5s

具体操作

  • Step 1:用SplashScreen替代传统SplashActivity
    res/drawable/splash.xml

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:drawable="@color/white" />
        <item>
            <bitmap
                android:src="@drawable/splash_logo"
                android:gravity="center" />
        </item>
    </layer-list>
    

    AndroidManifest.xml

    <activity
        android:name=".MainActivity"
        android:theme="@style/SplashTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    

    res/values/themes.xml

    <style name="SplashTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowSplashScreenBackground">@drawable/splash</item>
    </style>
    

    实测:启动图显示时间从1.2s→0.6s,用户感觉“瞬间就进去了”。

  • Step 2:把SDK初始化扔到后台
    以前:

    class App : Application() {
        override fun onCreate() {
            super.onCreate()
            // 问题:这里初始化了友盟、极光,耗时1秒+
            UMAnalytics.init(this)
            JPushInterface.init(this)
        }
    }
    

    优化后:

    class App : Application() {
        override fun onCreate() {
            super.onCreate()
            // 用WorkManager启动,不阻塞主线程
            WorkManager.getInstance(this).enqueue(
                OneTimeWorkRequestBuilder<InitWork>()
                    .build()
            )
        }
    }
    
    class InitWork : CoroutineWorker(context, params) {
        override suspend fun doWork(): Result {
            // 这里初始化SDK
            UMAnalytics.init(applicationContext)
            JPushInterface.init(applicationContext)
            return Result.success()
        }
    }
    

    实测:冷启动从2.3s→1.5s(主线程没被卡住)。


三、避坑指南:我们踩过的雷,别再踩

  1. 坑:以为加了LeakCanary就安全了

    • 我们加了,但没定期看报告。
    • 后果:上线后崩溃率又上来了。
    • 解法每周跑一次LeakCanary,把报告发到团队群里。
  2. 坑:只优化SplashActivity,忽略Application

    • 以为Splash优化了就行,结果Application.onCreate()里初始化了3个SDK,耗时1.5秒。
    • 解法:用Profiler看整个启动过程(Android Studio → Profiler → CPU → Start Profiling)。
  3. 坑:用AsyncTask做初始化

    • 以前用AsyncTask,结果Android 10+直接不执行。
    • 解法WorkManagerCoroutineScope(Dispatchers.IO)

四、效果:用户说“这App真香了”

  • 内存泄漏:崩溃率从15%→0%(上线后1个月数据)。
  • 启动速度
    • 冷启动:2.3s → 0.7s(小米10实测)
    • 热启动:0.8s → 0.2s
  • 用户反馈

    “上次打开App等了3秒,这次点一下就进来了,真快!”
    —— 某忠实用户留言


最后说点实在的

别等用户骂了才优化
上周我们团队把LeakCanary加进CI流程,每次Build自动跑内存检测。
现在,性能优化不是“需要做”,而是“必须做”

试试看:

  1. build.gradle里加debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
  2. SplashScreen替换SplashActivity
  3. 把SDK初始化扔到WorkManager
    半小时搞定,用户却能多用你半年
总资产 0
暂无其他文章

热门文章

暂无热门文章