前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 的应用冷优化

Android 的应用冷优化

原创
作者头像
用户9253515
发布2022-01-20 13:48:05
3410
发布2022-01-20 13:48:05
举报
文章被收录于专栏:Android开发技术

应用启动分类

冷启动

用户点击屏幕上的应用图标,经过展示启动窗口创建进程展示应用几个过程

热启动

用户进程已经创建,如果响应了低内存事件,例如在 onTrimMemory 中清除资源,则需要重新初始化

如何检测应用启动时长

  • 通过 adb shell am start -S -W $packageName/.MainActivity 启动检测
  • 通过显示调用 reportFullDrawn 根据业务需求,通知系统完成加载
  • 通过 android-sdk/platform-tools/systracesystrace.py 通过 Python systrace.py --time=10 -o launch.html sched gfx view wm 查看每个函数的耗时
  • 通过 Android Studio Profiler 启动应用查看

为什么会出现白屏

在应用启动过程中 ActivityStackstartActivityLocked 方法中会判断当前应用的启动模式,若为冷启动,则调用 ActivityRecordwindowContainerControllershowStartingWindow 方法,添加启动白屏页

通过 WindowMangerService 调用 mPolicyaddSplashScreen 方法,创建了一个 PhoneWindow 添加到窗口上

关键代码

代码语言:javascript
复制
if (theme != context.getThemeResId() || labelRes != 0) {
    try {
        context = context.createPackageContext(packageName, CONTEXT_RESTRICTED);
        context.setTheme(theme);
    } catch (PackageManager.NameNotFoundException e) {
        // Ignore
    }
}

final PhoneWindow win = new PhoneWindow(context);
win.setIsStartingWindow(true);

win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
        WindowManager.LayoutParams.MATCH_PARENT);

wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
view = win.getDecorView();

if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
    + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));

wm.addView(view, params);

如何优化

启动优化步骤分类

  • 白屏的视觉优化
  • MultiDex 优化
  • 逻辑代码优化
  • 首页布局优化

白屏的优化

对于白屏启动页面的优化,根据上述的代码分析,可以通过设置主题,为 windowBackground 添加与启动页一致的图片,视觉上可以骗过用户

MultiDex 优化

着应用的不断迭代,内部的方法数会不断增加,最终超过方法数上限。而官方也退出了 MultiDex的方案来解决,也就是意味着分包。在低版本手机 DVM 上,我们需要手动调用 MultiDex.install 加载主 Dex 以外的文件,可能造成 ANR

低版本启动的优化

面对这个问题,我们可以采用 Facebook 提出的方案。创建一个新的进程在 ApplicationattachBaseContext 中调用 MultiDex.install,主进程可以通过多种手段阻塞等到加载结束之后再进入应用

代码语言:javascript
复制
class App : Application() {

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(base)

        if (isAsyncLaunchProcess().not()) {
            val thread = DexInstallDeamonThread(base)
            thread.start()

            synchronized(lock) {
                try {
                    lock.wait()
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }
            }

            thread.exit()
        }
    }

    fun isAsyncLaunchProcess(): Boolean {
        val processName = SystemUtils.getCurrentProcessName(this)
        return processName != null && processName.contains(":install")
    }

    private class DexInstallDeamonThread(private val base: Context) :
        Thread() {
        private var handler: Handler? = null
        private var looper: Looper? = null

        @SuppressLint("HandlerLeak")
        override fun run() {
            Looper.prepare()
            looper = Looper.myLooper()
            handler = object : Handler() {
                override fun handleMessage(msg: Message) {
                    synchronized(lock) { lock.notify() }
                }
            }
            val messenger = Messenger(handler)
            val intent = Intent(base, MultiInstallActivity::class.java)
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            intent.putExtra("MESSENGER", messenger)
            base.startActivity(intent)
            Looper.loop()
        }

        fun exit() {
            if (looper != null) looper!!.quit()
        }
    }
}
代码语言:javascript
复制
class MultiInstallActivity : AppCompatActivity() {
    private var messenger: Messenger? = null
    public override fun onCreate(savedInstanceState: Bundle?) {
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        super.onCreate(savedInstanceState)
        window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
        setContentView(R.layout.activity_install)
        val from = intent
        messenger = from.getParcelableExtra("MESSENGER")
        thread {
            try {
                Log.d(TAG, "start install")
                MultiDex.install(application)
                Thread.sleep(5000L)
                Log.d(TAG, "finish install")
                messenger?.send(Message.obtain())
            } catch (e: Exception) {
                e.printStackTrace()
            }
            runOnUiThread {
                finish()
                exitProcess(0)
            }
        }
    }

    override fun onBackPressed() {
        //无法退出
    }

    companion object {
        private const val TAG = "MultiInstallActivity"
    }
}
代码语言:javascript
复制
<activity
    android:name="com.weex.multidexinstall.MultiInstallActivity"
    android:launchMode="singleTask"
    android:process=":install" />

逻辑代码优化

在应用启动过程中,我们一般会在 ApplicationonCreate 中加载 SDK 以及读取 SharedPreference 中的值等耗时操作

  • 对于 SharedPreference 可以替换为 MMKV

首页布局优化

对于布局优化,比较常见的一些技术如

  • ViewStub
  • merge 标签
  • 使用ConstraintLayout 替换原来的布局标签

本文转自 https://juejin.cn/post/7054214019749511205,如有侵权,请联系删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 应用启动分类
    • 冷启动
      • 热启动
      • 如何检测应用启动时长
      • 为什么会出现白屏
      • 如何优化
        • 启动优化步骤分类
          • 白屏的优化
            • MultiDex 优化
              • 低版本启动的优化
            • 逻辑代码优化
              • 首页布局优化
              相关产品与服务
              腾讯云代码分析
              腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,助力维护团队卓越代码文化。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档