美文网首页
Android性能优化

Android性能优化

作者: Johnson_Coding | 来源:发表于2019-10-12 10:03 被阅读0次

    android内存监测

    • LeakCanary 详情

      一个可以检测程序在运行过程中发生的内存泄漏问题,通过简单的代码配置,可以方便的找出我们应用中的内存问题

    • Memory Monitor

      android studio自带的实时内存分析工具,我们可以通过实时的内存、CPU等的波动来分析问题,如果某个页面反复进入后内存持续增长,我们就要注意了。

    内存优化方案

    • 界面不可见时及时回收部分内存

      当用户打开了另外一个程序,我们的程序界面已经不再可见的时候,我们应当将所有和界面相关的资源进行释放。在这种场景下释放资源可以让系统缓存后台进程的能力显著增加,因此也会让用户体验变得更好。

      检测界面是否可见我们可以重写如下方法:

      @Override  
      public void onTrimMemory(int level) {  
          super.onTrimMemory(level);  
          switch (level) {  
          case TRIM_MEMORY_UI_HIDDEN:  
              // 进行资源释放操作  
              break;  
          }  
      }
      
      

      onTrimMemory方法只有当程序中的所有UI组件全部不可见的时候才会触发完全不可见时候才会调用,这和onStop()方法还是有很大区别的,因为onStop()方法只是当一个Activity完全不可见的时候就会调用,比如说用户打开了我们程序中的另一个Activity。 因此,我们可以在onStop()方法中去释放一些Activity相关的资源,比如说取消网络连接或者注销广播接收器等,但是像UI相关的资源应该一直要等到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)这个回调之后才去释放,这样可以保证如果用户只是从我们程序的一个Activity回到了另外一个Activity,界面相关的资源都不需要重新加载,从而提升响应速度。

    • 使用Handler时尽量弱引用

      我们经常会在handler中进行一些延时任务,这些延时任务会导致Activity被引用,从而发生内存泄漏,为了避免这类事情发生,我们可以对handler使用弱引用。

      public class MainActivity extends AppCompatActivity {
      
          public static final String TAG = "carson:";
          private Handler showhandler;
      
          // 主线程创建时便自动创建Looper & 对应的MessageQueue
          // 之后执行Loop()进入消息循环
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              //1\. 实例化自定义的Handler类对象->>分析1
              //注:
                  // a. 此处并无指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue;
                  // b. 定义时需传入持有的Activity实例(弱引用)
              showhandler = new FHandler(this);
      
              // 2\. 启动子线程1
              new Thread() {
                  @Override
                  public void run() {
                      try {
                          Thread.sleep(1000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      // a. 定义要发送的消息
                      Message msg = Message.obtain();
                      msg.what = 1;// 消息标识
                      msg.obj = "AA";// 消息存放
                      // b. 传入主线程的Handler & 向其MessageQueue发送消息
                      showhandler.sendMessage(msg);
                  }
              }.start();
      
              // 3\. 启动子线程2
              new Thread() {
                  @Override
                  public void run() {
                      try {
                          Thread.sleep(5000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      // a. 定义要发送的消息
                      Message msg = Message.obtain();
                      msg.what = 2;// 消息标识
                      msg.obj = "BB";// 消息存放
                      // b. 传入主线程的Handler & 向其MessageQueue发送消息
                      showhandler.sendMessage(msg);
                  }
              }.start();
      
          }
      
          // 分析1:自定义Handler子类
          // 设置为:静态内部类
          private static class FHandler extends Handler{
      
              // 定义 弱引用实例
              private WeakReference<Activity> reference;
      
              // 在构造方法中传入需持有的Activity实例
              public FHandler(Activity activity) {
                  // 使用WeakReference弱引用持有Activity实例
                  reference = new WeakReference<Activity>(activity); }
      
              // 通过复写handlerMessage() 从而确定更新UI的操作
              @Override
              public void handleMessage(Message msg) {
                  switch (msg.what) {
                      case 1:
                          Log.d(TAG, "收到线程1的消息");
                          break;
                      case 2:
                          Log.d(TAG, " 收到线程2的消息");
                          break;
      
                  }
              }
          }
      }
      
      

      这样一来,handler中发送延时消息便不会发生内存泄漏了。

      当然避免handler内存泄漏还可以采取当Activity结束使用时候,清空消息队列的操作,如下:

      @Override
      protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
        // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
      }
      
      
    • 加载图片的注意事项 详情

      • 不在小控件上显示大图
      • 列表类图片,仅加载当前页面可见的图片
      • 有显示原图需求时,要判断可用内存,内存不够时要压缩图片
    • 避免内存抖动 详情

      • 尽量避免在循环体内创建对象,应该把对象创建移到循环体外。

      • 注意自定义View的onDraw()方法会被频繁调用,所以在这里面不应该频繁的创建对象。

      • 当需要大量使用Bitmap的时候,试着把它们缓存在数组中实现复用。

      • 对于能够复用的对象,同理可以使用对象池将它们缓存起来。

    布局优化 详情

    避免过度绘制

    • 什么是过度绘制

      过度绘制就是在同一个位置,有多次的颜色绘制过程。常见的情况就是在同一个位置堆叠了许多控件,这会造成一些性能问题,严重的情况会造成卡顿。

    • 如何检测过度绘制

      开发者选项->调试GPU过度绘制->显示过度绘制区域

    • 过度绘制优化

      • 移除控件中不需要的背景
      • 将layout层级扁平化
      • 减少透明度的使用
      • 自定义View中减少重复绘制区域

    布局优化技巧

    • 简单布局优先使用LinearLayoutFragmentLayout
    • 复杂布局优先使用constrainLayout
    • 使用include标签提高复用性
    • 使用ViewStub标签延迟加载
    • onDraw()中不要创建新的局部变量以及不要做耗时操作

    网络优化 详情

    为什么要网络优化

    • 过多的无用网络请求,会消耗用户的网络流量。流量消耗过大会流失用户
    • 频繁的网络操作会导致设备用电量提升
    • 网络弹框的频繁出现会降低用户体验
    • 应用更新、大文件下载等场景,更优的网络传输速度可提升用户体验

    网络优化的方式

    • 使用GZip压缩,数据压缩后可以减少流量的消耗,减少传输的时间
    • 使用IP直连,DNS域名解析是一个较为耗时操作,可以直连IP减少解析时间
    • 图片加载
      • 使用WebP格式可以大幅节省流量
      • 图片按需加载,列表中图片只加载缩略图
      • 大图上传时,采用分片传输,失败只传对应片段
      • 用户体验影响不大时,手机原图压缩后再传输
    • 减少接口数量,同一个页面尽量只使用一个接口,数据可以放到后台去拼凑
    • 利用缓存,对数据设定有效期,有效期内数据不重复请求
    • 检测网络状态,不同网络转态执行不同策略,例如移动网络不加载图片,2G网络只加载标题等。
    • 文件上传、下载采用断点续传,不浪费已传输完成部分流量
    • 利用抓包工具模拟多种情况,在实践中调整不断优化用户体验

    启动优化 详情

    闪屏页优化

    主流APP是在应用启动时候会加载一个默认的主题,用来去掉应用启动时候的黑/白屏的情况

    <style name="AppThemeWelcome" parent="Theme.AppCompat.NoActionBar">
        ...
        <item name="android:windowBackground">@drawable/logo</item>  <!-- 默认背景-->
    </style>
    
    

    应用主题到ApplicationActivity

    <activity android:name=".ui.activity.DemoSplashActivity"
      android:configChanges="orientation|screenSize|keyboardHidden"
      android:theme="@style/AppThemeWelcome"
      android:screenOrientation="portrait">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    
    

    其实就是个障眼法而已,提前让你看到了假的界面。也算是一种不错的方法,但是治标不治本。

    第三方库懒加载

    在开发中会用到很多的三方库,如友盟、百度、bugly、图片库、网络库等。

    这些都是必须的,不能去掉,那么办法就是异步加载了,可以有以下几种思路

    • 像友盟,bugly这样的业务非必要的可以的异步加载。

    • 比如地图,推送等,非第一时间需要的可以在主线程做延时启动。当程序已经启动起来之后,在进行初始化。

    • 对于图片,网络请求框架必须在主线程里初始化了。

    • 我们一般会有闪屏页面,也可以把延时启动的地图、推送的启动放到这个页面

    按照以上方式处理后,还可以进一步降低应用启动时间。

    WebView启动优化

    • WebView第一次创建比较耗时,可以预先创建WebView,提前将其内核初始化。
    • 使用WebView缓存池,用到WebView的地方都从缓存池取,缓存池中没有缓存再创建,注意内存泄漏问题。
    • 本地预置html和css,WebView创建的时候先预加载本地html,之后通过js脚本填充内容部分。

    数据项预加载

    主页数据变化不大时候,可以再第一次启动后,缓存主页数据到本地,下次启动先读取本地数据,页面完全显示后再去请求新数据进行增量更新。

    安装包体积优化

    体积优化的必要性

    安装包体积是用户搜索应用后能第一眼看到的数据,虽然现在的应用体积越来越大,但小体积的App依旧是很多存储空间紧张用户的痛点。所以减少安装包体积是性能优化方面必不可少的一步。

    减少应用体积的N种办法

    • 使用lint工具删除无用的资源

    • 简单的切图尽量替换为shape类型的xml文件

    • 形状一致的图片只使用一个切图,比如方向不同的箭头、图像相同着色不同的切图等

    • 对图片进行压缩,优先使用WebP格式图像

    • 使用矢量图(.9)图来实现大小可变的背景图

    • 代码混淆,使用proGuard代码混淆器工具,它包括压缩、优化、混淆等功能。

    • 插件化,不需要的部分可以存在服务器,当用到时候动态下载。

    电量优化

    电量优化我放到最后说,是因为这个优先级比较低,因为一般APP在使用过程中,很难造成电量的明显下降,除非是游戏、相机或者视频类APP

    电量优化相对来说比较简单,在开发中注意一下几点就可以了:

    • 数据备份、日志报告等后台活动,可以放到电量充足或者正在充电时候执行
    • 除视屏播放外,一直避免一直亮屏。
    • 录音、GPS、相机等耗电操作,在执行完成后及时释放对应资源
    • 后台不必要的service记得及时关闭

    总结

    Android的性能优化是一个长期且漫长的过程。一般企业在开发中都是先实现功能再去管性能,这样做会导致后期优化起来麻烦且耗时。建议有可能的话尽量保持一个好的开发习惯,在项目初期就注意性能方面的事情,不要引入无用的内容、保持代码整洁、及时删除已废弃模块等,这样开发的项目才回高效且易维护。

    相关文章

      网友评论

          本文标题:Android性能优化

          本文链接:https://www.haomeiwen.com/subject/iiofmctx.html