App性能优化(布局优化,线程优化,app瘦身优化,页面切换优化,App启动优化,内存优化)

Android APP性能优化(最新总结)

在目前Android开发中,UI布局可以说是每个App使用频率很高的,随着UI越来越多,布局的重复性、复杂度也随之增长,这样使得UI布局的优化,显得至关重要,UI布局不慎,就会引起过度绘制,从而造成UI卡顿的情况,本篇博客,主要总结了下目前所了解的优化。

主要对一下几点进行说明
布局优化 ,线程优化 ,App瘦身优化 , 页面切换优化 , App启动优化 , 内存优化(内存泄漏,内存溢出OOM)

布局优化

减少嵌套,避免过度加载。
其实说白了就是减少层级,越简单越好,减少overdraw,就能更好的突出其性能:。

  1. **如果能使用RelativeLayout的,尽量不用linearlayout **
    在RelativeLayout和LinearLayout同时都能够满足需求时,尽量使用RelativeLayout,这一点可以从我们MainActivity引用默认布局就可以看出,默认是RelativeLayout,因为可以通过扁平的RelativeLayout降低LinearLayout嵌套所产生布局树的层级。可以自己用LinearLayout和RelativeLayout写个布局看看他们之间布局的不同。
    看下这两二布局的分级关系 首先LinearLayout
    在这里插入图片描述

RelativeLayout在这里插入图片描述

  1. 布局中使用抽象布局标签include、merge、ViewStub
    在Android种系统对View进行测量、布局和绘制时,都是通过对View数的遍历来进行操作的。如果一个View数的高度太高就会严重影响测量、布局和绘制的速度。Google也在其API文档中建议View高度不宜哦过10层。现在版本种Google使用RelativeLayout替代LineraLayout作为默认根布局,目的就是降低LineraLayout嵌套产生布局树的高度,从而提高UI渲染的效率。
    布局复用,使用<www.taohuayuan178.com include>标签重用layout;
    提高显示速度,使用延迟View加载;
    减少层级,使用标签替换父级布局;
    注意使用wrap_content,会增加measure计算成本;
    删除控件中无用属性;

include
开发Android布局时,我们常将一些通用的视图提取到一个单独的layout文件中,然后使用标签在需要使用的其他layout布局文件中加载进来,比如我们自己App导航栏等。这样,便于对相同视图内容进行统一的控制管理,提高布局重用性。

viewstub
它是view的子类。他是一个轻量级View, 隐藏的,没有尺寸的View。他可以用来在程序运行时简单的填充布局文件
merge
merge标签存在的意义是帮助include标签排除多余的一层ViewGroup容器,减少view hierarchy的结构,提升UI渲染的性能。include标签存在着一个不好的地方,可能会导致产生多余的布局嵌套。

  1. Android最新的布局方式ConstaintLayout拖拽

ConstraintLayout允许你在不适用任何嵌套的情况下创建大型而又复杂的布局。它与RelativeLayout非常相似,所有的view都依赖于兄弟控件和父控件的相对关系。但是,ConstraintLayout比RelativeLayout更加灵活,目前在AndroidStudio中使用也十分方便,就和以前的拖拉控件十分相似。那么怎么使用呢?
 首先是安装Constaintlayout了。Android SDK -> SDK Tools -> Support Repository中的ConstrainLayout for Android和Solver for ConstaintLayout。
 
在这里插入图片描述
 然后build.gradle中添加:

compile ‘com.android.support.constraint:constraint-layout:1.0.0-beta4’

然后同步下就可以正常使用ConstaintLayout了。

  1. 利用Android Lint工具寻求可能优化布局的层次
     一些Lint规则如下:
      1、使用组合控件: 包含了一个ImageView以及一个TextView控件的LinearLayout如果能够作为一个组合控件将会被更有效的处理。
      2、合并作为根节点的帧布局(Framelayout) :如果一个帧布局时布局文件中的根节点,而且它没有背景图片或者padding等,更有效的方式是使用merge标签替换该Framelayout标签 。
      3、无用的叶子节点:通常来说如果一个布局控件没有子视图或者背景图片,那么该布局控件时可以被移除(由于它处于 invisible状态)。
      4、无用的父节点 :如果一个父视图即有子视图,但没有兄弟视图节点,该视图不是ScrollView控件或者根节点,并且它没有背景图片,也是可以被移除的,移除之后,该父视图的所有子视图都直接迁移至之前父视图的布局层次。同样能够使解析布局以及布局层次更有效。
      5、过深的布局层次:内嵌过多的布局总是低效率地。考虑使用一些扁平的布局控件,例如 RelativeLayout、GridLayout ,来改善布局过程。默认最大的布局深度为10 。

