Android卡顿优化 | 基于AndroidPerformanceMonitor源码简析

1. 【监控周期的 定义】
blockCanary打印一轮信息的周期,
是从主线程一轮阻塞的开始开始,到阻塞的结束结束,为一轮信息;
这个周期我们也可以成为BlockCanary的监控周期/监控时间段;

2. 【dump模块 / 关于.log文件】
这一个周期的信息,除了展现在通知处,还会展示在logcat处,
同时框架封装了dump模块,
即框架会把我们这一轮信息,在手机(移动终端)内存中,
输出成一个.log文件;
【当然,前提是在终端需要给这个APP授权,允许APP读写内存】

存放.log文件的目录名,我们可以在上面提到的配置类中自定义:
 

9125154-c68447dd12f8b0a0.png如这里定义成blockcanary
在终端生成的文件与目录便是这样:

9125154-5cfa6de13aaad887.png

3. 【采集堆栈周期的 设定】
我们说过配置类中,这个函数可以指定认定为卡顿的阈值时间

9125154-41aca50ab00be0a2.png

这里指定为500ms,使得刚刚那个2s的阻塞被确定为卡顿问题
其实还有一个函数,
用于指定在一个监控周期内,采集数据的周期!!!:'

9125154-936d378191e9e4bc.png

这里返回的同样是500ms,
即从线程阻塞开始,每500ms采集一次数据,
给出一个阻塞问题出现的根源;
而刚刚那个卡顿问题阻塞的时间是2s,
那毫无疑问我们可以猜到,刚刚那个.log文件的内容里边,
有2s/500ms = 4次采集的堆栈信息!!
但是一个监控周期/log文件只打印一次现场的详细信息:
————————————————
9125154-62feab92fba2e0ca.png

如果设置为250ms,那便是有2s/250ms = 8采集的堆栈信息了:

9125154-e929be7ccc0285c2.png

4. 【框架的 配置存储类 以及 文件系统操作封装】
框架准备了一个存储配置的类,用于存储响应的配置:
配置存储类:

9125154-e201c4bec1654ab2.png

-getPath():拿到sd卡根目录到存储log文件夹的目录路径;!!!!!!!!

-detectedBlockDirectory():返回file类型的 存储log文件的文件夹的目录(如果没有这个文件夹,就创建文件夹,再返回file类型的这个文件夹);!!!!!!!!!!

-getLogFiles():
如果detectedBlockDirectory()返回的那个存储log文件的文件夹的目录存在的话,
就把这个目录下所有的.log文件过滤提取出来,
并存储在一个File[](即File数组)里边,最后返回这个File数组;!!!!!!!

-getLogFiles()中的listFiles()是JDK中的方法,
用来返回文件夹类型的File类实例其 对应文件夹中(对应目录下)所有的文件,
这里用的是它的重载方法,
就是传入一个过滤器,可以过滤掉不需要的文件;!!!!!!!
-BlockLogFileFilter是过滤器,用于过滤出.log文件;

9125154-124f785b360caaaa.png

###下面稍微实战一下这个文件封装:
呐我们在MainActivity的onCreate中,使用getLogFiles()
功能是刚说的获取BlockCanary生成的所有.log文件,以.log文件的形式返回,
完了我们把它打印出来:

9125154-75440b5c0b43a7e4.png

运行之后,呐,毫无悬念,BlockCanary生成的所有.log文件都被打印出来了:

9125154-e6a4067310b10a13.png拿到了文件,
意味着我们可以在适当的时机,
将之上传到服务器处理!!!

5. 【文件写入过程(生成.log文件的源码)】\

一切要从框架的初始化开始说起:

9125154-9e1f6b7d49d74bf1.png

nstall()做了什么,
install()里边,初始化了BlockCanaryContext和Notification等的一些对象,
重要的,最后return调用了,get();

有点单例的味道哈,BlockCanary的构造方法是私有的(下图可以见得),
get()正是返回一个BlockCanary实例,
当然new这一下也就调用了BlockCanary的构造方法;

哦~ BlockCanary的构造方法中,
调用了BlockCanaryInternals.getInstance();,
拿到一个BlockCanaryInternals实例,赋给类中的全局变量!
9125154-e60deb03b98c15e8.png9125154-49ffbe993e2c6953.png

