diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c7ab2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties +token.properties +keystore.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# Intellij +*.iml +.idea/ +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/dictionaries +.idea/libraries + +# Keystore files +*.jks + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json +apktool/ +app/sampledata/ +psFiles/ +projectFilesBackup/ +*.hprof +checksum.txt +RouterDocument/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5398201 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# Library Analysis Gradle Plugin + +## 概述 + +分析各依赖库文件的大小 + +1. 支持大文件提醒 +2. 支持忽略部分依赖库大小(大小显示为灰色) +3. 支持依赖节点大小统计 +4. 输出aar的PackageName以及冲突提示 +5. 输出依赖库被直接依赖次数(Used)、包含的依赖库数量(Contains) +6. 标识可移除的依赖库(实验性功能) + +>可移除的依赖库
+例如有这样的依赖关系:A->B->C, A->C,那么A~~->C~~ + +## 配置 + +```gradle +buildscript { + repositories { + // ... + maven { url "https://jitpack.io" } + } + dependencies { + classpath 'com.timecat.plugin:report:#LAST_VERSION' + } +} + +apply plugin: 'com.timecat.plugin.report' + +libReport { + output = [ + "txt", "html" // default + ] + ignore = [ + "com.android.support:support-v4" + ] + log = true +} +``` + +## 使用 + +``` +gradle libReportReleaseCompileClasspath ... +``` + +### output + +**/build/reports/timecat/analysis/library/compile/Tree.html** + +![screenshot](./image/module-list.png) + +![screenshot](./image/tree-x.png) + +**/build/reports/timecat/analysis/library/compile/Tree.txt** + +``` + 6.050 MB 0 B \--- project :analysis-sample + 20.275 KB 20.275 KB +--- com.android.support:multidex:1.0.1 + 0 B 0 B +--- project :module-library + 174 KB 104 KB +--- com.squareup.moshi:moshi:1.2.0 + 70.328 KB 70.328 KB | \--- com.squareup.okio:okio:1.8.0 + 199 KB 199 KB +--- com.alibaba:fastjson:1.1.54.android + 1.432 MB 1.143 MB +--- com.fasterxml.jackson.core:jackson-databind:2.7.0 + 49.701 KB 49.701 KB | +--- com.fasterxml.jackson.core:jackson-annotations:2.7.0 + 246 KB 246 KB | \--- com.fasterxml.jackson.core:jackson-core:2.7.0 + 314 KB 259 KB +--- com.github.Raizlabs.DBFlow:dbflow:3.1.1 + 33.072 KB 33.072 KB | +--- com.github.Raizlabs.DBFlow:dbflow-core:3.1.1 + 21.275 KB 21.275 KB | \--- com.android.support:support-annotations:23.1.1 -> 24.1.0 + 325 KB 119 KB +--- com.squareup.retrofit:retrofit:1.9.0 + 206 KB 206 KB | \--- com.google.code.gson:gson:2.3.1 + 492 KB 86.272 KB +--- com.squareup.retrofit2:retrofit:2.1.0 + 406 KB 335 KB | \--- com.squareup.okhttp3:okhttp:3.3.0 + 70.328 KB 70.328 KB | \--- com.squareup.okio:okio:1.8.0 + 464 KB 464 KB +--- com.github.bumptech.glide:glide:3.7.0 + 118 KB 118 KB +--- com.squareup.picasso:picasso:2.5.2 + 1.456 MB 11.842 KB +--- com.facebook.fresco:fresco:0.13.0 + 200 KB 106 KB | +--- com.facebook.fresco:drawee:0.13.0 + 0 B 1.167 MB | | +--- com.android.support:support-v4:23.2.1 + 0 B 21.275 KB | | | \--- com.android.support:support-annotations:23.2.1 -> 24.1.0 + 93.288 KB 93.288 KB | | \--- com.facebook.fresco:fbcore:0.13.0 + 93.288 KB 93.288 KB | +--- com.facebook.fresco:fbcore:0.13.0 + 1.341 MB 1.114 MB | \--- com.facebook.fresco:imagepipeline:0.13.0 + 0 B 1.167 MB | +--- com.android.support:support-v4:23.2.1 + 232 KB 102 KB | +--- com.facebook.fresco:imagepipeline-base:0.13.0 + 0 B 1.167 MB | | +--- com.android.support:support-v4:23.2.1 + 37.199 KB 37.199 KB | | +--- com.parse.bolts:bolts-tasks:1.4.0 + 93.288 KB 93.288 KB | | \--- com.facebook.fresco:fbcore:0.13.0 + 37.199 KB 37.199 KB | +--- com.parse.bolts:bolts-tasks:1.4.0 + 93.288 KB 93.288 KB | \--- com.facebook.fresco:fbcore:0.13.0 + 18.699 KB 18.699 KB +--- com.jakewharton.timber:timber:4.3.0 + 1.111 MB 9.466 KB +--- io.reactivex:rxandroid:1.2.1 + 1.102 MB 1.102 MB | \--- io.reactivex:rxjava:1.1.6 -> 1.3.0 + 1.102 MB 1.102 MB +--- io.reactivex:rxjava:1.3.0 + 54.464 KB 16.989 KB +--- com.jakewharton:butterknife:8.4.0 + 37.475 KB 16.199 KB | +--- com.jakewharton:butterknife-annotations:8.4.0 + 21.275 KB 21.275 KB | | \--- com.android.support:support-annotations:24.1.0 + 21.275 KB 21.275 KB | \--- com.android.support:support-annotations:24.1.0 + 9.570 KB 9.570 KB +--- com.jakewharton.scalpel:scalpel:1.1.2 + 3.603 KB 3.603 KB \--- com.jakewharton:process-phoenix:1.0.2 + ... +``` diff --git a/analysis-sample/.gitignore b/analysis-sample/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/analysis-sample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/analysis-sample/build.gradle b/analysis-sample/build.gradle new file mode 100644 index 0000000..2007aa4 --- /dev/null +++ b/analysis-sample/build.gradle @@ -0,0 +1,79 @@ +apply plugin: 'com.android.application' +//apply plugin: 'com.timecat.plugin.report' +//apply plugin: 'kotlin-android' +//apply plugin: 'kotlin-android-extensions' + +android { + compileSdk 30 + + defaultConfig { + applicationId "com.timecat.android.analysis.sample" + minSdkVersion 9 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'com.android.support:multidex:1.0.1' + implementation project(":module-library") + + implementation 'com.squareup.moshi:moshi:1.2.0' + implementation 'com.alibaba:fastjson:1.1.54.android' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.7.0' + + implementation "com.github.Raizlabs.DBFlow:dbflow:3.1.1" + + implementation 'com.squareup.retrofit:retrofit:1.9.0' + implementation 'com.squareup.retrofit2:retrofit:2.1.0' + + implementation 'com.github.bumptech.glide:glide:3.7.0' + implementation 'com.squareup.picasso:picasso:2.5.2' + implementation 'com.facebook.fresco:fresco:0.13.0' + + implementation 'com.jakewharton.timber:timber:4.3.0' + + implementation 'io.reactivex:rxandroid:1.2.1' + implementation 'io.reactivex:rxjava:1.3.0' +// implementation 'io.reactivex.rxjava2:rxjava:2.0.0-RC2' + + implementation 'com.jakewharton:butterknife:8.4.0' + implementation 'com.jakewharton.scalpel:scalpel:1.1.2' + implementation 'com.jakewharton:process-phoenix:1.0.2' + + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + + testImplementation 'junit:junit:4.12' +} + +//libReport { +//// log = true +//// fullTree = true +//// output = [ +//// "txt", "html" +//// ] +// ignore = [ +// "com.android.support:support-v4" +// ] +//} \ No newline at end of file diff --git a/analysis-sample/proguard-rules.pro b/analysis-sample/proguard-rules.pro new file mode 100644 index 0000000..eeb461f --- /dev/null +++ b/analysis-sample/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\sdk\Android/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/analysis-sample/src/androidTest/java/com/timecat/android/analysis/sample/ExampleInstrumentedTest.java b/analysis-sample/src/androidTest/java/com/timecat/android/analysis/sample/ExampleInstrumentedTest.java new file mode 100644 index 0000000..38fcb27 --- /dev/null +++ b/analysis-sample/src/androidTest/java/com/timecat/android/analysis/sample/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.timecat.android.analysis.sample; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.timecat.android.analysis.sample", appContext.getPackageName()); + } +} diff --git a/analysis-sample/src/main/AndroidManifest.xml b/analysis-sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ccc11f8 --- /dev/null +++ b/analysis-sample/src/main/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/analysis-sample/src/main/java/com/timecat/android/analysis/sample/BaseActivity.java b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/BaseActivity.java new file mode 100644 index 0000000..485df2f --- /dev/null +++ b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/BaseActivity.java @@ -0,0 +1,91 @@ +package com.timecat.android.analysis.sample; + +import android.app.Activity; +import android.os.Bundle; +import android.support.annotation.Nullable; + +import rx.Observable; +import rx.Observable.OnSubscribe; +import rx.Subscriber; +import rx.functions.Func1; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +public abstract class BaseActivity extends Activity { + + Observable ready; + + @Override + protected final void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + onBaseCreate(savedInstanceState); + onAfterCreate(savedInstanceState); + } + + private void onAfterCreate(@Nullable Bundle state) { + if (InitManager.isHasInit()) { + ready = null; + afterCreate(state); + return; + } + createReady(state); + } + + private void createReady(Bundle state) { + if (ready == null) { + ready = InitManager + .ready() + .map(new Func1() { + @Override + public Object call(Object result) { + afterCreate(state); + return result; + } + }); + } + } + + protected abstract void afterCreate(@Nullable Bundle state); + + protected abstract void onBaseCreate(@Nullable Bundle savedInstanceState); + + @Override + protected void onResume() { + super.onResume(); + if (ready == null) { + onObsResume(); + } else { + Observable obs = genObsResume(); + if (obs != null) { + Observable.concat(ready, obs).subscribe(); + } else { + ready.subscribe(); + } + } + } + + @Override + protected void onPause() { + super.onPause(); + ready = null; + } + + protected Observable genObsResume() { + return Observable.create(new OnSubscribe() { + @Override + public void call(Subscriber subscriber) { + BaseActivity.this.onObsResume(); + subscriber.onNext(null); + subscriber.onCompleted(); + } + }); + } + + protected void onObsResume() { + } +} diff --git a/analysis-sample/src/main/java/com/timecat/android/analysis/sample/DemoApplication.java b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/DemoApplication.java new file mode 100644 index 0000000..10b7932 --- /dev/null +++ b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/DemoApplication.java @@ -0,0 +1,20 @@ +package com.timecat.android.analysis.sample; + +import android.app.Application; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +public class DemoApplication extends Application { + + @Override + public void onCreate() { + super.onCreate(); + + InitManager.initLazy(this); + } +} diff --git a/analysis-sample/src/main/java/com/timecat/android/analysis/sample/InitManager.java b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/InitManager.java new file mode 100644 index 0000000..5b0cf15 --- /dev/null +++ b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/InitManager.java @@ -0,0 +1,65 @@ +package com.timecat.android.analysis.sample; + + +import android.content.Context; +import android.os.SystemClock; + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Func0; +import rx.schedulers.Schedulers; + +/** + * @author 林学渊 + * @email linxy59@mail2.sysu.edu.cn + * @date 2021/9/10 + * @description null + * @usage null + */ +public class InitManager { + + private static Context sContext; + + private static Context sCache; + private static boolean hasInit; + + public static void initLazy(Context ctx) { + sCache = ctx; + } + + public static boolean isHasInit() { + return hasInit; + } + + public static Context getContext() { + return sContext; + } + + public static Observable ready() { + return Observable.defer( + new Func0>() { + @Override + public Observable call() { + init(sCache); + return Observable.just(true); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + public static void clear() { + hasInit = false; + sContext = null; + } + + private static synchronized void init(Context ctx) { + if (hasInit) { + return; + } + SystemClock.sleep(2000); + sContext = ctx; + hasInit = true; + } + +} diff --git a/analysis-sample/src/main/java/com/timecat/android/analysis/sample/MainActivity.java b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/MainActivity.java new file mode 100644 index 0000000..966189c --- /dev/null +++ b/analysis-sample/src/main/java/com/timecat/android/analysis/sample/MainActivity.java @@ -0,0 +1,43 @@ +package com.timecat.android.analysis.sample; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.TextView; + +public class MainActivity extends BaseActivity { + + TextView mTextMain; + Button mBtnGo; + + @Override + protected void afterCreate(@Nullable Bundle state) { + Log.d("MAIN", "set fail!"); + mTextMain.setText(InitManager.getContext().getString(R.string.fail)); + } + + @Override + protected void onBaseCreate(@Nullable Bundle savedInstanceState) { + setContentView(R.layout.activity_main); + mTextMain = (TextView) findViewById(R.id.tv_main); + mBtnGo = (Button) findViewById(R.id.btn_go); + } + + @Override + protected void onObsResume() { + super.onObsResume(); + mTextMain.setText(InitManager.getContext().getString(R.string.success)); + mBtnGo.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View listener) { + InitManager.clear(); + Intent intent = new Intent(MainActivity.this, MainActivity.class); + MainActivity.this.startActivity(intent); + } + }); + } +} diff --git a/analysis-sample/src/main/res/layout/activity_main.xml b/analysis-sample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..1133aa4 --- /dev/null +++ b/analysis-sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,26 @@ + + + + + +