线程优化

1.不能通过非UI线程对View进行操作。因为Android的UI不是安全的,如果View能被不同的线程所访问或修改,那么就可能在程序的执行期间,产生不可预期的行为或者并发错误。
2.使用线程时,避免在循坏中使用同步,因为获取和释放锁的操作代价很大。会引起CPU资源的损耗。
3.处理多线程以及线程间通信时,使用HandlerThread来操作,它内部包装了Looper,记得不用的时候退出/释放资源哦。
4.当工作线程与UI线程之间通信的时候,推荐使用AsyncTask(Android 7.0后内部任务变成串行处理,不再会出现以前并行时超过任务数执行饱和策略的情况)
5.Loader可以用来代替AsyncTask的某些情况,因为Loader的生命周期是独立的(与Application Context有关),当Activity/Fragment销毁重建时,它仍然在,而且它特别使用异步操作,比如AsyncTaskLoader代替AsyncTask也可以实现后者的功能,但是生命周期完全独立于Activity。切记Loader使用完记得销毁。

6.当你的Service不需要交互时,请使用可以自动停止的IntentService。
7.当你希望延长BroadcastReceiver的生命周期时,例如启动一个后台线程IntentService。在onReceiver中调用BroadcastReceiver.goAsync(),它会返回一个PendingResult对象,这时,广播接收器的生命周期会延长持续到PendingResult.finish()方法调用。
8.线程池最好用构造方法手动创建,而不要用Executors来直接调用工厂方法,这样利于明白线程池的运行规则,避免用了错误的线程池导致资源耗尽。
9.给线程一个好听的名字,调试时候用。
10.线程池设置线程的存活时间,以保证空闲线程准确释放。

App瘦身优化

App瘦身优化包括
使用软件
1.1 软件名字:Pngyu
1.2 软件下载地址: windows版本下载地址mac版下载地址。具体使用方法网上可以搜一下哦。
2. 删除冗余文件
 在开发的过程中,随着需求的变更,项目中会有一些冗余的资源文件例如图片,音频,plist文件,不用的第三方的文件等。需要我们去定期的清理。这个需要责任到人,某一个模块用到的资源进行分类,每个人对自己负责的模块要有owner精神。在需求发生变化的时候检查自己负责的模块是否有冗余的资源文件删除。
3. 图片资源压缩
一般情况UI给开发人员发的切图都是未经压缩的,所以我们在给app瘦身的时候需要对工程中的切图进行压缩操作。可以使用Pngyu进行压缩。
4.合适的资源文件存取策略
虽然我们采取了一些策略,但是随着app功能的增加,源代码文件,切图等资源文件还是在增加。所以我们就需要对资源文件的位置进行处理。对于使用频率较高的切图等资源文件,我们放进工程中,而对于app内容展示到,使用频率较低的我们可以考虑将这一类资源文件放台后台服务器上通过接口来返回,图片资源提供图片的url。再通过合适的缓存策略减少网络请求的次数,提升用户的体验。

可能优化的点在哪?
1、减少切片,大图,及背景图

2、图片压缩,采用.9图片。

3、采用压缩率更高的webp图片格式,代替当前的png格式

4、清理不需要的布局文件,XML格式资源文件,图片资源

5、删除一些用户量极少,“无意义”的功能

6、检查第三方包,把不需要的组件、图片之类的删除或替换

7、降低so库的体积

8、把部分页面做成H5,客户端删除这部分功能

9、自查代码,尤其是逻辑相似的部分,抽出去来,删除冗余代码

具体实现
1、lint使用

采用lint工具,删除了大量无用的资源。有一定作用。Android studio集成了lint工具,检测“unused resoure“及unused declaration等。这里我们使用lint检测了无用的资源文件。
另我的量一篇文章有介绍

2、其他团队so库的体积减少,作用明显著,尤其类似于百度地图,直播软件

3、代码冗余部分:效果非常有限,你再减少也少不了几kb

