Skip to content

Commit

Permalink
Filters feature (#55)
Browse files Browse the repository at this point in the history
All changes related to java module. Implemented filters feature (issue #48). Added default filterset (filters/default.json). Changed package name. Java code re-factored. Added unit tests. Updated run, release, build scripts. Updated Readme.
  • Loading branch information
alexzaitsev authored Jul 17, 2019
1 parent fe9557c commit 2ad9271
Show file tree
Hide file tree
Showing 36 changed files with 1,249 additions and 375 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ apk-dependency-graph-scripts-*.zip
.ant
.gradle
# lib/ directory where the dependencies are downloaded by Ivy Apache.
lib
lib
filters/*.json
!filters/default.json
.vscode
9 changes: 5 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ jdk: openjdk8

install: skip

before_script: chmod +x ./gradlew
script: ./gradlew build
before_script: chmod +x ./gradlew # Get Permissions
script: ./gradlew jar test clean # Compile, Test, Clean excluding .jar

branches:
only:
- master # master
- /(feature/).*/ # for features
- /[0-9]+\.[0-9]+\.[0-9]+(.*)?/ # TAG

before_deploy: # Create Archive
- rm -fr build/classes/
- zip -r apk-dependency-graph-scripts-$TRAVIS_TAG.zip run.bat run.sh lib/ gui/ build/
# - rm -fr build/classes/ # Uncomment line if used ant for buld
- zip -r apk-dependency-graph-scripts-$TRAVIS_TAG.zip run.bat run.sh lib/ gui/ build/ filters/
deploy:
provider: releases
api_key: $GITHUB_TOKEN # More options > Settings > Environment Variables
Expand Down
121 changes: 33 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,109 +1,67 @@
# Apk Dependency Graph

[![Build Status](https://travis-ci.org/alexzaitsev/apk-dependency-graph.svg?branch=master)](https://travis-ci.org/alexzaitsev/apk-dependency-graph)
[![version](https://img.shields.io/badge/version-0.1.5-brightgreen.svg)](https://github.com/alexzaitsev/apk-dependency-graph/releases/tag/0.1.5) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-apk--dependency--graph-blue.svg?style=flat)](http://android-arsenal.com/details/1/4411)
[![version](https://img.shields.io/badge/version-0.2.0-brightgreen.svg)](https://github.com/alexzaitsev/apk-dependency-graph/releases/tag/0.2.0) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-apk--dependency--graph-blue.svg?style=flat)](http://android-arsenal.com/details/1/4411)

Android dependency visualizer. It's a tool that helps to visualize current state of your project. It's really easy to see how tight your classes are coupled.
Class dependency visualizer. Only `apk` file is needed.
Class coupling is one of the significant code metrics that shows how easy is to change, maintain and test the code. This tool helps to view whole picture of the project.

**Table of contents**
* [Theory](#Theory)
* [Project structure](#Project-structure)
* [Compile](#Compile)
* [Requirements](#Requirements)
* [Gradle](#Gradle)
* [5.0 or newer](#50-or-newer)
* [via Wrapper](#via-Wrapper)
* [Ant](#Ant)
* [Run](#Run)
* [Usage](#Usage)
* [Fast way](#Fast-way)
* [Compile](#Compile)
* [Examples](#Examples)
* [Demo](#Demo)
* [Credits](#Credits)
* [Contributors](#Contributors)
* [Dependency Injection Graph](#Dependency-Injection-Graph)

## Theory

Class coupling is one of the significant code metrics which shows how easy is to change your code. Actually the architecture of microservices is based on the idea that the modules should be low-coupled so you are able to easily replace one module with another one with the same interface. This tool helps to view whole picture of your project. Check yourself!

## Project structure

This project consists of the several parts:

* gui (d3)
* src (apk-dependency-graph)

###### To get more information please check our [wiki page](https://github.com/alexzaitsev/apk-dependency-graph/wiki).

## Compile

### Requirements

* at least **Java 5**

Ways to compile `build/jar/apk-dependency-graph.jar`:

### Gradle

#### 5.0 or newer

run `gradle build`

#### via Wrapper

run `gradlew build`

### Ant

From terminal just move to the parent folder of the project and run `ant` command. Classes will be generated to `build/classes` folder and jar file will appear onto `build/jar` folder.

## Run

You need at least **Java 8** to run `apk-dependency-graph` `jar` file.
* [Credits](#Credits)

## Usage

### Fast way

I've prepared helpful scripts for you. All you need to do is to download and unpack [the latest release](https://github.com/alexzaitsev/apk-dependency-graph/releases) and type the next command in your command line:
Some helpful scripts are prepared for you. All you need to do is to download and unpack [the latest release](https://github.com/alexzaitsev/apk-dependency-graph/releases) and type the next command in your command line:

*For Windows*:

```shell
run.bat full\path\to\the\apk\app-release.apk com.example.test true
run.bat full\path\to\the\apk\app-release.apk full\path\to\the\filterset.json
```

or

```shell
run.bat full\path\to\the\apk\app-release.apk nofilter false
```
Where:
* `run.bat` is a path to script in your local repository
* `full\path\to\the\apk\app-release.apk` is a full path to the apk file you want to analize
* `full\path\to\the\filterset.json` is a full path to the filterset file

where `run.bat` is a path to script in your local repository, `full\path\to\the\apk\app-release.apk` is a full path to the apk file you want to analize, `com.example.test` is a filter. **We recommend to use your package name as a filter so you will avoid unnecessary dependencies in your graph. If you don't want to filter just pass `nofilter`.** The last argument defines whether you want to skip inner classes on your graph (_true_ to skip, _false_ otherwise).
The tool is provided with the [default filterset](https://github.com/alexzaitsev/apk-dependency-graph/blob/master/filters/default.json). However, you're highly encouraged to customize it. Read [filter instructions](https://github.com/alexzaitsev/apk-dependency-graph/blob/master/filters/instructions.txt) for the details.

*For Unix*:

```shell
./run.sh full/path/to/the/apk/app-release.apk com.example.test true
```

or

```shell
./run.sh full/path/to/the/apk/app-release.apk nofilter false
./run.sh full/path/to/the/apk/app-release.apk full/path/to/the/filterset.json
```

Wait until the command finishes:

```shell
Baksmaling classes.dex...
Analyzing dependencies...
Success! Now open index.html in your browser.
```

It will decompile your apk and create `apk-file-name` folder in the same folder where the script is. After this it will analyze the smali code and generate `gui/analyzed.js` file which contains all dependencies.
It will decompile your apk and create `output/apk-file-name` folder in the same folder where the script is. After this it will analyze the smali code and generate `gui/analyzed.js` file which contains all dependencies.
**Now open `gui/index.html` in your browser and enjoy!**

## Compile

At least **Java 8** is needed to compile and run the `jar` file.

Ways to compile `build/jar/apk-dependency-graph.jar`:

`>> gradle build` (Gradle 5.0 or newer)
`>> gradlew build` (Gradle Wrapper)
`>> ant` (Ant)

Classes will be generated to `build/classes` folder and jar file will appear onto `build/jar` folder.

###### To get more information please check our [wiki page](https://github.com/alexzaitsev/apk-dependency-graph/wiki).

## Examples

Here is the sample of good architecture with low class coupling:
Expand All @@ -118,25 +76,12 @@ Does your project look like the first or the second picture? :)

Watch [demo video](https://www.youtube.com/watch?v=rw501tvT4ko).

---

## Credits

There is the same tool for iOS: <https://github.com/PaulTaykalo/objc-dependency-visualizer>
I have used `gui/index.html` of that project. Thanks Paul for the great tool.

## Contributors

I want to say thank you to all the people who made even tiny pull request. This project is intended to improve current state of Android architecture all over the world so each detail is important. Below you can find a list of people who have found some time to improve this tool:

* [WarrenFaith](https://github.com/WarrenFaith)
* [victorrattis](https://github.com/victorrattis)
* [SupinePandora43](https://github.com/SupinePandora43)
I want to say thank you to all the people who made even tiny pull request. This project is intended to improve current state of Android architecture all over the world so each detail is important. In the [contributors page](https://github.com/alexzaitsev/apk-dependency-graph/graphs/contributors) you can find a list of people who have found some time to improve this tool.

##### Btw we still need contributors!

Yes, we really need you man! We always have something to do and have special label for such issues. If you want to add or edit something on this tool - [welcome](https://github.com/alexzaitsev/apk-dependency-graph/issues?q=is%3Aissue+is%3Aopen+label%3A%22contributors+wanted%22)!
## Credits

## Dependency Injection Graph
There is the same tool for iOS: <https://github.com/PaulTaykalo/objc-dependency-visualizer>
I have used `gui/index.html` of that project. Thanks Paul for the great tool.

If you're looking for an Android Studio plugin that allows to display graph of dependency injections - please check out [this repository](https://github.com/kaygisiz/Dependency-Injection-Graph). It's based on current project and available in [Jetbrains repository](https://plugins.jetbrains.com/plugin/10107-dependency-injection-graph).
39 changes: 31 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
ant.importBuild 'build.xml'

task build {
dependsOn resolve
dependsOn jar
// https://stackoverflow.com/a/32909428/9765252
tasks.findByName('jar').mustRunAfter 'resolve'
}
plugins {
id 'java'
id 'com.adarshr.test-logger' version '1.7.0' // https://github.com/radarsh/gradle-test-logger-plugin
}
repositories {
mavenCentral()
}
jar {
manifest {
attributes(
'Main-Class': 'com.alex_zaitsev.adg.Main'
)
}
}
testlogger {
theme 'standard-parallel' // theme
slowThreshold 0 // show time
}
clean {
delete = [ // delete anything excluding compiled jar
'build/classes',
'build/generated',
'build/tmp',
'build/test-results',
'build/reports']
}
dependencies {
implementation 'org.smali:baksmali:2.2.5' // runtime dependency
testImplementation 'org.hamcrest:hamcrest:2.1' // test dependency
testImplementation 'junit:junit:4.12' // test dependency
}
54 changes: 40 additions & 14 deletions build.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<project xmlns:ivy="antlib:org.apache.ivy.ant"
name="apk-dependency-graph" basedir="." default="main">

<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="main-class" value="code.Main"/>
<property name="lib.dir" value="lib" />
<property name="src.dir" value="src/main/java"/>
<property name="test.dir" value="src/test/java"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="testclasses.dir" value="${build.dir}/test-classes"/>
<property name="lib.dir" value="lib" />
<property name="main-class" value="com.alex_zaitsev.adg.Main"/>

<target name="clean">
<delete dir="${build.dir}"/>
Expand All @@ -17,11 +19,33 @@
<javac
srcdir="${src.dir}"
destdir="${classes.dir}"
classpathref="external-libraries-classpath"
classpathref="classpath.libraries"
includeantruntime="false" />
</target>

<target name="jar" depends="compile">
<target name="compileTest" depends="compile" description="Compile jUnit Test cases">
<mkdir dir="${testclasses.dir}"/>
<javac
srcdir="${test.dir}"
destdir="${testclasses.dir}"
classpathref="classpath.test"
includeantruntime="false" />
</target>

<target name="test" depends="compileTest">
<junit printsummary="on" haltonfailure="yes" fork="true">
<classpath>
<path refid="classpath.test"/>
<pathelement location="${testclasses.dir}"/>
</classpath>
<formatter type="brief" usefile="false" />
<batchtest>
<fileset dir="${test.dir}" includes="**/*Test*.java" />
</batchtest>
</junit>
</target>

<target name="jar" depends="test">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<manifest>
Expand All @@ -35,13 +59,9 @@
</jar>
</target>

<target name="run" depends="jar">
<java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
</target>

<target name="clean-build" depends="clean,jar"/>

<target name="main" depends="clean,resolve,run"/>
<target name="main" depends="clean,resolve,jar"/>

<!-- target: resolve -->
<property name="ivy.install.version" value="2.2.0" />
Expand Down Expand Up @@ -71,11 +91,17 @@
</target>

<!-- path: external libraries classpath, we don't need sources and javadoc -->
<path id="external-libraries-classpath">
<path id="classpath.libraries">
<fileset dir="${basedir}/">
<include name="${lib.dir}/*.jar" />
<exclude name="${lib.dir}/*sources.jar"/>
<exclude name="${lib.dir}/*javadoc.jar"/>
</fileset>
</path>

<!-- path: source classes for testing -->
<path id="classpath.test">
<pathelement location="${classes.dir}" />
<path refid="classpath.libraries" />
</path>
</project>
5 changes: 5 additions & 0 deletions filters/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"package-name": "",
"show-inner-classes": false,
"ignored-classes": [".*Dagger.*", ".*Inject.*", ".*ViewBinding$", ".*Factory$", ".*_.*", "^R$", "^R\\$.*"]
}
12 changes: 12 additions & 0 deletions filters/instructions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'default.json' is a default filterset that is shipped with the releases.

It consists of the next filters:
package-name - Java package to filter by, e.g. "com.mysite.myandroidapp". Empty by default.
show-inner-classes - If true it will contain inner class processing (the ones creating ClassName$InnerClass files). False by default.
ignored-classes - The list of regexps that will be applied to class names.
If class name matches any of provided regexps - it will be ignored.
By default it filters Dagger, ButterKnife, R and some generated classes.

You may extend this file or create a set of your own filter files for each project.

Read Usage part of Readme for the details.
7 changes: 7 additions & 0 deletions ivy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,12 @@
<dependencies>
<!-- https://mvnrepository.com/artifact/org.smali/baksmali -->
<dependency org="org.smali" name="baksmali" rev="2.2.5"/>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<!-- 5th version isn't working with Ant properly -->
<!-- It constantly gives the next error: -->
<!-- The <classpath> or <modulepath> for <junit> must include junit.jar if not in Ant's own classpath -->
<dependency org="junit" name="junit" rev="4.12"/>
<!-- https://mvnrepository.com/artifact/org.hamcrest/hamcrest -->
<dependency org="org.hamcrest" name="hamcrest" rev="2.1"/>
</dependencies>
</ivy-module>
2 changes: 1 addition & 1 deletion release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ if [ $# -ne 1 ]; then
fi
gradle build
echo "var dependencies = {links:[{\"source\":\"Class A\",\"dest\":\"Class B\"},{\"source\":\"Class C\",\"dest\":\"Class B\"},]};" > gui/analyzed.js
zip -u -x .DS_Store -r "apk-dependency-graph-scripts-$1.zip" build/jar/apk-dependency-graph.jar build.xml gui/* lib/* run.bat run.sh
zip -u -x .DS_Store -r "apk-dependency-graph-scripts-$1.zip" build/jar/apk-dependency-graph.jar build.xml gui/* lib/* filters/default.json filters/instructions.txt run.bat run.sh
10 changes: 4 additions & 6 deletions run.bat
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ set argCount=0
for %%x in (%*) do (
set /A argCount+=1
)
if not "%~3"=="" if "%~4"=="" goto main
if not "%~2"=="" if "%~3"=="" goto main
echo This script requires the next parameters:
echo - absolute path to apk file
echo - filter ^(can be a package name or 'nofilter' string^)
echo - true or false ^(where true means that you want to see inner classes on your graph^)
echo - absolute path to the filters file
echo Examples:
echo %~nx0 full\path\to\the\apk\app-release.apk com.example.test true
echo %~nx0 full\path\to\the\apk\app-release.apk nofilter false
echo %~nx0 full\path\to\the\apk\app-release.apk full\path\to\the\filters.json
exit /b

:main
Expand All @@ -26,4 +24,4 @@ For %%A in ("%filename%") do (
Set outPath=%~dp0\output\%Name:~0,-4%
Set jsonPath=%~dp0\gui\analyzed.js

java -jar %~dp0\build\jar\apk-dependency-graph.jar -i %outPath% -o %jsonPath% -f %2 -d %3 -a %1
java -jar %~dp0\build\jar\apk-dependency-graph.jar -i %outPath% -o %jsonPath% -a %1 -f %2
Loading

0 comments on commit 2ad9271

Please sign in to comment.