为了别的一篇功用优化实战方案讲解博客的结构明晰和篇幅,
咱们“望文生义”,把结构的源码解析部分搬到这边哈~

项目GitHub

目录 1. 监控周期的 界说 2. dump模块 / 关于.log文件 3. 搜集仓库周期的 设定 4. 结构的 装备存储类 以及 文件体系操作封装 5. 文件写入进程(生成.log文件的源码) 6. 上传文件 7. 规划形式、技巧 8. 结构中各个首要类的功用划分

1. 【监控周期的 界说】 blockCanary打印一轮信息的周期, 是从主线程一轮Message(使命)分发处理开端,到这个Message(使命)分发处理完毕完毕,为一轮信息; 这个周期咱们也能够成为BlockCanary监控周期/监控时刻段

2. 【dump模块 / 关于.log文件】 这一个周期的信息,除了展现在告诉处,还会展示在logcat处, 一起结构封装了dump模块, 即结构会把咱们这一轮信息,在手机(移动终端)内存中, 输出成一个.log文件; 【当然,条件是在终端需要给这个APP授权,答应APP读写内存
寄存.log文件的目录名,咱们能够在上面提到的装备类自界说

Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
如这儿界说成blockcanary, 在终端生成的文件与目录便是这样:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)

3. 【搜集仓库周期的 设定】 咱们说过装备类中,这个函数能够指定认定为卡顿的阈值时刻

Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
这儿指定为500ms,使得刚刚那个2s的堵塞被确定为卡顿问题; 其实还有一个函数, 用于指定在一个监控周期内,搜集数据的周期!!!:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
这儿回来的相同是500ms, 即从线程堵塞开端,每500ms搜集一次数据, 给出一个堵塞问题呈现的本源; 而刚刚那个卡顿问题堵塞的时刻是2s, 那毫无疑问咱们能够猜到,刚刚那个.log文件的内容里面, 有2s/500ms = 4搜集的仓库信息!! 但是一个监控周期/log文件只打印一次现场的详细信息:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
假如设置为250ms,那便是有2s/250ms = 8搜集的仓库信息了:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)

4. 【结构的 装备存储类 以及 文件体系操作封装】 结构预备了一个存储装备的类,用于存储响应的装备: 装备存储类:

Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
getPath():拿到sd卡根目录到存储log文件夹的目录途径;!!!!!!!!
detectedBlockDirectory():回来file类型存储log文件文件夹目录(假如没有这个文件夹,就创立文件夹,再回来file类型的这个文件夹);!!!!!!!!!!
getLogFiles(): 假如detectedBlockDirectory()回来的那个存储log文件文件夹目录存在的话, 就把这个目录下一切的.log文件过滤提取出来, 并存储在一个File[](即File数组)里面,终究回来这个File数组;!!!!!!!
getLogFiles()中的listFiles()是JDK中的办法, 用来回来文件夹类型的File类实例对应文件夹中(对应目录下)一切的文件, 这儿用的是它的重载办法, 便是传入一个过滤器,能够过滤掉不需要的文件;!!!!!!!
BlockLogFileFilter是过滤器,用于过滤出.log文件;
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
###下面稍微实战一下这个文件封装: 呐咱们在MainActivity的onCreate中,运用getLogFiles(), 功用是刚说的获取BlockCanary生成的一切.log文件,以.log文件的形式回来, 完了咱们把它打印出来:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
运转之后,呐,毫无悬念,BlockCanary生成的一切.log文件都被打印出来了:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)

拿到了文件, 意味着咱们能够在恰当的时机, 将之上传到服务器处理!!!

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

  • 一切要从结构的初始化开端说起:
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
  • install()做了什么, install()里面,初始化了BlockCanaryContext和Notification等的一些对象, 重要的,终究return调用了,get()
    有点单例的味道哈,BlockCanary的结构办法是私有的(下图能够见得), get()正是回来一个BlockCanary实例, 当然new这一下也就调用了BlockCanary的结构办法;
    哦~ BlockCanary的结构办法中, 调用了BlockCanaryInternals.getInstance();, 拿到一个BlockCanaryInternals实例,赋给类中的全局变量!
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    BlockCanaryInternals.getInstance();相同是运用了单例形式, 回来一个BlockCanaryInternals实例:
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    相同也是new时分调用了BlockCanaryInternals的结构办法:
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    能够看到BlockCanaryInternals的结构办法中 呈现了关于装备信息存储以及文件的写入逻辑了; LogWriter.save(blockInfo.toString());留意这儿传入的是装备信息的字符串,接着是LogWriter.save(),这儿的str便是刚刚的blockInfo.toString(),即装备信息;
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    往下还有一层save(一参对应刚刚的字符串"looper",二参为Block字符串信息【最早是来自BlockCanaryInternals中的LogWriter.save(blockInfo.toString());中的 blockInfo.toString() 】)
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    能够看到.log文件名的命名规则的便是界说在这儿了, 往.log文件写入的输入流逻辑,也都在这儿了;
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    比照一下刚刚实验的结果,也便是实际生成的.log文件文件名, 可见文件名跟上面save()办法中界说好的规则是一样的,无误;
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    这两个在表头的字符串格式化器, 榜首个是用来给.log文件命名的,.log文件名中的时刻序列来自这儿; 第二个是在save()函数中,用来写入文件的, 用时刻来区别仓库信息的每一次搜集:
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    下面这个办法是用来结构zip文件实例的, 给出一个文件名,再结构一个成对应的File实例;
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    这个则是用来删去本结构生成的一切log文件的:
    Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
    其他的很简单看懂,就不多说了;

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这儿来,

Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
或许能够参考一下 这篇博客!!!!!!!, 能够在后台敞开一个线程,守时扫描并上传。
或许 能够运用一下刚刚提到的 结构的文件体系操作封装 , 再结合 自界说网络恳求逻辑, 把文件上传到服务器也是ok的!

7. 规划形式、技巧: 7.1 单例形式,不用多说, 刚刚提到BlockCanaryBlockCanaryInternals里面都用到了; 7.2 回调机制规划: 内部接口,供应回调:

Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
界说内部接口的类,“笼统调用”回调接口办法:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
接口暴露给外部,在外部完成回调:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
8 .结构中各个首要类的功用划分 –BlockCanary 供给应外部运用的,担任结构整体的办法调度;整体的、最顶层的调度;
BlockCanaryInternals  封装操控 周期性搜集仓库信息打印、输入的要害逻辑; (卡顿断定阈值搜集信息周期 的装备,都在这儿首要被运用) (留意这儿的onBlockEvent() 回调办法
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
 封装文件操作模块(创立文件、创立文件目录、获取相关途径等等 这些 从SD卡根目录到存储.log文件目录 这个等级的处理,往下的目录下文件单位等级的处理,交给LogWriter)等中心逻辑;  调用了LogWriter.save()进行log文件存储等;  创立CpuSamplerStackSample实例,用于帮忙完结周期性搜集
LogWriter 封装了文件流写入、处理等逻辑;
LooperMonitor帮忙完结周期性搜集 【首要是堵塞使命始末的各种调度,即面向卡顿阈值; 当然,调度的内容也包含对周期性搜集启闭调度!!!!】;
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
如上, &println()有点像闹钟的角色, 它在主线程的使命分发dispatchMessage前后别离被调用一次; 它在搜集周期开端的时分,就记录下开端时刻, 在堵塞使命完结之后,会再次被调用,记录下完毕时刻
&isBlock():凭借println()中记录的关于主线程使命分发开端时刻完毕时刻, 来判断堵塞的时刻是不是大于咱们设定的或许默认卡顿判守时刻, 假如是,调用notifyBlockEvent(),直接调用到回调办法 onBlockEvent(), 这个办法上面说了,在BlockCanaryInternals 的结构器中被详细完成了, 能够调用LogWriter 终究输出.log文件;
&startDump()stopDump(): 咱们能够看到在println()中还有startDump()stopDump()这两个办法, 别离也是在主线程使命分发开端完毕时,跟着println()被调用而被调用;
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
startDump()stopDump()的内容正是操控两个Sample类的启闭
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
CpuSamplerStackSample 相同担任帮忙完结周期性搜集CpuSampler的逻辑首要是面向CPU信息的处理,而 StackSample的逻辑首要是对仓库信息的搜集; 他们都承继自AbstractSample】 首要在上面的源码咱们能够看到, 在BlockCanaryInternals 的结构器中, 就别离创立了一个CpuSampler(参数正为搜集仓库信息周期特点)和一个StackSample实例(参数为搜集仓库信息周期特点):
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 这个参数一路上走,经过CpuSampler的结构器, 终究是在CpuSampler的父类AbstractSampler中被运用!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
咱们能够看到在AbstractSampler中, AbstractSampler结构器接收了搜集仓库信息周期特点, 一起预备了一个Runnable使命单元, 使命run()中做了两件事, 榜首件事是调用笼统办法doSample(); 第二件事是基于这个搜集仓库周期特点这个Runnable单元, 创立一个循环守时使命!!!!!!!!!!!!!!!!!!!!!!!!! 即, 这个Runnable单元start()之后, 将会每隔一个搜集周期,就履行一次run()和其中的doSample(); 进行仓库信息CPU信息的周期性搜集作业; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 这便是BlockCanary能够周期搜集仓库信息的本源!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
那接下来咱们能够打开三个点, 解决这个三个疑问点,脉络就理得差不多了: 【1. 由哪个Handler来处理这个Runnable】 咱们知道, Android中的多线程使命单元能够由一个Handler去post或许postDelayed一个Runnable来敞开; 而这儿处理这个RunnableHandler正是 HandlerThreadFactory.getTimerThreadHandler()HandlerThreadFactory是结构的供给的一个内部线程类, 源码解析如下,运用了工厂形式吼:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
如此便能够获得,绑定了作业线程(子线程)的LooperHandler; 有了这个Handler就能够处理刚刚说的Runnable使命单元了;
【2. HandlerRunnable使命单元启闭是在哪个当地?】 当然是在AbstractSampler供给的start()stop()里面了;
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
CpuSamplerStackSample都会承继自AbstractSampler,自然也就承继了这start()stop(); 终究上面讲过了, 在LooperMonitorprintln()中, startDump()stopDump()会被调用, 而在startDump()stopDump()中, CpuSamplerStackSample实例的start()stop()也会被调用了, !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 从而操控了周期搜集信息作业线程(子线程)使命单元【上述的Runnable实例】的启闭 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
【3. doSample()的完成】 CpuSamplerStackSample都承继自AbstractSample, 运用的都是从AbstractSample承继过来的Runnable实例
前面说过这个Runnable单元start()之后, 将会每隔一个搜集周期,就履行一次run()和其中的doSample(); 进行仓库信息CPU信息的周期性搜集作业;
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
是这样的, 然后CpuSamplerStackSample经过对父类笼统办法doSample()做了不同的完成, 使得各自循环处理的使命内容不同罢了; 【CpuSampler的面向CPU信息的处理, 而StackSample则对仓库信息的搜集;】
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)

BlockCanaryContext 结构装备类的超类,供给应外部进行集成和装备信息:
Android卡顿优化---AndroidPerformanceMonitor(BlockCanary)源码详析(真的很详细哦!)