同样也是new时候调用了BlockCanaryInternals的构造方法:

9125154-994f470c3b754409.png

可以看到BlockCanaryInternals的构造方法中
出现了关于配置信息存储类以及文件的写入逻辑了;
LogWriter.save(blockInfo.toString());注意这里传入的是配置信息的字符串,接着是LogWriter.save(),这里的str便是刚刚的blockInfo.toString(),即配置信息;
————————————————
9125154-d83c02d5dbd47c21.png

往下还有一层save(一参对应刚刚的字符串"looper",二参为Block字符串信息【最早是来自BlockCanaryInternals中的LogWriter.save(blockInfo.toString());中的 blockInfo.toString() 】)

9125154-969378d61dc8a7e8.png

可以看到.log文件名的命名规则的就是定义在这里了,
.log文件写入的输入流逻辑,也都在这里了;

9125154-e6a4067310b10a13.png

对比一下刚刚实验的结果,也就是实际生成的.log文件文件名
可见文件名跟上面save()方法中定义好的规则是一样的,无误;

这两个在表头的字符串格式化器,
第一个是用来给.log文件命名的,.log文件名中的时间序列来自这里;
第二个是在save()函数中,用来写入文件的,
用时间来区分堆栈信息的每一次收集:

9125154-95d0e6b2da49b74b.png


下面这个方法是用来构造zip文件实例的,
给出一个文件名,再构造一个成对应的File实例;

9125154-e230c532fc7c5282.png

这个则是用来删除本框架生成的所有log文件的:

9125154-24cebc00df09c0cb.png

6.【上传文件】
首先框架想得很周到哈,它已经为我们封装了一个Uploader类,源码如下:

/*
 * Copyright (C) 2016 MarkZhai (http://zhaiyifan.cn).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
...
final class Uploader {
 
    private static final String TAG = "Uploader";
    private static final SimpleDateFormat FORMAT =
            new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
 
    private Uploader() {
        throw new InstantiationError("Must not instantiate this class");
    }
 
    private static File zip() {
        String timeString = Long.toString(System.currentTimeMillis());
        try {
            timeString = FORMAT.format(new Date());
        } catch (Throwable e) {
            Log.e(TAG, "zip: ", e);
        }
        File zippedFile = LogWriter.generateTempZip("BlockCanary-" + timeString);
        BlockCanaryInternals.getContext().zip(BlockCanaryInternals.getLogFiles(), zippedFile);
        LogWriter.deleteAll();
        return zippedFile;
    }
 
    public static void zipAndUpload() {
        HandlerThreadFactory.getWriteLogThreadHandler().post(new Runnable() {
            @Override
            public void run() {
                final File file = zip();
                if (file.exists()) {
                    BlockCanaryInternals.getContext().upload(file);
                }
            }
        });
    }
}

都封装成zip文件了,想得很周到很齐全吼,
点一下这个upload,又回到配置类BlockCanaryContext这儿来,

9125154-0d0f3736236e559c.png

或者可以参考一下 这篇博客!!!!!!!,
可以在后台开启一个线程,定时扫描并上传。

或者
可以利用一下刚刚提到的 框架的文件系统操作封装 ,
再结合 自定义网络请求逻辑,
把文件上传到服务器也是ok的!
7. 设计模式、技巧:
7.1 单例模式,不用多说,
刚刚提到BlockCanary和BlockCanaryInternals里边都用到了;
7.2 回调机制设计:
内部接口,供给回调

定义内部接口的类,“抽象调用”回调接口方法:

9125154-25ac85dc6db6a685.png

接口暴露给外部,在外部实现回调:

9125154-da0feb415a3ca0e9.png

7.3 各个主要类的功能划分
-BlockCanary 提供给外部使用的,负责框架整体的方法调度;整体的、最顶层的调度;
-BlockCanaryInternals 封装文件IO操作模块(创建文件、创建文件目录等)等核心逻辑;调用了LogWriter.save()进行log文件存储等;
-BlockCanaryContext 框架配置类的超类,提供给外部进行集成和配置信息:

-LogWriter 封装了文件流的写入、处理等逻辑;
————————————————
版权声明:本文为CSDN博主「凌川江雪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/aaLiweipeng/article/details/105307312

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值