美文网首页
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