4、使用图片压缩工具,有一定作用
Google推荐图片压缩工具:https://developers.google.com/speed/docs/insights/OptimizeImages,
市面上有许多工具可用来对JPEG和PNG文件执行进一步的无损压缩,且不会对图片质量造成任何影响。对于JPEG文件,我们建议您使用jpegtran或jpegoptim(仅适用于Linux;使用–strip-all选项运行)。对于PNG文件,我们建议使用OptiPNG或PNGOUT。

5、H5页面,作用明显,而且H5适配非常简单啊

6、支持插件so,插件支持网络加载so及更新原则,作用明显

7、代码混淆,jar包资源混淆、
8、TinyPng压缩图片  试一下测试 https://tinypng.com/

TinyPng能够在视觉上几乎不
影响图片的情况下显著压缩图片体积。
从图中可以看到,压缩3次以后基本上体积就很难再减少了,压缩5次以后体积基本上就不变了,值得高兴的是,即使不停的压缩,图片依然不会太失真(但这是有损压缩,有时候alpha通道会给你压缩没了)。如下,压缩5次后的对比图:

TinyPng好用的地方:

1、图片压缩后对视觉影响不大,但体积显著减小

2、可以批量压缩

3、压缩后文件名与原文件名相同,可以直接替换,尤其是批量压缩的时候

4、可以反复压缩5-6次,而不用担心太失真,肯定会失真啦,不影响视觉效果

页面切换优化

所谓界面和流畅度优化,就是尽可能多地消除用户可直接感知的、影响用户操作体验的bug

1、人为在UI线程中做轻微耗时操作,导致UI线程卡顿

人为避免一切耗时操作
开启 StrictMode(严苛模式)

在Activity里加入如下代码

public void onCreate() {
     if (DEVELOPER_MODE) {
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectDiskReads()
                 .detectDiskWrites()
                 .detectNetwork()   // or .detectAll() for all detectable problems
                 .penaltyLog()
                 .build());
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                 .detectLeakedSqlLiteObjects()
                 .detectLeakedClosableObjects()
                 .penaltyLog()
                 .penaltyDeath()
                 .build());
     }
     super.onCreate();
 }

注意:StrictMode只适用于测试版本,千万不要在上线版本使用 StrictMode

2、布局Layout过于复杂,无法在16ms内完成渲染
利用drawableXXX属性来做有图文的控件,特别是类似设置页面中文字在左右边有剪头的View
多用tools的属性例如tools:text tools:listitem

3、同一时间动画执行的次数过多,导致CPU或GPU负载过重
人为避免同一时间执行过多动画

4、View过度绘制,导致某些像素在同一帧时间内被绘制多次
简化布局嵌套
减少不必要的背景颜色填充(用纯色图片代替颜色)

在设置-> 开发者选项->调试GPU过度绘制中打开调试
定位绘制区域
利用Android提供的工具进行位置确认以及修改(HierarchyView,Tracer for OpenGL ES)
定位到具体的视图(xml文件或者View)
通过代码和xml文件分析过度绘制原因
结合具体情况进行优化

5、View频繁触发onMeasure,onLayout,导致onMeasure,onLayout累计耗时过多及整个View频繁的重新渲染
使用系统性能分析工具systrace分析measure, layout耗时

6、内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作
使用内存分配跟踪工具Allocation Tracker工具跟踪对象的分配

7、冗余资源及逻辑等导致加载和执行缓慢
lint检查删除冗余
去除重复库无用库,使用更小库
去除无用的语言资源
图片压缩webp
开启ProGuard
开启shrinkResources

buildTypes{
	release{
		miniyEnabled true
		shrinkResources true
		proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	}
} 

8、代码效率低
考虑使用ArrayMap代替传统数据结构HashMap
避免使用enums,推荐使用static
避免在非Constants类中使用static

其他详见《大话Java性能优化》

9、其他
对于不需要使用硬件加速的activity(没有动画效果、视频播放以及各种多媒体文件),不要在AndroidManifest.xml文件声明activity时添加“android:hardwareAccelerated=“true””关掉硬件加速可节省应用内存

对于需要横竖屏转换的应用,又不想在横竖屏切换的时候重新跑onCreate方法,可以在AndroidManifest.xml文件声明Activity时添加“android:configChanges=“screenSize|orientation””

