├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro ├── release │ ├── app-release.apk │ └── output.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── snail │ │ └── labaffinity │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── snail │ │ │ └── labaffinity │ │ │ ├── activity │ │ │ ├── BaseActivity.java │ │ │ ├── FpsTestActivity.java │ │ │ ├── LauncherTestActivity.java │ │ │ ├── LeakTestActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── SplashActivity.java │ │ │ └── TrafficTestActivity.java │ │ │ ├── adapter │ │ │ ├── MyFragment.java │ │ │ └── MyFragmentPagerAdapter.java │ │ │ ├── app │ │ │ ├── LabApplication.java │ │ │ └── MyButton.java │ │ │ ├── service │ │ │ └── BackGroundService.java │ │ │ └── utils │ │ │ ├── AppProfile.java │ │ │ ├── LogUtils.java │ │ │ └── ToastUtil.java │ └── res │ │ ├── anim │ │ ├── activity_no_anim.xml │ │ ├── activity_slide_left_in.xml │ │ ├── activity_slide_left_out.xml │ │ ├── activity_slide_right_in.xml │ │ ├── activity_slide_right_out.xml │ │ ├── anim_fade_in.xml │ │ ├── anim_fade_out.xml │ │ ├── anim_float_button_fade_in.xml │ │ ├── anim_float_button_fade_out.xml │ │ ├── anim_nug_fade_out.xml │ │ ├── anim_nug_in.xml │ │ ├── anim_nug_tran_down.xml │ │ ├── anim_set_bounce.xml │ │ ├── anim_webview_load_in.xml │ │ ├── popwindow_fade_in.xml │ │ ├── popwindow_fade_out.xml │ │ ├── popwindow_push_bottom_in.xml │ │ ├── popwindow_push_bottom_out.xml │ │ ├── popwindow_stretch_show.xml │ │ ├── slide_left_in.xml │ │ ├── slide_left_out.xml │ │ ├── slide_right_in.xml │ │ └── slide_right_out.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_second.xml │ │ └── content_main.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── snail │ └── labaffinity │ └── ExampleUnitTest.java ├── build.gradle ├── collie ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── snail │ └── collie │ ├── Collie.kt │ ├── CollieListener.kt │ ├── Config.kt │ ├── battery │ ├── BatteryInfo.kt │ ├── BatteryLevelReceiver.kt │ └── BatteryStatsTracker.kt │ ├── core │ ├── ActivityStack.kt │ ├── CollieHandlerThread.kt │ ├── ITracker.kt │ ├── LooperMonitor.kt │ ├── ProcessUtil.kt │ ├── ReflectFiled.kt │ ├── ReflectMethod.kt │ ├── ReflectUtils.kt │ └── SimpleActivityLifecycleCallbacks.kt │ ├── cpu │ └── CpuInfoTracker.kt │ ├── debug │ ├── DebugHelper.kt │ ├── FloatHelper.kt │ ├── FloatingFpsView.kt │ └── MeasureUtil.kt │ ├── fps │ ├── ANRMonitorRunnable.kt │ ├── CollectItem.kt │ ├── FpsThread.kt │ ├── FpsTracker.java │ └── ITrackFpsListener.kt │ ├── mem │ ├── AppMemory.kt │ ├── FdLeakTrack.kt │ ├── MemoryLeakTrack.kt │ ├── SystemMemory.kt │ └── TrackMemoryInfo.kt │ ├── startup │ └── LauncherTracker.kt │ └── trafficstats │ ├── ITrackTrafficStatsListener.kt │ ├── TrafficStatsItem.kt │ └── TrafficStatsTracker.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keystore.jks ├── publish.gradle ├── release.properties ├── settings.gradle └── sonar-project.properties /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | /.idea 10 | /app/build 11 | /.gralde 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Collie 2 | 3 | 轻量级Android性能监测工具 4 | 5 | * FPS监测及卡顿监测: 利用Looper的printLoop来实现 6 | * 流量监测: Trafficstats 7 | * 耗电 :Battery BroadCast 似乎意义不是特别大 8 | * 内存占用:Debug 9 | * 内存泄漏:weakHashMap 10 | * 启动耗时:ContentProvier+onwindforcus 11 | 12 | 13 | 技术文档:[Android线上轻量级APM性能监测方案](https://juejin.im/post/6872151038305140744) 14 | 15 | 16 | 17 | ### 使用方法 mavenCenter 18 | 19 | app的build.gradle添加 20 | 21 | implementation 'io.github.happylishang:collie:1.1.8' 22 | 23 | 24 | 25 | Application中添加 26 | 27 | Collie.getInstance().init(this, new Config(true, true, true, true, true, true), new CollieListener() { 28 | 29 | @Override 30 | public void onTrafficStats(Activity activity, long value) { 31 | Log.v("Collie", "" + activity.getClass().getSimpleName() + " 流量消耗 " + value * 1.0f / (1024 * 1024) + "M"); 32 | 33 | } 34 | 35 | @Override 36 | public void onBatteryCost(BatteryInfo batteryInfo) { 37 | Log.v("Collie", " 电量流量消耗 " +batteryInfo.cost); 38 | 39 | } 40 | 41 | @Override 42 | public void onAppColdLaunchCost(long duration ,String processName) { 43 | Log.v("Collie", "启动耗时 " + duration +" processName "+processName); 44 | } 45 | 46 | @Override 47 | public void onActivityLaunchCost(Activity activity, long duration,boolean finishNow) { 48 | Log.v("Collie", "activity启动耗时 " + activity + " " + duration + " finishNow "+finishNow); 49 | } 50 | 51 | @Override 52 | public void onLeakActivity(String activity, int count) { 53 | Log.v("Collie", "内存泄露 " + activity + " 数量 " + count); 54 | } 55 | 56 | @Override 57 | public void onCurrentMemoryCost(TrackMemoryInfo trackMemoryInfo) { 58 | Log.v("Collie", "内存 " + trackMemoryInfo.procName + " java内存 " 59 | + trackMemoryInfo.appMemory.dalvikPss + " native内存 " + 60 | trackMemoryInfo.appMemory.nativePss); 61 | } 62 | 63 | @Override 64 | public void onFpsTrack(Activity activity, long currentCostMils, long currentDropFrame, boolean isInFrameDraw, long averageFps) { 65 | if (currentDropFrame >= 2) 66 | Log.v("Collie", "Activity " + activity + " 掉帧 " + currentDropFrame + " 是否因为Choro 绘制掉帧 " + isInFrameDraw + " 1s 平均帧率" + averageFps); 67 | } 68 | 69 | @Override 70 | public void onANRAppear(Activity activity) { 71 | Log.v("Collie", "Activity " + activity + " ANR " ); 72 | 73 | } 74 | }); 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'com.google.gms.google-services' 4 | apply plugin: 'com.google.firebase.crashlytics' 5 | 6 | android { 7 | compileSdkVersion 33 8 | viewBinding { 9 | enabled = true 10 | } 11 | defaultConfig { 12 | applicationId "com.snail.labaffinity" 13 | minSdkVersion 21 14 | targetSdkVersion 28 15 | versionCode 1 16 | versionName "1.3" 17 | firebaseCrashlytics { 18 | nativeSymbolUploadEnabled true 19 | strippedNativeLibsDir "src/main/libs" 20 | unstrippedNativeLibsDir "src/main/libs" 21 | } 22 | } 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | 30 | compileOptions { 31 | sourceCompatibility 1.8 32 | targetCompatibility 1.8 33 | } 34 | 35 | 36 | } 37 | 38 | dependencies { 39 | implementation fileTree(dir: 'libs', include: ['*.jar']) 40 | implementation 'androidx.appcompat:appcompat:1.1.0' 41 | implementation 'com.google.android.material:material:1.1.0' 42 | implementation 'io.reactivex:rxjava:1.1.8' 43 | implementation 'io.reactivex:rxandroid:1.2.1' 44 | implementation 'com.github.campusappcn.AndRouter:router:1.2.8' 45 | implementation project(path: ':collie') 46 | // implementation 'io.github.happylishang:collie:1.1.8' 47 | // annotationProcessor 'com.github.campusappcn.AndRouter:compiler:1.2.8' 48 | implementation "androidx.core:core-ktx:1.8.0" 49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 50 | 51 | 52 | } 53 | repositories { 54 | mavenCentral() 55 | } 56 | -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "1012032823686", 4 | "firebase_url": "https://labaffinity-3f75c.firebaseio.com", 5 | "project_id": "labaffinity-3f75c", 6 | "storage_bucket": "labaffinity-3f75c.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:1012032823686:android:de46c1bd2db18425", 12 | "android_client_info": { 13 | "package_name": "com.snail.labaffinity" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "1012032823686-dc79a79gd0c5pksh6993k7a62dk6k0n7.apps.googleusercontent.com", 19 | "client_type": 1, 20 | "android_info": { 21 | "package_name": "com.snail.labaffinity", 22 | "certificate_hash": "cea4da8f6a541a140a6a64e08604bc07f87364f4" 23 | } 24 | }, 25 | { 26 | "client_id": "1012032823686-b3gkava655j7ch0ppc12rjfk2g66tmcp.apps.googleusercontent.com", 27 | "client_type": 3 28 | } 29 | ], 30 | "api_key": [ 31 | { 32 | "current_key": "AIzaSyCd7F0BkgeeZMxJpWT-NQQ2nrlns2K28mo" 33 | } 34 | ], 35 | "services": { 36 | "appinvite_service": { 37 | "other_platform_oauth_client": [ 38 | { 39 | "client_id": "1012032823686-b3gkava655j7ch0ppc12rjfk2g66tmcp.apps.googleusercontent.com", 40 | "client_type": 3 41 | } 42 | ] 43 | } 44 | } 45 | } 46 | ], 47 | "configuration_version": "1" 48 | } -------------------------------------------------------------------------------- /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/personal/software/adt-bundle-mac-x86_64-20140702/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 | -dontwarn com.tencent.bugly.** 19 | -keep public class com.tencent.bugly.**{*;} -------------------------------------------------------------------------------- /app/release/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/happylishang/Collie/bfdc6782d568bfcefef01e846e81ccfd5a7e3470/app/release/app-release.apk -------------------------------------------------------------------------------- /app/release/output.json: -------------------------------------------------------------------------------- 1 | [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] -------------------------------------------------------------------------------- /app/src/androidTest/java/com/snail/labaffinity/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 22 | 23 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/activity/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.activity; 2 | 3 | import android.os.Bundle; 4 | import android.os.SystemClock; 5 | 6 | import androidx.annotation.LayoutRes; 7 | import androidx.annotation.Nullable; 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import androidx.appcompat.widget.Toolbar; 10 | 11 | import com.snail.labaffinity.R; 12 | import com.snail.labaffinity.utils.LogUtils; 13 | 14 | /** 15 | * Author: hzlishang 16 | * Data: 16/10/12 上午9:57 17 | * Des: 18 | * version: 19 | */ 20 | public class BaseActivity extends AppCompatActivity { 21 | @Override 22 | protected void onCreate(@Nullable Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | } 25 | 26 | 27 | @Override 28 | public void setContentView(@LayoutRes int layoutResID) { 29 | super.setContentView(layoutResID); 30 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 31 | setSupportActionBar(toolbar); 32 | 33 | } 34 | 35 | 36 | @Override 37 | protected void onStop() { 38 | super.onStop(); 39 | LogUtils.v("onStop " + this + SystemClock.uptimeMillis()); 40 | 41 | } 42 | 43 | @Override 44 | protected void onPause() { 45 | super.onPause(); 46 | LogUtils.v("onPause " + this + SystemClock.uptimeMillis()); 47 | } 48 | 49 | @Override 50 | protected void onDestroy() { 51 | super.onDestroy(); 52 | LogUtils.v("onDestroy " + this + SystemClock.uptimeMillis()); 53 | } 54 | 55 | @Override 56 | protected void onResume() { 57 | super.onResume(); 58 | LogUtils.v("onResume " + this + SystemClock.uptimeMillis()); 59 | } 60 | 61 | // 第一帧的显示点,基本是在这里 62 | @Override 63 | public void onWindowFocusChanged(boolean hasFocus) { 64 | 65 | LogUtils.v("onWindowFocusChanged " + this + SystemClock.uptimeMillis()); 66 | super.onWindowFocusChanged(hasFocus); 67 | } 68 | 69 | protected void onPostResume() { 70 | 71 | super.onPostResume(); 72 | LogUtils.v("onPostResumem" + this + SystemClock.uptimeMillis()); 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/activity/FpsTestActivity.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.activity; 2 | 3 | import android.os.Bundle; 4 | import android.os.SystemClock; 5 | import android.view.ViewGroup; 6 | import android.view.ViewTreeObserver; 7 | import android.widget.TextView; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.recyclerview.widget.LinearLayoutManager; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import com.snail.labaffinity.databinding.ActivitySecondBinding; 14 | import com.snail.labaffinity.utils.LogUtils; 15 | 16 | 17 | public class FpsTestActivity extends BaseActivity { 18 | 19 | private int count; 20 | private ActivitySecondBinding mActivitySecondBinding; 21 | 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | 27 | mActivitySecondBinding = ActivitySecondBinding.inflate(getLayoutInflater()); 28 | setContentView(mActivitySecondBinding.getRoot()); 29 | LinearLayoutManager linearLayoutManager= new LinearLayoutManager(this); 30 | // linearLayoutManager.setItemPrefetchEnabled(false); 31 | 32 | mActivitySecondBinding.recy.setLayoutManager(linearLayoutManager); 33 | mActivitySecondBinding.recy.setAdapter(new RecyclerView.Adapter() { 34 | @NonNull 35 | @Override 36 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 37 | return new RecyclerView.ViewHolder(new TextView(FpsTestActivity.this)) { 38 | { 39 | 40 | } 41 | }; 42 | } 43 | 44 | @Override 45 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { 46 | // LogUtils.v("位置 " + position); 47 | if (position > 10) 48 | SystemClock.sleep(30); 49 | ((TextView) holder.itemView).setHeight(500); 50 | ((TextView) holder.itemView).setText("位置 " + position); 51 | 52 | // Log.d("lishang", Log.getStackTraceString(new Throwable())); 53 | 54 | } 55 | 56 | @Override 57 | public int getItemCount() { 58 | return 1000; 59 | } 60 | }); 61 | } 62 | 63 | 64 | @Override 65 | protected void onResume() { 66 | super.onResume(); 67 | LogUtils.v("onResume " +SystemClock.uptimeMillis()); 68 | } 69 | 70 | // 第一帧的显示点,基本是在这里 71 | @Override 72 | public void onWindowFocusChanged(boolean hasFocus) { 73 | LogUtils.v("onWindowFocusChanged " + SystemClock.uptimeMillis()); 74 | super.onWindowFocusChanged(hasFocus); 75 | } 76 | 77 | protected void onPostResume() { 78 | 79 | super.onPostResume(); 80 | LogUtils.v("onPostResumem" +SystemClock.uptimeMillis()); 81 | 82 | } 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/activity/LauncherTestActivity.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.activity; 2 | 3 | import android.os.Bundle; 4 | import android.os.SystemClock; 5 | 6 | import com.snail.labaffinity.app.MyButton; 7 | import com.snail.labaffinity.databinding.ActivitySecondBinding; 8 | import com.snail.labaffinity.utils.LogUtils; 9 | 10 | 11 | public class LauncherTestActivity extends BaseActivity { 12 | 13 | private int count; 14 | private ActivitySecondBinding mActivitySecondBinding; 15 | 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | MyButton textView = new MyButton(this); 21 | textView.setText("LauncherTestActivity"); 22 | setContentView(textView); 23 | } 24 | 25 | @Override 26 | protected void onResume() { 27 | super.onResume(); 28 | 29 | // Debug.stopMethodTracing(); 30 | 31 | 32 | // SystemClock.sleep(2000); 33 | } 34 | 35 | @Override 36 | public void onWindowFocusChanged(boolean hasFocus) { 37 | super.onWindowFocusChanged(hasFocus); 38 | } 39 | 40 | @Override 41 | public void onAttachedToWindow() { 42 | // 绘制之前 43 | super.onAttachedToWindow(); 44 | // SystemClock.sleep(1000); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/activity/LeakTestActivity.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.activity; 2 | 3 | import android.app.Activity; 4 | import android.app.ListActivity; 5 | import android.net.http.SslError; 6 | import android.os.Bundle; 7 | import android.webkit.SslErrorHandler; 8 | import android.webkit.WebChromeClient; 9 | import android.webkit.WebResourceRequest; 10 | import android.webkit.WebSettings; 11 | import android.webkit.WebView; 12 | import android.webkit.WebViewClient; 13 | import android.widget.TextView; 14 | 15 | import androidx.annotation.Nullable; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | public class LeakTestActivity extends BaseActivity { 21 | 22 | public static List sActivity=new ArrayList<>(); 23 | @Override 24 | protected void onCreate(@Nullable Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | sActivity .add(this); 27 | TextView textView = new TextView(this); 28 | textView.setText("LeakTestActivity"); 29 | setContentView(textView); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.activity; 2 | 3 | import static com.snail.labaffinity.app.LabApplication.sLaunchCost; 4 | 5 | import android.annotation.SuppressLint; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.os.Handler; 9 | import android.os.SystemClock; 10 | import android.util.Log; 11 | import android.view.View; 12 | 13 | import com.snail.labaffinity.databinding.ActivityMainBinding; 14 | import com.snail.labaffinity.service.BackGroundService; 15 | 16 | public class MainActivity extends BaseActivity { 17 | 18 | private int count; 19 | ActivityMainBinding mResultProfileBinding; 20 | long start=SystemClock.uptimeMillis(); 21 | @SuppressLint("SetTextI18n") 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | 25 | super.onCreate(savedInstanceState); 26 | 27 | startService(new Intent(this, BackGroundService.class)); 28 | 29 | mResultProfileBinding = ActivityMainBinding.inflate(getLayoutInflater()); 30 | setContentView(mResultProfileBinding.getRoot()); 31 | mResultProfileBinding.contentMain1.activityStart.setOnClickListener(new View.OnClickListener() { 32 | @Override 33 | public void onClick(View v) { 34 | // LabApplication.startTrace(MainActivity.this); 35 | Intent mIntent=new Intent( MainActivity.this, LauncherTestActivity.class); 36 | startActivity(mIntent); 37 | } 38 | }); 39 | 40 | mResultProfileBinding.contentMain1.first.setOnClickListener(v -> { 41 | Intent mIntent=new Intent( MainActivity.this, LeakTestActivity.class); 42 | startActivity(mIntent); 43 | 44 | }); 45 | mResultProfileBinding.contentMain1.second.setOnClickListener(new View.OnClickListener() { 46 | @Override 47 | public void onClick(View v) { 48 | Intent mIntent=new Intent( MainActivity.this, TrafficTestActivity.class); 49 | startActivity(mIntent); 50 | 51 | } 52 | }); 53 | mResultProfileBinding.contentMain1.third.setOnClickListener(new View.OnClickListener() { 54 | @SuppressLint("InvalidAnalyticsName") 55 | @Override 56 | public void onClick(View v) { 57 | 58 | Intent mIntent=new Intent(MainActivity.this, FpsTestActivity.class); 59 | startActivity(mIntent); 60 | } 61 | }); 62 | } 63 | 64 | @Override 65 | protected void onResume() { 66 | super.onResume(); 67 | 68 | } 69 | 70 | @Override 71 | public void onWindowFocusChanged(boolean hasFocus) { 72 | super.onWindowFocusChanged(hasFocus); 73 | } 74 | 75 | @Override 76 | protected void onPostResume() { 77 | super.onPostResume(); 78 | new Handler().postDelayed((Runnable) () -> mResultProfileBinding.contentMain1.appStart.setText("冷启动耗时" + sLaunchCost), 300); 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/activity/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.activity; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.os.SystemClock; 6 | 7 | import com.snail.labaffinity.R; 8 | import com.snail.labaffinity.utils.LogUtils; 9 | 10 | 11 | public class SplashActivity extends BaseActivity { 12 | 13 | private int count; 14 | 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_main); 20 | startActivity(new Intent(SplashActivity.this, MainActivity.class)); 21 | LogUtils.v("onCreate " + SystemClock.uptimeMillis()); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/activity/TrafficTestActivity.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.activity; 2 | 3 | import android.net.http.SslError; 4 | import android.os.Bundle; 5 | import android.webkit.SslErrorHandler; 6 | import android.webkit.WebChromeClient; 7 | import android.webkit.WebResourceRequest; 8 | import android.webkit.WebSettings; 9 | import android.webkit.WebView; 10 | import android.webkit.WebViewClient; 11 | 12 | import androidx.annotation.Nullable; 13 | 14 | public class TrafficTestActivity extends BaseActivity { 15 | 16 | 17 | @Override 18 | protected void onCreate(@Nullable Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | WebView webView = new WebView(this); 21 | 22 | WebSettings webSettings = webView.getSettings(); 23 | webSettings.setJavaScriptEnabled(true); 24 | webSettings.setDomStorageEnabled(true); 25 | webView.setWebViewClient(new WebViewClient() { 26 | @Override 27 | public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { 28 | return false; 29 | } 30 | 31 | @Override 32 | public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { 33 | super.onReceivedSslError(view, handler, error); 34 | handler.proceed(); 35 | } 36 | }); 37 | webView.setWebChromeClient(new WebChromeClient(){ 38 | 39 | @Override 40 | public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) { 41 | super.onReceivedTouchIconUrl(view, url, precomposed); 42 | } 43 | }); 44 | 45 | setContentView(webView); 46 | // webView.loadUrl("https://www.mi.com/"); 47 | webView.loadUrl("https://you.163.com/"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/adapter/MyFragment.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.adapter; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.Nullable; 5 | import androidx.fragment.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | import com.snail.labaffinity.R; 12 | 13 | /** 14 | * Author: lishang 15 | * Data: 17/1/5 下午2:10 16 | * Des: 17 | * version: 18 | */ 19 | 20 | public class MyFragment extends Fragment { 21 | 22 | 23 | public static Fragment newInstance(String msg) { 24 | Fragment fragment = new MyFragment(); 25 | Bundle bundle = new Bundle(); 26 | bundle.putString("msg", msg); 27 | fragment.setArguments(bundle); 28 | return fragment; 29 | } 30 | 31 | @Nullable 32 | @Override 33 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 34 | View root = inflater.inflate(R.layout.activity_main, container, false); 35 | TextView textView = (TextView) root.findViewById(R.id.first); 36 | textView.setText("Pos " + getArguments().getString("msg")); 37 | return root; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/adapter/MyFragmentPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.adapter; 2 | 3 | import androidx.fragment.app.Fragment; 4 | import androidx.fragment.app.FragmentManager; 5 | import androidx.fragment.app.FragmentPagerAdapter; 6 | 7 | /** 8 | * Author: lishang 9 | * Data: 17/1/5 下午2:10 10 | * Des: 11 | * version: 12 | */ 13 | 14 | public class MyFragmentPagerAdapter extends FragmentPagerAdapter { 15 | 16 | public MyFragmentPagerAdapter(FragmentManager fm) { 17 | super(fm); 18 | } 19 | 20 | @Override 21 | public Fragment getItem(int position) { 22 | return MyFragment.newInstance(String.valueOf(position)); 23 | } 24 | 25 | @Override 26 | public int getCount() { 27 | return 10; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/app/LabApplication.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.app; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.content.Context; 6 | import android.os.Debug; 7 | import android.os.SystemClock; 8 | import android.util.Log; 9 | 10 | import com.snail.collie.Collie; 11 | import com.snail.collie.CollieListener; 12 | import com.snail.collie.Config; 13 | import com.snail.collie.battery.BatteryInfo; 14 | import com.snail.collie.mem.TrackMemoryInfo; 15 | 16 | import java.io.File; 17 | 18 | import cn.campusapp.router.Router; 19 | 20 | /** 21 | * Author: hzlishang 22 | * Data: 16/10/11 下午12:44 23 | * Des: 24 | * version: 25 | */ 26 | public class LabApplication extends Application { 27 | 28 | public static long sLaunchCost; 29 | @Override 30 | public void onCreate() { 31 | super.onCreate(); 32 | sApplication = this; 33 | Router.initBrowserRouter(this); 34 | Router.initActivityRouter(getApplicationContext()); 35 | Collie.getInstance().init(this, new Config(false, true, true, true, true, true), new CollieListener() { 36 | 37 | @Override 38 | public void onActivityFocusableCost(Activity activity, long duration, boolean finishNow) { 39 | Log.v("Collie", "Activity 获取焦点" + activity + " "+ duration ); 40 | 41 | } 42 | 43 | @Override 44 | public void onTrafficStats(Activity activity, long value) { 45 | Log.v("Collie", "" + activity.getClass().getSimpleName() + " 流量消耗 " + value * 1.0f / (1024 * 1024) + "M"); 46 | 47 | } 48 | 49 | @Override 50 | public void onBatteryCost(BatteryInfo batteryInfo) { 51 | Log.v("Collie", " 电量流量消耗 " + batteryInfo.cost); 52 | 53 | } 54 | 55 | @Override 56 | public void onAppColdLaunchCost(long duration, String processName) { 57 | Log.v("Collie", "启动耗时 " + duration + " processName " + processName); 58 | sLaunchCost = duration; 59 | } 60 | 61 | @Override 62 | public void onActivityLaunchCost(Activity activity, long duration, boolean finishNow) { 63 | Log.v("Collie", "activity启动耗时 " + activity + " " + duration + " finishNow " + finishNow); 64 | } 65 | 66 | @Override 67 | public void onLeakActivity(String activity, int count) { 68 | Log.v("Collie", "内存泄露 " + activity + " 数量 " + count); 69 | } 70 | 71 | @Override 72 | public void onCurrentMemoryCost(TrackMemoryInfo trackMemoryInfo) { 73 | Log.v("Collie", "内存 " + trackMemoryInfo.procName + " java内存 " 74 | + trackMemoryInfo.appMemory.dalvikPss + " native内存 " + 75 | trackMemoryInfo.appMemory.nativePss); 76 | } 77 | 78 | @Override 79 | public void onFpsTrack(Activity activity, long currentCostMils, long currentDropFrame, boolean isInFrameDraw, long averageFps) { 80 | if (currentDropFrame >= 2) 81 | Log.v("Collie", "Activity " + activity + " 掉帧 " + currentDropFrame + " 是否因为Choro 绘制掉帧 " + isInFrameDraw + " 1s 平均帧率" + averageFps); 82 | } 83 | 84 | @Override 85 | public void onANRAppear(Activity activity) { 86 | Log.v("Collie", "Activity " + activity + " ANR "); 87 | 88 | } 89 | 90 | }); 91 | } 92 | 93 | private static Application sApplication; 94 | 95 | public static Application getContext() { 96 | return sApplication; 97 | } 98 | 99 | public static void startTrace(Context context) { 100 | File file = new File(context.getExternalFilesDir("android"), SystemClock.uptimeMillis()+"methods.trace"); 101 | Debug.startMethodTracing(file.getAbsolutePath(), 300 * 1024 * 1024); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/app/MyButton.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.app; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.os.SystemClock; 6 | import android.util.AttributeSet; 7 | 8 | import com.snail.labaffinity.utils.LogUtils; 9 | 10 | public class MyButton extends androidx.appcompat.widget.AppCompatTextView { 11 | public MyButton(Context context) { 12 | super(context); 13 | } 14 | 15 | public MyButton(Context context, AttributeSet attrs) { 16 | super(context, attrs); 17 | } 18 | 19 | public MyButton(Context context, AttributeSet attrs, int defStyleAttr) { 20 | super(context, attrs, defStyleAttr); 21 | } 22 | 23 | @Override 24 | protected void onDraw(Canvas canvas) { 25 | super.onDraw(canvas); 26 | SystemClock.sleep(300); 27 | LogUtils.v("onDraw"); 28 | } 29 | 30 | @Override 31 | protected void onAttachedToWindow() { 32 | super.onAttachedToWindow(); 33 | LogUtils.v("onAttachedToWindow"); 34 | } 35 | 36 | @Override 37 | public void onWindowFocusChanged(boolean hasWindowFocus) { 38 | super.onWindowFocusChanged(hasWindowFocus); 39 | SystemClock.sleep(300); 40 | LogUtils.v("onWindowFocusChanged 慢"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/service/BackGroundService.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.service; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | import androidx.annotation.Nullable; 7 | 8 | import com.snail.labaffinity.utils.LogUtils; 9 | 10 | /** 11 | * Author: hzlishang 12 | * Data: 16/7/5 下午2:17 13 | * Des: 14 | * version: 15 | */ 16 | public class BackGroundService extends Service { 17 | @Nullable 18 | @Override 19 | public IBinder onBind(Intent intent) { 20 | return null; 21 | } 22 | 23 | @Override 24 | public int onStartCommand(Intent intent, int flags, int startId) { 25 | LogUtils.v("onStartCommand"); 26 | return START_STICKY; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/utils/AppProfile.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.utils; 2 | 3 | import android.app.Application; 4 | 5 | import com.snail.labaffinity.app.LabApplication; 6 | 7 | /** 8 | * Author: hzlishang 9 | * Data: 16/10/11 下午12:45 10 | * Des: 11 | * version: 12 | */ 13 | public class AppProfile { 14 | 15 | public static Application getAppContext() { 16 | return LabApplication.getContext(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/utils/LogUtils.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.utils; 2 | 3 | import android.util.Log; 4 | 5 | /** 6 | * Author: hzlishang 7 | * Data: 16/10/11 下午12:48 8 | * Des: 9 | * version: 10 | */ 11 | public class LogUtils { 12 | public static void v(String msg) { 13 | Log.v("Collie", msg); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/snail/labaffinity/utils/ToastUtil.java: -------------------------------------------------------------------------------- 1 | package com.snail.labaffinity.utils; 2 | 3 | import android.widget.Toast; 4 | 5 | /** 6 | * Author: hzlishang 7 | * Data: 16/10/11 下午12:47 8 | * Des: 9 | * version: 10 | */ 11 | public class ToastUtil { 12 | 13 | public static void show(String msg) { 14 | Toast.makeText(AppProfile.getAppContext(), msg, Toast.LENGTH_SHORT).show(); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/anim/activity_no_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/anim/activity_slide_left_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/activity_slide_left_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/activity_slide_right_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/activity_slide_right_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_float_button_fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_float_button_fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_nug_fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_nug_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_nug_tran_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_set_bounce.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_webview_load_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/popwindow_fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/anim/popwindow_fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/anim/popwindow_push_bottom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/anim/popwindow_push_bottom_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/anim/popwindow_stretch_show.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_left_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_left_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_right_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_right_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | 23 | 24 | 27 | 28 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_second.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 19 | 20 | 24 | 25 |