关于Android性能优化----卡顿优化步骤

前言

无论是启动,内存,布局等等这些优化,最终的目的就是为了应用不卡顿。应用的体验性好坏,最直观的表现就是应用的流畅程度,用户不知道什么启动优化,内存不足,等等,应用卡顿,那么这个应用就不行,被卸载的概率非常大。所以说为了保证用户留存率,卡顿优化是非常非常的重要。在这篇文章,咱们不讨论是什么原因造成卡顿,其实在前面写的性能优化文章中,都是造成卡顿的原因,需要需要做好卡顿优化,最好从头开始一步一步来处理。今天我们主要是介绍一些针对卡顿检测的一些工具使用。
检测卡顿常用工具
Systrace

Systrace这个工具在《布局优化》一章节中已经介绍过了,这里就不在赘述。地址:https://www.cnblogs.com/huangjialin/p/13353541.html
StrictMode的使用

StrictMode,严苛模式。StrictMode是在Android开发过程中不可缺少的性能检测工具,它能够检测出在开发过程中不合理的代码块,非常方便。
策略分类

StrictMode分为线程策略(ThreadPolicy)和虚拟机策略(VmPolicy)
使用方式

//开启Thread策略模式
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectNetwork()//监测主线程使用网络io
// .detectCustomSlowCalls()//监测自定义运行缓慢函数
// .detectDiskReads() // 检测在UI线程读磁盘操作
// .detectDiskWrites() // 检测在UI线程写磁盘操作
.detectAll()
.penaltyLog() //写入日志
.penaltyDialog()//监测到上述状况时弹出对话框
.build());

    //开启VM策略模式
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()

// .detectLeakedSqlLiteObjects()//监测sqlite泄露
// .detectLeakedClosableObjects()//监测没有关闭IO对象
// .setClassInstanceLimit(MainActivity.class, 1) // 设置某个类的同时处于内存中的实例上限,可以协助检查内存泄露
// .detectActivityLeaks()
.detectAll()
.penaltyLog()//写入日志
.build());

上面基本都注释好了,这里就不在一一说明了。如果我们在开发过程中,能够通过StrictMode这个工具类来规避掉这些问题,那么将会大大的减少很多性能相关的问题。
BlockCanary使用! http://www.lsszyp.com

我们先看看怎么使用,然后在看BlockCanary
依赖

debugImplementation ‘com.github.bzcoder:blockcanarycompat-android:0.0.4’

在application中

BlockCanary.install(mContext, appBlockCanaryContext).start();

appBlockCanaryContext类

/**

  • BlockCanary配置的各种信息
    */
    public class AppBlockCanaryContext extends BlockCanaryContext {

    /**

    • Implement in your project.
    • @return Qualifier which can specify this installation, like version + flavor.
      */
      public String provideQualifier() {
      return “unknown”;
      }

    /**

    • Implement in your project.
    • @return user id
      */
      public String provideUid() {
      return “uid”;
      }

    /**

    • Network type
    • @return {@link String} like 2G, 3G, 4G, wifi, etc.
      */
      public String provideNetworkType() {
      return “unknown”;
      }

    /**

    • Config monitor duration, after this time BlockCanary will stop, use
    • with {@code BlockCanary}'s isMonitorDurationEnd
    • @return monitor last duration (in hour)
      */
      public int provideMonitorDuration() {
      return -1;
      }

    /**

    • Config block threshold (in millis), dispatch over this duration is regarded as a BLOCK. You may set it
    • from performance of device.
    • @return threshold in mills
      */
      public int provideBlockThreshold() {
      return 500;
      }

    /**

    • Thread stack dump interval, use when block happens, BlockCanary will dump on main thread
    • stack according to current sample cycle.
    • Because the implementation mechanism of Looper, real dump interval would be longer than
    • the period specified here (especially when cpu is busier).
    • @return dump interval (in millis)
      */
      public int provideDumpInterval() {
      return provideBlockThreshold();
      }

    /**

    • Path to save log, like “/blockcanary/”, will save to sdcard if can.
    • @return path of log files
      */
      public String providePath() {
      return “/blockcanary/”;
      }

    /**

    • If need notification to notice block.
    • @return true if need, else if not need.
      */
      public boolean displayNotification() {
      return true;
      }

    /**

    • Implement in your project, bundle files into a zip file.
    • @param src files before compress
    • @param dest files compressed
    • @return true if compression is successful
      */
      public boolean zip(File[] src, File dest) {
      return false;
      }

    /**

    • Implement in your project, bundled log files.
    • @param zippedFile zipped file
      */
      public void upload(File zippedFile) {
      throw new UnsupportedOperationException();
      }

    /**

    • Packages that developer concern, by default it uses process name,
    • put high priority one in pre-order.
    • @return null if simply concern only package with process name.
      */
      public List concernPackages() {
      return null;
      }

    /**

    • Filter stack without any in concern package, used with @{code concernPackages}.
    • @return true if filter, false it not.
      */
      public boolean filterNonConcernStack() {
      return false;
      }

    /**

    • Provide white list, entry in white list will not be shown in ui list.
    • @return return null if you don’t need white-list filter.
      */
      public List provideWhiteList() {
      LinkedList whiteList = new LinkedList<>();
      whiteList.add(“org.chromium”);
      return whiteList;
      }

    /**

    • Whether to delete files whose stack is in white list, used with white-list.
    • @return true if delete, false it not.
      */
      public boolean deleteFilesInWhiteList() {
      return true;
      }

    /**

    • Block interceptor, developer may provide their own actions.
      */
      public void onBlock(Context context, BlockInfo blockInfo) {
      Log.i(“lz”,"blockInfo "+blockInfo.toString());
      }
      }

就是这么简单,一旦发生卡顿,那么将会以通知的形式在通知栏中显示出对应的堆栈信息(和leakcanary类似,但是两者没有任何关系)。或者在
SD卡中的blockcanary文件夹中生成对应的log日志

那么,问题来了BlockCanary是怎么检测卡顿的呢?大家注意AppBlockCanaryContext类中有一个provideBlockThreshold()方法,return了一个500ms。我们知道Android中消息的分发处理都是通过handler来的,handler中有一个looper对象,looper对象中有一个loop方法,看一下源码

for (;😉 {
Message msg = queue.next(); // might block
…//省略若干
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}

       ...//省略若干
        
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...//省略若干

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

       ...//省略若干
    }

应用中的所有事件都是通过dispatchMessage这个方法来进行处理,那么是不是说dispatchMessage执行的时间就是某个事件执行的时间。如果这个时间大于我们在provideBlockThreshold()定义的时间,我们就认为发送了卡顿了,需要优化,就会获取到对应的堆栈信息保存起来并提示。
ANR-WatchDog介绍

发生卡顿,严重时很容易导致anr,我们知道发生anr的情况基本上就是以下几种情况
1、触摸事件在5s没有得到响应。
2、Bradcast 是10s,后台广播是60s
3、service:前台service 20s 后台service 200s

用户在使用应用时,出现ANR,那么体验将会是非常非常差,现在很多手机厂商为了用户体验,去掉了这个ANR弹框,但是还是会一直卡在这里。
ANR-WatchDog 使用
添加依赖

implementation ‘com.github.anrwatchdog:anrwatchdog:1.3.0’

在application中的onCreate()方法

new ANRWatchDog().setIgnoreDebugger(true).start();
http://www.xyhwbz.com
setIgnoreDebugger 是指忽略点断点的情况
当然如果这样设置的话,一旦出现ANR,那么ANR-Watchdog是会将应用杀死的,如果不想杀死应用那么需要设置一个监听

new ANRWatchDog().setANRListener(new ANRWatchDog.ANRListener() {
@Override
public void onAppNotResponding(ANRError error) {
// Handle the error. For example, log it to HockeyApp:

}

}).start();

ANR-WatchDog 原理
我用自己的话来总结一下原理:前面我们说了所有的消息都是通过handler中的dispatchMessage方法进行分发的,而ANR-WatchDog 原理就是通过dispatchMessage的时间长度来判断是否出现anr的,过程大概是这样的:ANR-WatchDog 开启一个监控线程,并向主线程发一个消息,然后自己休眠5s,5s后看主线程有没有处理这个消息,如果处理了,那么继续发送,进入一下次检查。如果没有处理,说明主线程发生了阻塞,收集对应的anr信息。
Lancet的介绍

前面写的几篇性能优化文章基本都介绍有对应的hook框架,这里再介绍一个AOP框架—Lancet,这几个框架的比对,后面会再开一篇。这里先看看怎么使用。Lancet是一个轻量级Android AOP框架,特点:
1、编译速度快,并且支持增量编译
2、简洁的API,几行代码完成注入需求
3、没有任何多余代码插入apk
4、支持用于SDK,可以在SDK编写注入代码来修改依赖SDK的APP
http://www.yxgcjs.com
使用方式
在根目录的build.gradle
dependencies{
classpath ‘me.ele:lancet-plugin:1.0.5’
}

在APP目录的build.gradle
apply plugin: ‘me.ele.lancet’

dependencies {
provided ‘me.ele:lancet-base:1.0.5’
}

开一个类aaaa.java

@Proxy(“i”)
@TargetClass(“android.util.Log”)
public static int anyName(String tag, String msg){
msg = msg + “lancet”;
return (int) Origin.call();
}

这样,就完成了,当我们通过Log.i(“lancet”,"你好“);来打印日志的时候,输出的文本先被Lancet出来后,在输出。“你好,lancet”
常见卡顿问题解决方案

1、内存抖动的问题,GC过于频繁
2、方法太耗时了(CPU占用)
3、布局过于复杂,渲染速度慢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值