美文网首页
Android性能优化实践

Android性能优化实践

作者: SevChen | 来源:发表于2018-03-27 08:59 被阅读54次

    首先,我们进行优化的目标是:

    1) 流畅: 冷/热启动快,打开页面快,某一个业务逻辑快。

    2) 稳定:内存占用小,代码结构合理。

    3)省电:CPU资源占用小

    4)安装包小: 没有无用资源。

    基于以上目标,进行了一系列的优化,总结如下。

    冷启动优化

    1. 最简单直接的办法,设置主题图片,然后在MainActivity的 onCreate前将主题图片去除
    <item name="android:windowBackground">
    @drawable/tp_ic_start_activity_background</item>
    

    目前我们就是采用上述方案,但是如果想要在APP发布后再去修改 那么上述方案就不行了,我们可以使用一个独立的Activity来放置需要显示的图片。

    以上俗称 “Splash Screen”

    这种方案其实并没有做到实际的优化,只是改善了用户体验。

    1. 将Application启动的耗时任务放到工作线程中执行,如何找出启动过程中比较耗时的任务是关键。

    SDK中提供了跟踪方法执行耗时的工具,比如:
    在Application的onCreate中加入

    Debug.startMethodTracing("Dialer_Coldstart"/*跟踪文件名*/);
    

    在MainActivity的onWindowFocusChanged中加入

    Debug.stopMethodTracing();
    

    冷启动完成后,会在手机生成一个trace文件
    /storage/emulated/0/Android/data/com.android.dialer/files/Dialer_Coldstart.trace

    使用AndroidStudio打开该文件后

    Trace文件.png

    直接输入包名,就可以快速定位耗时的函数在哪里,蓝色的条越长,耗时越多。

    在上面分析发现DialtactsActivity.onCreate占用时间比较长,因此我们也可以在onCreate里面加入

    Debug.startMethodTracing("Dialer_Coldstart"/*跟踪文件名*/);
    ...
    Debug.stopMethodTracing();
    

    重新生成trace文件进行分析,分析起来更方便。

    一般情况下,当发现耗时任务时,有如下2中处理方法:

    a. 该任务不需要在主线程中执行的:可以将其放到异步线程中执行,注意最好维护一个全局的线程池,避免野线程的存在;同时也要考虑线程并发带来的数据安全问题。

    b. 该任务必须在主线程中执行的:

    比如包含2个Fragment的ViewPager,每个Fragment的inflateView都必须在主线程中执行,考虑到冷启动只需要先初始化一个Fragment就可以了,因此,另一个Fragment可以延迟到界面稳定显示后(获取用户选择该页面后),再去加载。这样就可以节省一个Fragment的加载时间。

    具体可以采用ViewStub或者 一个空的FrameLayout实现。

    热启动优化

    Android为了提升用户体验,在用户点击返回按钮退出应用时,只是关闭了Activity,并不会杀掉应用,这样在下次启动该应用时,可以省去创建进程的时间。

    参考对应的思想,我们可以重写返回按钮的逻辑,在点击返回时,不销毁Activity,仅仅是将Activity切换到后台,这样在下次打开应用时,连Activity都不用重建,从而达到秒开。

    重写 onBackPressed:

    if (!moveTaskToBack(true)) {
                    super.onBackPressed();
     }
    

    注意,在onStop时释放资源。

    内存优化

    电话APP对内存占用不高,分析只是发现一处内存占用的问题----为了加快通话背景展示,缓存了桌面的背景或者联系人头像,目前修改了高斯模糊的流程,去除了图片的缓存,减少了4M左右内存。

    线程优化

    使用 Android Device Monitor 可以查看具体的线程

    简单HelloworldAPP启动稳定后线程 我们电话APP启动完成后线程

    相比于Helloworld程序,我把可疑的线程圈了出来,经过简单分析如下:

    1. NonUiExecutor 线程是我们自己实现的一个线程池中的线程,线程池核心线程数 为5
    private static final ExecutorService sDefaultParallelExecutor =
                Executors.newFixedThreadPool(
                    5,
                    new ThreadFactory() {
                        @Override
                        public Thread newThread(@NonNull Runnable r) {
                            Thread thread = new Thread(r, "NonUiExecutor");
                            thread.setPriority(4);
                            return thread;
                        }
                    }
                );
    

    因此NonUiExecutor线程最多存在在5个,而且会一直保持着,之所以引入这个线程池,是为了缩减线程不停创建和销毁所带来的损耗。也是为了统一规范系统的异步任务,避免野线程的出现。

    1. 除了考虑上面稳定时 线程的情况, 还需要优化在进行某些业务逻辑时,随意创建线程执行异步任务所带来的性能损耗。

    比如将APP切换到后台,再切换到前台,线程列表就出现了异常:

    切换到后台线程.png

    从线程的命名可以看出,是使用了AsyncTask来执行异步的任务,但是又没有指定线程池,因此,需要跟踪该业务逻辑。将AsyncTask提交到上面sDefaultParallelExecutor中执行,这就要求我们将sDefaultParallelExecutor暴露出来给使用者。

    资源优化

    打开Android Studio,点击菜单Analyze -> Run Inspection By Name -> 输入Unused resources 就可以查找出没有被引用的资源。

    注意,需要检查代码中是否有类似如下获取资源的代码,避免误删

    Resources r = getResources();
    
    int id = r.getIdentifier("test1", "drawable", getContext().getPackageName());
    r.getDrawable(id, null);
    

    目前电话电话APP还没进行该项优化。

    业务逻辑优化

    关于业务逻辑主要分2部分

    1. 检查耗时的任务,当发现一段业务很卡,可以使用冷启动里面介绍的MethodTrace工具生成trace文件进行分析,
      另外AndroidStudio也提供了图形界面的支持:
    StartMethodTrace.png

    点击小红点开始Record a method trace. 再点一下就停止。会在地下生成一个函数调用堆栈耗时表,大体上和分析冷启动差不多。

    2)对于分析结果的处理,除了减少耗时调用外,对于必须但耗时的操作,我们可以提前缓存,采用空间换时间的做法。
    注意,考虑内存中数据的时效性。

    比如上面的onCreateView函数,分析发现其实就是inflateView耗时,我们就可以考虑是否可以提前地去inflateView,然后缓存在内存中,等到用户点击了拨号盘后,那么就不需要再infalteView,从而加快了拨号盘的弹出时间。但是,有一个问题,如果用户一直不点击拨号盘,那么这部分内存则是浪费的。

    相关文章

      网友评论

          本文标题:Android性能优化实践

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