| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> Android 启动优化(六)- 深入理解布局优化,价值2000元的学习资源泄露 -> 正文阅读 |
|
[移动开发]Android 启动优化(六)- 深入理解布局优化,价值2000元的学习资源泄露 |
=================================================================== 渐进式加载,简单来说,就是一部分一部分加载,当前帧加载完成之后,再去加载下一帧。 一种极致的做法是,加载 xml 文件,就想加载一个空白的 xml,布局全部使用 ViewStub 标签进行懒加载。 这样设计的好处是可以减缓同一时刻,加载 View 带来的压力,通常的做法是我们先加载核心部分的 View,再逐步去加载其他 View。 有人可能会这样问了,这样的设计很鸡肋,有什么用呢? 确实,在高端机上面作用不明显,甚至可能看不出来,但是在中低端机上面,带来的效果还是很明显的。在我们项目当中,复杂的页面首帧耗时约可以减少 30%。 优点:适配成本低,在中低端机上面效果明显。 缺点:还是需要在主线程读取 xml 文件 start(){ loadA(){ loadB(){ loadC() } } } 上面的这种写法,是可以的,但是这种做法,有一个很明显的缺点,就是会造成回调嵌套层数过多。当然,我们也可以使用 RxJava 来解决这种问题。但是,如果项目中没用 Rxjava,引用进来,会造成包 size 增加。 一个简单的做法就是使用队列的思想,将所有的 ViewStubTask 添加到队列当中,当当前的 ViewStubTask 加载完成,才加载下一个,这样可以避免回调嵌套层数过多的问题。 改造之后的代码见 val decorView = this.window.decorView ViewStubTaskManager.instance(decorView) .addTask(ViewStubTaskContent(decorView)) .addTask(ViewStubTaskTitle(decorView)) .addTask(ViewStubTaskBottom(decorView)) .start() class ViewStubTaskManager private constructor(val decorView: View) : Runnable { private var iViewStubTask: IViewStubTask? = null companion object { const val TAG = “ViewStubTaskManager” @JvmStatic fun instance(decorView: View): ViewStubTaskManager { return ViewStubTaskManager(decorView) } } private val queue: MutableList = CopyOnWriteArrayList() private val list: MutableList = CopyOnWriteArrayList() fun setCallBack(iViewStubTask: IViewStubTask?): ViewStubTaskManager { this.iViewStubTask = iViewStubTask return this } fun addTask(viewStubTasks: List): ViewStubTaskManager { queue.addAll(viewStubTasks) list.addAll(viewStubTasks) return this } fun addTask(viewStubTask: ViewStubTask): ViewStubTaskManager { queue.add(viewStubTask) list.add(viewStubTask) return this } fun start() { if (isEmpty()) { return } iViewStubTask?.beforeTaskExecute() // 指定 decorView 绘制下一帧的时候会回调里面的 runnable ViewCompat.postOnAnimation(decorView, this) } fun stop() { queue.clear() list.clear() decorView.removeCallbacks(null) } private fun isEmpty() = queue.isEmpty() || queue.size == 0 override fun run() { if (!isEmpty()) { // 当队列不为空的时候,先加载当前 viewStubTask val viewStubTask = queue.removeAt(0) viewStubTask.inflate() iViewStubTask?.onTaskExecute(viewStubTask) // 加载完成之后,再 postOnAnimation 加载下一个 ViewCompat.postOnAnimation(decorView, this) } else { iViewStubTask?.afterTaskExecute() } } fun notifyOnDetach() { list.forEach { it.onDetach() } list.clear() } fun notifyOnDataReady() { list.forEach { it.onDataReady() } } } interface IViewStubTask { fun beforeTaskExecute() fun onTaskExecute(viewStubTask: ViewStubTask) fun afterTaskExecute() } 源码地址:github.com/gdutxiaoxu/… ================================================================== 异步加载,简单来说,就是在子线程创建 View。在实际应用中,我们通常会先预加载 View,常用的方案有:
官方提供了一个类,可以来进行异步的inflate,但是有两个缺点:
因此,我们可以仿造官方的 AsyncLayoutInflater 进行改造。核心代码在 AsyncInflateManager。主要介绍两个方法。
@UiThread fun asyncInflate( context: Context, vararg items: AsyncInflateItem? ) { items.forEach { item -> if (item == null || item.layoutResId == 0 || mInflateMap.containsKey(item.inflateKey) || item.isCancelled() || item.isInflating()) { return } mInflateMap[item.inflateKey] = item onAsyncInflateReady(item) inflateWithThreadPool(context, item) } }
/**
*/ @U
iThread fun getInflatedView( context: Context?, layoutResId: Int, parent: ViewGroup?, inflateKey: String?, inflater: LayoutInflater ): View { if (!TextUtils.isEmpty(inflateKey) && mInflateMap.containsKey(inflateKey)) { val item = mInflateMap[inflateKey] val latch = mInflateLatchMap[inflateKey] if (item != null) { val resultView = item.inflatedView if (resultView != null) { //拿到了view直接返回 removeInflateKey(item) replaceContextForView(resultView, context) Log.i(TAG, “getInflatedView from cache: inflateKey is $inflateKey”) return resultView } if (item.isInflating() && latch != null) { //没拿到view,但是在inflate中,等待返回 try { latch.await() } catch (e: InterruptedException) { Log.e(TAG, e.message, e) } removeInflateKey(item) if (resultView != null) { Log.i(TAG, “getInflatedView from OtherThread: inflateKey is $inflateKey”) replaceContextForView(resultView, context) return resultView } } //如果还没开始inflate,则设置为false,UI线程进行inflate item.setCancelled(true) } } Log.i(TAG, “getInflatedView from UI: inflateKey is $inflateKey”) //拿异步inflate的View失败,UI线程inflate return inflater.inflate(layoutResId, parent, false) } 第一步:选择在合适的时机调用 object AsyncUtils { fun asyncInflate(context: Context) { val asyncInflateItem = AsyncInflateItem( LAUNCH_FRAGMENT_MAIN, R.layout.fragment_asny, null, null ) AsyncInflateManager.instance.asyncInflate(context, asyncInflateItem) } fun isHomeFragmentOpen() = getSP(“async_config”).getBoolean(“home_fragment_switch”, true) } 第二步:在获取 View 的时候,先去缓存里面查找 View override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment val startTime = System.currentTimeMillis() val homeFragmentOpen = AsyncUtils.isHomeFragmentOpen() val inflatedView: View inflatedView = AsyncInflateManager.instance.getInflatedView( context, R.layout.fragment_asny, container, LAUNCH_FRAGMENT_MAIN, inflater ) Log.i( TAG, “onCreateView: homeFragmentOpen is $homeFragmentOpen, timeInstance is ${System.currentTimeMillis() - startTime}, ${inflatedView.context}” ) return inflatedView // return inflater.inflate(R.layout.fragment_asny, container, false) } |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 6:06:28- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |