├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── seek │ │ └── test │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── seek │ │ │ └── test │ │ │ ├── App.java │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── seek │ └── test │ └── ExampleUnitTest.java ├── build.gradle ├── caton_design.png ├── caton_log.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── seek │ │ └── caton │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── seek │ │ │ └── caton │ │ │ ├── BlockHandler.java │ │ │ ├── Caton.java │ │ │ ├── Collector.java │ │ │ ├── Config.java │ │ │ ├── FPSFrameCallBack.java │ │ │ ├── StackTraceCollector.java │ │ │ └── UILooperObserver.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── seek │ └── caton │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 1.8 62 | 63 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://jitpack.io/v/pruas/Caton.svg)](https://jitpack.io/#pruas/Caton) 2 | # Caton 3 | Caton是一个android卡顿监测模块。当UI线程卡顿(得不到执行、无反应)达到预定阈值时,将把卡顿期间线程堆栈打印出来,以便开发人员分析和优化App的性能。 4 | Caton由于本身有个收集线程堆栈的后台线程工作,所以会带来一定的性能消耗,这个大概在百分之2%-3%左右。设置收集堆栈时间越小,消耗越大。触发卡顿时间范围是500ms\~4000ms,默认为3000ms;收集时间间隔范围为500ms\~2000ms,默认为1000ms。注意:自定义时,不要把收集时间间隔设置大于触发卡顿时间间隔。 5 | # Usage 6 | Add it to your build.gradle with: 7 | ```gradle 8 | allprojects { 9 | repositories { 10 | maven { url "https://jitpack.io" } 11 | } 12 | } 13 | ``` 14 | and: 15 | 16 | ```gradle 17 | dependencies { 18 |    compile 'com.github.pruas:Caton:v1.0.1' 19 | } 20 | ``` 21 | 默认情况下,你在Application的onCreate方法中这样写就可以。 22 | ```java 23 |  Caton.initialize(this);//default 24 | ``` 25 | 26 | 你也可以使用Builder来自定义 27 | 28 | ```java 29 | // use builder build your custom way 30 | Caton.Builder builder = new Caton.Builder(this) 31 | .monitorMode(Caton.MonitorMode.FRAME)//默认监测模式为Caton.MonitorMode.LOOPER,这样指定Caton.MonitorMode.FRAME 32 | .loggingEnabled(true)// 是否打印log 33 | .collectInterval(1000) //监测采集堆栈时间间隔 34 | .thresholdTime(2000) // 触发卡顿时间阈值 35 | .callback(new Caton.Callback() { //设置触发卡顿时回调 36 | @Override 37 | public void onBlockOccurs(String[] stackTraces, String anr, long... blockArgs) { 38 | // stackTraces : 收集到的堆栈,以便分析卡顿原因。 anr : 如果应用发生ANR,这个就我ANR相关信息,没发生ANR,则为空。 39 | //采用Caton.MonitorMode.FRAME模式监测时,blockArgs的size为1,blockArgs[0] 即是发生掉帧的数。 40 | //采用Caton.MonitorMode.LOOPER模式监测时,blockArgs的size为2,blockArgs[0] 为UI线程卡顿时间值,blockArgs[1]为在此期间UI线程能执行到的时间。 41 | //这里你可以把卡顿信息上传到自己服务器 42 | } 43 | }); 44 | Caton.initialize(builder); 45 | ``` 46 | 47 | 1、监测模式 48 | 49 | 监测模式有两种: 50 | 51 |
  • Caton.MonitorMode.FRAME
  • 52 | 这种模式是通过监测绘制帧间隔时间来判断是否卡顿。也就是给Choreographer设置FrameCallback的方式。这种方式只能在API 16上才能使用,否则默认使用LOOPER模式。 53 | 54 |
  • Caton.MonitorMode.LOOPER
  • 55 | 这种模式是通过监测主线程消息处理时间来判断。也就是给主线程Looper设置Printer,来计算消息处理开始前和处理后的时间间隔判断。 56 | 57 | 2、结构原理图 58 | 59 | ![](https://github.com/pruas/Caton/blob/master/caton_design.png) 60 | 61 | 3、测试。 62 | 63 | 我们人为在MainActivity中制造卡顿: 64 | ```java 65 | public void pause(View view){ 66 | try { 67 | Thread.sleep(3000); 68 | } catch (InterruptedException e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | ``` 73 | logcat将打印出如下图: 74 | 75 | ![](https://github.com/pruas/Caton/blob/master/caton_log.png) 76 | 77 | 78 | 4、好了,是时候去测试一把了! 79 | 80 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.seek.test" 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 29 | testCompile 'junit:junit:4.12' 30 | compile project(':library') 31 | } 32 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/seek/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/seek/test/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.seek.test; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.seek.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/seek/test/App.java: -------------------------------------------------------------------------------- 1 | package com.seek.test; 2 | 3 | import android.app.Application; 4 | 5 | import com.seek.caton.Caton; 6 | 7 | /** 8 | * Created by seek on 2017/6/21. 9 | */ 10 | 11 | public class App extends Application { 12 | @Override 13 | public void onCreate() { 14 | super.onCreate(); 15 | // Caton.initialize(this);//default 16 | // Caton.setLoggingEnabled(false);//close log 17 | // use builder build your custom way 18 | Caton.Builder builder = new Caton.Builder(this) 19 | .monitorMode(Caton.MonitorMode.FRAME)//默认监测模式为Caton.MonitorMode.LOOPER,这样指定Caton.MonitorMode.FRAME 20 | .loggingEnabled(true)// 是否打印log 21 | .collectInterval(1000) //监测采集堆栈时间间隔 22 | .thresholdTime(2000) // 触发卡顿时间阈值 23 | .callback(new Caton.Callback() { //设置触发卡顿时回调 24 | @Override 25 | public void onBlockOccurs(String[] stackTraces, String anr, long... blockArgs) { 26 | // stackTraces : 收集到的堆栈,以便分析卡顿原因。 anr : 如果应用发生ANR,这个就我ANR相关信息,没发生ANR,则为空。 27 | //采用Caton.MonitorMode.FRAME模式监测时,blockArgs的size为1,blockArgs[0] 即是发生掉帧的数。 28 | //采用Caton.MonitorMode.LOOPER模式监测时,blockArgs的size为2,blockArgs[0] 为UI线程卡顿时间值,blockArgs[1]为在此期间UI线程能执行到的时间。 29 | //这里你可以卡顿信息上传到自己服务器 30 | } 31 | }); 32 | Caton.initialize(builder); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/seek/test/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.seek.test; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | public class MainActivity extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main); 13 | } 14 | 15 | public void pause(View view){ 16 | try { 17 | Thread.sleep(3000); 18 | } catch (InterruptedException e) { 19 | e.printStackTrace(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 |