为了减轻应用程序主进程的内存压力,对于耗内存比较多的界面(多媒体),可以在AndroidManifest.xml文件声明Activity时下添加“android:process=".processname"”单开一个进程,退出在退出这个界面的时候一定要在该界面的onDestory方法中调用System的kill方法来杀掉该进程;

可以通过为application、activity自定义主题的方式来关掉多点触摸功能,只需要在自定义的主题下添加这两个标签:

  <item name="android:windowEnableSplitTouch">false</item>
  <item name="android:splitMotionEvents">false</item>

App启动优化

一、App启动分类
1.冷启动 Cold start

在启动应用前,系统还没有App的任何进程。比如设备开机后应用的第一次启动,系统杀掉应用进程 (如:系统内存吃紧引发的 kill 和 用户主动产生的 kill) 后 的再次启动等。那么自然这种方式下,应用的启动时间最长。

2.热启动 Warm start
当应用中的 Activities 被销毁,但在内存中常驻时,应用的启动方式就会变为暖启动。相比冷启动,暖启动过程减少了对象初始化、UI的布局和渲染。启动时间更短。但启动时,系统依然会展示一个空白背景,直到第一个 Activity 的内容呈现为止。

3.温启动 Lukewarm start
用户退出您的应用,但随后重新启动。该过程可能已继续运行,但应用程序必须通过调用onCreate()从头开始重新创建活动。系统从内存中驱逐您的应用程序,然后用户重新启动它。进程和Activity需要重新启动,但任务可以从保存的实例状态包传递到onCreate()中。

启动速度优化主要是针对冷启动方式。下面看下冷启动的时候会做哪些工作。

二、冷启动
应用发生冷启动时,系统有三件任务要做:

加载启动App;
App启动之后立即展示出一个空白的Window;
创建App的进程;
创建App进程后,会马上执行以下任务:

初始化应用中的对象 (比如 Application 中的工作);

启动主线程 (UI 线程) ;

创建第一个 Activity;

加载内容视图 (Inflating) ;

计算视图在屏幕上的位置排版 (Laying out);

进行第一次绘制 (draw)。

只有当应用完成第一次绘制,系统当前展示的空白背景才会消失,才会被 Activity 的内容视图替换掉。也就是这个时候,用户才能和我们的应用开始交互。下图展示了冷启动过程系统和应用的一个工作时间流:
在这里插入图片描述

三、优化思路
作为普通应用,App进程的创建等环节我们是无法主动控制的。开发人员唯一能做的就是在Application 和 第一个 Activity 中,减少 onCreate() 方法的工作量,从而缩短冷启动的时间。像应用中嵌入的一些第三方 SDK,都建议在 Application 中做一些初始化工作,开发人员不妨采取懒加载的形式移除这部分代码,而在真正需要用到第三方 SDK 时再进行初始化。

Google也给出了启动加速的方向:

1、利用提前展示出来的Window,快速展示出来一个界面,给用户快速反馈的体验; 
2、 避免在启动时做密集沉重的初始化(Heavy app initialization); 
3、 定位问题:避免I/O操作、反序列化、网络操作、布局嵌套等

四、正确测量评估启动性能的方法
1.display time
从Android KitKat版本开始,Logcat中会输出从程序启动到某个Activity显示到画面上所花费的时间。这个方法比较适合测量程序的启动时间。

在这里插入图片描述

2.reportFullyDrawn
我们通常来说会使用异步懒加载的方式来提升程序画面的显示速度,这通常会导致的一个问题是,程序画面已经显示,可是内容却还在加载中。为了衡量这些异步加载资源所耗费的时间,我们可以在异步加载完毕之后调用activity.reportFullyDrawn()方法来告诉系统此时的状态,以便获取整个加载的耗时。
在这里插入图片描述
3.Traceview
告诉我们每一个方法执行了多长时间.这个工具可以通过 Android Device Monitor 或者从代码中启动。
3.1 Android Device Monitor启动
启动应用,点击 Start Method Tracing,应用启动后再次点击,会自动打开刚才操作所记录下的.trace文件,建议使用DDMS来查看,功能更加方便全面。

在这里插入图片描述

3.2 代码启动
①在onCreate开始和结尾打上trace

Debug.startMethodTracing("GithubApp");
    ...
    Debug.stopMethodTracing();

注意加读写权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

运行程序, 会在sdcard上生成一个”GithubApp.trace”的文件.

②通过adb pull将文件导出到本地

adb pull /sdcard/GithubApp.trace ~/temp

③打开DDMS分析trace文件
④分析trace文件
在这里插入图片描述

在下方的方法区点击”Real Time/Call”, 按照方法每次调用耗时降序排.
耗时超过500ms都是值得注意的.
看左边的方法名, 可以看到耗时大户就是我们用的几大平台的初始化方法, 特别是Bugly, 还加载native的lib, 用ZipFile操作-等.
点击每个方法, 可以看到其父方法(调用它的)和它的所有子方法(它调用的).
点击方法时, 上方的该方法执行时间轴会闪动, 可以看该方法的执行线程及相对时长.
4.Systrace
在onCreate方法里面添加trace.beginSection()与trace.endSection()方法来声明需要跟踪的起止位置,系统会帮忙统计中间经历过的函数调用耗时,并输出报表。在这里插入图片描述
5.adb命令计算 App 的启动时间

adb shell am start -W packageName/packageName.activity

例如:

`adb shell am start -W com.media.painter/com.media.painter.PainterMainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.media.painter/.PainterMainActivity }
Status: ok
Activity: com.media.painter/.PainterMainActivity
ThisTime: 355
TotalTime: 355
WaitTime: 365
Complete

详细请见:
Android 中如何计算 App 的启动时间

五、优化方案
1.主题切换
通过主题设置,不显示启动时的白屏背景。有以下几种方案:

1.1 直接不显示白屏,直到程序初始化完毕直接显示第一个Activity

<style name="LaunchStyle" parent="Theme.AppCompat.Light.DarkActionBar">
            ......
            <item name="android:windowIsTranslucent">true</item>
            <item name="android:windowNoTitle">true</item>
</style>

   <style name="LaunchStyle" parent="Theme.AppCompat.Light.DarkActionBar">
        ......
        <item name="android:windowDisablePreview">true</item>
    </style>

然后设置给第一个activity

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

然后在MainActivity中在加载布局之前,重新设置主题

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
    }

效果
添加链接描述

内存优化(内存泄漏,内存溢出)

内存溢出
内存溢出(out of memory)通俗理解就是内存不够,通常在运行大型软件或游戏时,软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。此时软件或游戏就运行不了,系统会提示内存溢出,有时候会自动关闭软件,重启电脑或者软件后释放掉一部分内存又可以正常运行该软件
产生原因的区别

内存溢出产生的原因:

  1. 内存中加载的数据量过于庞大,如一次从数据库中取出过多的数据

  2. 集合类中有对对象的引用,使用完后未清空

  3. 代码中存在死循环或循环产生过多重复的实体对象

  4. 使用的第三方软件中的bug

  5. 启动参数内存值设定的过小

内存溢出的解决方案:

  1.  修改JVM启动参数,直接增加内存 (-Xms –Xms 参数一定不要忘记加)
    
  2.  检查错误日志查看 OutOfMemory 错误前是否有其他异常或错误
    
  3.  对代码进行分步运行分析,找出可能发生溢出的位置
    

重点排查一下几点:

  1. 检查是否一次获取大量数据的查询,一般来说,一次获取十万条以上的记录到内存,就可能产生内存溢出.所以大家在开发的时候,需要考虑如果上线后需要获取大量数据,增加预防次问题,对此建议查询尽量使用分析查询

  2. 检查代码是否有死循环或递归调用

  3. 检查是否有大量循环重复产生新对象实体

  4. 检查List Map等集合对象是否使用完后,未清除的问题,List Map等集合对象始终会有对对象的引用,这样的对象便不会被GC回收

其他详情 请 –https://blog.csdn.net/cp_panda_5/article/details/79613870

内存泄漏

内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存泄漏缺陷具有隐蔽性、积累性的特征,比其他内存非法访问错误更难检测。因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。

泄露原因

  • 常发性内存泄漏:发生内存泄漏的代码会被多次执行到,每次被执行时都会导致一块内存泄漏。

  • 常发性

  • 偶发性内存泄漏:发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。

  • 一次性内存泄漏:发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且仅有一块内存发生泄漏。

  • 隐式内存泄漏:程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值