├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── eastwood
│ │ └── demo
│ │ ├── App.java
│ │ ├── EventBusAutoBow.java
│ │ ├── MainActivity.java
│ │ └── ToastAutoBow.java
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.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
├── auto-inject-core
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── eastwood
│ │ └── common
│ │ └── autoinject
│ │ ├── AutoArrow.java
│ │ ├── AutoBow.java
│ │ ├── AutoBowArrow.java
│ │ ├── AutoTarget.java
│ │ ├── IAutoArrow.java
│ │ ├── IAutoBow.java
│ │ └── IAutoBowArrow.java
│ └── res
│ └── values
│ └── strings.xml
├── auto-inject-plugin
├── build.gradle
├── settings.gradle
└── src
│ └── main
│ ├── groovy
│ └── com
│ │ └── eastwood
│ │ └── tools
│ │ └── plugins
│ │ └── autoinject
│ │ ├── AutoClassInfo.groovy
│ │ ├── AutoClassInfoComparator.groovy
│ │ ├── AutoInjectExtension.groovy
│ │ ├── AutoInjectPlugin.groovy
│ │ ├── AutoInjectTransform.groovy
│ │ ├── AutoInjector.groovy
│ │ ├── AutoType.groovy
│ │ ├── Logger.groovy
│ │ └── adapter
│ │ ├── AnnotationAdapter.groovy
│ │ ├── BowArrowClassAdapter.groovy
│ │ ├── OnAnnotationValueListener.groovy
│ │ ├── OnMethodInjectListener.groovy
│ │ ├── TargetClassAdapter.groovy
│ │ └── TargetClassMethodAdapter.groovy
│ └── resources
│ └── META-INF
│ └── gradle-plugins
│ └── auto-inject.properties
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── modules
├── moduleB
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── eastwood
│ │ │ └── demo
│ │ │ └── module
│ │ │ └── b
│ │ │ ├── ModuleBActivity.java
│ │ │ └── ModuleBAutoArrow.java
│ │ └── res
│ │ └── values
│ │ └── strings.xml
└── moduleC
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── eastwood
│ │ └── demo
│ │ └── module
│ │ └── c
│ │ ├── ModuleCActivity.java
│ │ └── ModuleCAutoArrow.java
│ └── res
│ └── values
│ └── strings.xml
├── picture
└── 1.png
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Android Studio Navigation editor temp files
24 | .navigation/
25 | .externalNativeBuild
26 |
27 | # Android Studio captures folder
28 | captures/
29 |
30 | # Intellij
31 | *.iml
32 | .idea
33 |
34 | # Keystore files
35 | *.jks
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AutoInject
2 | Android 通用的组件自动注册、自动初始化解决方案
3 |
4 | ## 背景问题
5 | 我们在组件化的过程,业务被拆分至独立的Module中,一些公用组件会在各个Module中通过APT生成一些需要被注册至组件中的信息类,比如EventBus生成的Index类。我们这边RN定制的Plugin是跟随各自module,需要被注册。还有,各Module对外提供的api接口的话,也需要被注册。
6 |
7 | 另外,有些组件为某些Module特有,需要在App启动的时候就要初始化,有些需要在主线程中初始化,有些为不阻塞主线程可以在非主线程中初始化。
8 |
9 | 在组件化之前,我们是在Main Module通过硬编码来进行注册,在Application中堆积各个组件的初始化逻辑。
10 |
11 | **有没有更好的解决方式?**
12 |
13 | ## 解决思路
14 | 首先,将问题分解抽象:
15 |
16 | * 把注册行为进行抽象化,可以把一个类(需要被注册的信息)看作方法函数的入参,那方法函数就可以看作是对注册相关逻辑的实现。那注册问题可以进一步转化为各模块如何把相关类(需要被注册的信息)转化为方法函数的入参,组件定义方法函数,获取入参来实现注册逻辑。比如:
17 |
18 |
19 | A a = new A()
20 | B b = new B()
21 | b.shoot(a.get())
22 |
23 | A为Module定义的一个类,通过get方法可以获得被注册的信息,B为组件定义的一个类,在shoot方法中实现注册逻辑。
24 |
25 | * 接下来的问题是,AB分处不同模块,如何把AB按上述代码逻辑组合起来?
26 |
27 | AB组合意味着需要代码注入,代码注入使用的技术方案是Gradle Transform + ASM。AB分别实现约定的接口,再用注解标记。在编译时,通过Gradle Transform + ASM,通过注解找到AB,生成上述格式的代码并注入到合适的位置。
28 |
29 | * 剩下的问题就是,在什么位置注入?
30 |
31 | 采用的方式是预先定义一个空方法,通过注解标记,并在适当时机调用这个空方法。在编译时通过注解找到AB和空方法,生成上述格式的代码并注入到这个空方法中。
32 |
33 | 以上是有关组件注册方面的解决思路,而模块中的组件初始化有点不同,因为其不需要入参。 但可以直接在方法函数中实现初始化逻辑,比如:
34 |
35 |
36 | A a = new A()
37 | a.shoot()
38 |
39 | 模块定义一个类A,实现约定的接口,在shoot方法中做实现初始化逻辑。
40 |
41 |
42 | 其余和组件注册方式相类似,主要在注入的代码逻辑上有所不同。
43 |
44 |
45 | ## 设计模型
46 |
47 |
48 | 具体的实现方式其实是借鉴了弓箭耙的模式。
49 |
50 | 弓箭耙模式:
51 | * 箭 Arrow:对应一种型号
52 |
53 | 提供模块相关类(需要被注册的信息)的载体。
54 |
55 | * 弓 Bow:适配一种型号的箭,射向唯一的耙。
56 |
57 | 方法函数,即实现注册逻辑的载体。
58 |
59 | * 耙 Target:位置
60 |
61 | 将被注入的空方法。
62 |
63 | ## Usage
64 | 在根项目的build.gradle中添加插件依赖:
65 |
66 | buildscript {
67 | ...
68 | dependencies {
69 | ...
70 | classpath 'com.eastwood.tools.plugins:auto-inject:1.0.3'
71 | }
72 |
73 | }
74 |
75 | 在模块的build.gradle中添加注解库依赖:
76 |
77 | dependencies {
78 | ...
79 | implementation 'com.eastwood.common:auto-inject:1.0.0'
80 |
81 | }
82 |
83 | 在主模块的build.gradle中引用插件:
84 |
85 |
86 | apply plugin: 'auto-inject'
87 |
88 | autoInject {
89 | showLog = true
90 | ignorePackages = ['android', 'com/google']
91 | }
92 |
93 | #### @AutoTarget
94 | 预先定义一个空方法并调用,在方法上标记@AutoTarget,例如:
95 |
96 | public class App extends Application {
97 |
98 | public EventBusBuilder eventBusBuilder;
99 |
100 | @Override
101 | public void onCreate() {
102 | super.onCreate();
103 |
104 | eventBusBuilder = EventBus.builder();
105 | // add config to eventBusBuilder
106 | addIndex2EventBus();
107 | eventBusBuilder.build();
108 |
109 | }
110 |
111 | @AutoTarget
112 | void addIndex2EventBus() {}
113 |
114 | }
115 |
116 | `addIndex2EventBus` 方法将被注入代码。
117 |
118 | #### @AutoArrow
119 | 新建一个类,并实现IAutoArrow接口,在get方法中返回需要被注册的信息类。例如:
120 |
121 | @AutoArrow(model = "eventBusIndex")
122 | public class ModuleBAutoArrow implements IAutoArrow {
123 |
124 | @Override
125 | public SubscriberInfoIndex get() {
126 | return new ModuleBEventBusIndex();
127 | }
128 |
129 | }
130 |
131 | #### @AutoBow
132 | 新建一个类,并实现IAutoBow接口,在shoot方法中获取入参并执行具体的注册逻辑。例如:
133 |
134 | @AutoBow(target = "addIndex2EventBus", model = "eventBusIndex", context = true)
135 | public class EventBusAutoBow implements IAutoBow {
136 |
137 | private App app;
138 |
139 | EventBusAutoBow(Application application) {
140 | app = (App) application;
141 | }
142 |
143 | @Override
144 | public void shoot(SubscriberInfoIndex index) {
145 | app.eventBusBuilder.addIndex(index);
146 | }
147 |
148 | }
149 |
150 | 其中 `context` 用于声明EventBusAutoBow被实例化时是否需要上下文,这个上下文是被`@AutoTarget`标记的方法在运行时的上下文。为 `true`时,该类需定义一个以上下文做为唯一入参的构造函数。
151 |
152 | #### @AutoBowArrow
153 | 新建一个类,并实现IAutoBowArrow接口,在shoot方法中执行相关逻辑。
154 |
155 | @AutoBowArrow(target = "init")
156 | public class InitAutoBowArrow implements IAutoBowArrow {
157 |
158 | @Override
159 | public void shoot() {
160 | // ...
161 | }
162 |
163 | }
164 |
165 | ### 两种组合方式
166 | * @AutoArrow + @AutoBow + @AutoTarget,三者比例关系为 **n:1:1**
167 |
168 | * @AutoBowArrow + @AutoTarget ,两者比例关系为 **1:1**
169 |
170 | ### 编译后,被注入的代码样式
171 | 打包成apk后,@AutoTarget标记的方法将会被注入具有固定结构的代码,例如:
172 |
173 |
174 | // @AutoArrow + @AutoBow + @AutoTarget 组合
175 |
176 | @AutoTarget
177 | void addIndex2EventBus() {
178 | ModuleBAutoArrow moduleBAutoArrow = new ModuleBAutoArrow();
179 | EventBusAutoBow eventBusAutoBow = new EventBusAutoBow(this);
180 | eventBusAutoBow.shoot(moduleBAutoArrow.get());
181 | ...
182 | eventBusAutoBow.shoot(***.get());
183 | }
184 |
185 | // @AutoBowArrow + @AutoTarget 组合
186 |
187 | @AutoTarget
188 | void init() {
189 | InitAutoBowArrow initAutoBowArrow = new InitAutoBowArrow();
190 | initAutoBowArrow.shoot();
191 | ...
192 | }
193 |
194 | ## Question or Idea
195 | 有问题或想法可以直接加我微信: EastWoodYang
196 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | defaultConfig {
6 | applicationId "com.eastwood.demo"
7 | minSdkVersion 14
8 | targetSdkVersion 27
9 | versionCode 1
10 | versionName "1.0"
11 | }
12 |
13 | buildTypes {
14 | release {
15 | minifyEnabled true
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:27.1.1'
24 | implementation project(':auto-inject-core') // or implementation 'com.eastwood.common:auto-inject:1.0.0'
25 |
26 | implementation 'org.greenrobot:eventbus:3.1.1'
27 |
28 | }
29 |
30 | apply plugin: 'auto-inject'
31 |
32 | autoInject {
33 | showLog = true
34 | ignorePackages = ['android', 'com/google', 'com/hianalytics/android']
35 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | -keep @interface com.eastwood.common.autoinject.AutoArrow { *; }
24 | -keep @interface com.eastwood.common.autoinject.AutoTarget { *; }
25 | -keep @interface com.eastwood.common.autoinject.AutoBow { *; }
26 | -keep @com.eastwood.common.autoinject.AutoArrow public class * { *; }
27 | -keep @com.eastwood.common.autoinject.AutoBow public class * { *; }
28 | -keepclassmembers class * {
29 | @com.eastwood.common.autoinject.AutoTarget *;
30 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/eastwood/demo/App.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.demo;
2 |
3 | import android.app.Application;
4 |
5 | import com.eastwood.common.autoinject.AutoTarget;
6 |
7 | import org.greenrobot.eventbus.EventBus;
8 | import org.greenrobot.eventbus.EventBusBuilder;
9 |
10 | /**
11 | * @author eastwood
12 | * createDate: 2018-06-19
13 | */
14 | public class App extends Application {
15 |
16 | public EventBusBuilder eventBusBuilder;
17 |
18 | @Override
19 | public void onCreate() {
20 | super.onCreate();
21 |
22 | eventBusBuilder = EventBus.builder();
23 | // add config to eventBusBuilder
24 | addIndex2EventBus();
25 | eventBusBuilder.build();
26 |
27 | }
28 |
29 | @AutoTarget
30 | void addIndex2EventBus() {
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/eastwood/demo/EventBusAutoBow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.demo;
2 |
3 | import android.app.Application;
4 |
5 | import com.eastwood.common.autoinject.AutoBow;
6 | import com.eastwood.common.autoinject.IAutoBow;
7 |
8 | import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
9 |
10 | @AutoBow(target = "addIndex2EventBus", model = "eventBusIndex", context = true)
11 | public class EventBusAutoBow implements IAutoBow {
12 |
13 | private App app;
14 |
15 | EventBusAutoBow(Application application) {
16 | app = (App) application;
17 | }
18 |
19 | @Override
20 | public void shoot(SubscriberInfoIndex index) {
21 | app.eventBusBuilder.addIndex(index);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/eastwood/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.demo;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.View;
6 |
7 | import com.eastwood.common.autoinject.AutoTarget;
8 |
9 | /**
10 | * @author eastwood
11 | * createDate: 2018-09-18
12 | */
13 | public class MainActivity extends AppCompatActivity {
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.activity_main);
19 |
20 | findViewById(R.id.btn_show_toast).setOnClickListener(new View.OnClickListener() {
21 | @Override
22 | public void onClick(View v) {
23 | showToast();
24 | }
25 | });
26 |
27 | }
28 |
29 | @AutoTarget(name = "toast")
30 | void showToast() {
31 |
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/eastwood/demo/ToastAutoBow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.demo;
2 |
3 | import android.content.Context;
4 | import android.widget.Toast;
5 |
6 | import com.eastwood.common.autoinject.AutoBow;
7 | import com.eastwood.common.autoinject.AutoBowArrow;
8 | import com.eastwood.common.autoinject.IAutoBow;
9 | import com.eastwood.common.autoinject.IAutoBowArrow;
10 |
11 | @AutoBowArrow(target = "toast", context = true)
12 | public class ToastAutoBow implements IAutoBowArrow {
13 |
14 | private Context context;
15 |
16 | ToastAutoBow(Context context) {
17 | this.context = context;
18 | }
19 |
20 | @Override
21 | public void shoot() {
22 | Toast.makeText(context, "Hi", Toast.LENGTH_LONG).show();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | auto-register-dev
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/auto-inject-core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/auto-inject-core/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 26
5 |
6 | defaultConfig {
7 | minSdkVersion 14
8 | targetSdkVersion 26
9 | versionCode 1
10 | versionName "1.0"
11 | }
12 |
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 |
20 | }
21 |
22 | dependencies {
23 |
24 | }
25 |
26 | //apply plugin: 'maven'
27 | //
28 | //def groupId = 'com.eastwood.common'
29 | //def artifactId = 'auto-inject'
30 | //def version = '1.0.0'
31 | //
32 | //def localReleaseDest = "${buildDir}/release/${version}"
33 | //
34 | //task androidJavadocs(type: Javadoc) {
35 | // failOnError = false
36 | // source = android.sourceSets.main.java.srcDirs
37 | // ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
38 | // classpath += files(ext.androidJar)
39 | //}
40 | //
41 | //task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
42 | // classifier = 'javadoc'
43 | // from androidJavadocs.destinationDir
44 | //}
45 | //
46 | //task androidSourcesJar(type: Jar) {
47 | // classifier = 'sources'
48 | // from android.sourceSets.main.java.srcDirs
49 | //}
50 | //
51 | //uploadArchives {
52 | // repositories.mavenDeployer {
53 | // pom.groupId = groupId
54 | // pom.artifactId = artifactId
55 | // pom.version = version
56 | // // Add other pom properties here if you want (developer details / licenses)
57 | // repository(url: "file://${localReleaseDest}")
58 | // }
59 | //}
60 | //
61 | //task zipRelease(type: Zip) {
62 | // from localReleaseDest
63 | // destinationDir buildDir
64 | // archiveName "release-${version}.zip"
65 | //}
66 | //
67 | //task generateRelease {
68 | // doLast {
69 | // println "Release ${version} can be found at ${localReleaseDest}/"
70 | // println "Release ${version} zipped can be found ${buildDir}/release-${version}.zip"
71 | // }
72 | //}
73 | //
74 | //generateRelease.dependsOn(uploadArchives)
75 | //generateRelease.dependsOn(zipRelease)
76 | //
77 | //
78 | //artifacts {
79 | // archives androidSourcesJar
80 | // archives androidJavadocsJar
81 | //}
82 |
--------------------------------------------------------------------------------
/auto-inject-core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/auto-inject-core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/auto-inject-core/src/main/java/com/eastwood/common/autoinject/AutoArrow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.common.autoinject;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Target;
5 |
6 | @Target({ElementType.TYPE})
7 | public @interface AutoArrow {
8 | String model();
9 |
10 | int priority() default 0;
11 |
12 | boolean context() default false;
13 | }
--------------------------------------------------------------------------------
/auto-inject-core/src/main/java/com/eastwood/common/autoinject/AutoBow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.common.autoinject;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Target;
5 |
6 | @Target({ElementType.TYPE})
7 | public @interface AutoBow {
8 | String target();
9 |
10 | String model();
11 |
12 | int priority() default 0;
13 |
14 | boolean context() default false;
15 | }
--------------------------------------------------------------------------------
/auto-inject-core/src/main/java/com/eastwood/common/autoinject/AutoBowArrow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.common.autoinject;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Target;
5 |
6 | @Target({ElementType.TYPE})
7 | public @interface AutoBowArrow {
8 | String target();
9 |
10 | int priority() default 0;
11 |
12 | boolean context() default false;
13 | }
--------------------------------------------------------------------------------
/auto-inject-core/src/main/java/com/eastwood/common/autoinject/AutoTarget.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.common.autoinject;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Target;
5 |
6 | @Target({ElementType.METHOD})
7 | public @interface AutoTarget {
8 | String[] name() default {}; // default value is method name
9 | }
--------------------------------------------------------------------------------
/auto-inject-core/src/main/java/com/eastwood/common/autoinject/IAutoArrow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.common.autoinject;
2 |
3 | public interface IAutoArrow {
4 |
5 | T get();
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/auto-inject-core/src/main/java/com/eastwood/common/autoinject/IAutoBow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.common.autoinject;
2 |
3 | public interface IAutoBow {
4 |
5 | void shoot(T t);
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/auto-inject-core/src/main/java/com/eastwood/common/autoinject/IAutoBowArrow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.common.autoinject;
2 |
3 | public interface IAutoBowArrow {
4 |
5 | void shoot();
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/auto-inject-core/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | auto-inject
3 |
4 |
--------------------------------------------------------------------------------
/auto-inject-plugin/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'groovy'
2 |
3 | group 'com.eastwood.tools.plugins'
4 | sourceCompatibility = 1.8
5 |
6 | repositories {
7 | jcenter()
8 | google()
9 | }
10 |
11 | dependencies {
12 |
13 | compile gradleApi()
14 | compile localGroovy()
15 | compile 'com.android.tools.build:gradle:3.0.1'
16 | compile 'org.ow2.asm:asm:6.0'
17 | }
18 |
19 | apply plugin: 'maven'
20 |
21 | def groupId = 'com.eastwood.tools.plugins'
22 | def artifactId = 'auto-inject'
23 | def version = '1.0.3'
24 |
25 | def localReleaseDest = "${buildDir}/release/${version}"
26 |
27 | task groovydocJar(type: Jar, dependsOn: groovydoc) {
28 | classifier = 'javadoc'
29 | from groovydoc.getDestinationDir()
30 | }
31 |
32 | task sourcesJar(type: Jar, dependsOn: classes) {
33 | classifier = 'sources'
34 | from sourceSets.main.allSource
35 | }
36 |
37 | uploadArchives {
38 | repositories {
39 | mavenDeployer {
40 | pom.groupId = groupId
41 | pom.artifactId = artifactId
42 | pom.version = version
43 | // Add other pom properties here if you want (developer details / licenses)
44 | repository(url: "file://${localReleaseDest}")
45 | }
46 | }
47 | }
48 |
49 | task zipRelease(type: Zip) {
50 | from localReleaseDest
51 | destinationDir buildDir
52 | archiveName "release-${version}.zip"
53 | }
54 |
55 | task generateRelease {
56 | doLast {
57 | println "Release ${version} can be found at ${localReleaseDest}/"
58 | println "Release ${version} zipped can be found ${buildDir}/release-${version}.zip"
59 | }
60 | }
61 |
62 | generateRelease.dependsOn(uploadArchives)
63 | generateRelease.dependsOn(zipRelease)
64 |
65 | artifacts {
66 | archives groovydocJar
67 | archives sourcesJar
68 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'auto-inject'
2 |
3 |
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/AutoClassInfo.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject
2 |
3 | public class AutoClassInfo {
4 |
5 | public String className
6 | public String initDesc
7 | public String returnDesc
8 |
9 | public String target
10 | public String model
11 | public int priority
12 |
13 | public AutoType getAutoType() {
14 | if (target != null) {
15 | if (model != null) {
16 | return AutoType.BOW
17 | } else {
18 | return AutoType.BOW_ARROW
19 | }
20 | } else if (model != null) {
21 | return AutoType.ARROW
22 | } else {
23 | return AutoType.NONE
24 | }
25 | }
26 |
27 | @Override
28 | String toString() {
29 | if (target != null) {
30 | return 'Bow [target: ' + target + ', model: ' + model + ', priority: ' + priority + ', className: ' + className + ']'
31 | } else if (model != null) {
32 | return 'Arrow [model: ' + model + ', priority: ' + priority + ', className: ' + className + ', returnDesc: ' + returnDesc + ']'
33 | } else {
34 | return 'None [target: ' + target + ', model: ' + model + ', priority: ' + priority + ', className: ' + className + ', returnDesc: ' + returnDesc + ']'
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/AutoClassInfoComparator.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject
2 |
3 | class AutoClassInfoComparator implements Comparator {
4 |
5 | @Override
6 | int compare(AutoClassInfo o1, AutoClassInfo o2) {
7 | if (o1.priority > o2.priority) {
8 | return 1
9 | } else if (o1.priority < o2.priority) {
10 | return -1
11 | } else {
12 | return 0
13 | }
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/AutoInjectExtension.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject
2 |
3 | public class AutoInjectExtension {
4 |
5 | boolean showLog
6 |
7 | String[] ignorePackages
8 |
9 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/AutoInjectPlugin.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject
2 |
3 | import com.android.build.gradle.AppExtension
4 | import com.android.build.gradle.AppPlugin
5 | import org.gradle.api.Plugin
6 | import org.gradle.api.Project
7 |
8 | class AutoInjectPlugin implements Plugin {
9 |
10 | void apply(Project project) {
11 | def isApp = project.plugins.hasPlugin(AppPlugin)
12 | if (!isApp) return
13 |
14 | project.extensions.create("autoInject", AutoInjectExtension.class)
15 |
16 | def android = project.extensions.getByType(AppExtension)
17 | android.registerTransform(new AutoInjectTransform(project))
18 |
19 | project.afterEvaluate {
20 | Logger.project = project
21 | Logger.showLog = project.autoInject.showLog
22 | AutoInjector.ignorePackages = project.autoInject.ignorePackages
23 | }
24 |
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/AutoInjectTransform.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject
2 |
3 | import com.android.build.api.transform.DirectoryInput
4 | import com.android.build.api.transform.JarInput
5 | import com.android.build.api.transform.QualifiedContent
6 | import com.android.build.api.transform.Transform
7 | import com.android.build.api.transform.TransformException
8 | import com.android.build.api.transform.TransformInput
9 | import com.android.build.api.transform.TransformInvocation
10 | import com.android.build.api.transform.Format
11 | import com.android.build.gradle.internal.pipeline.TransformManager
12 | import com.android.utils.FileUtils
13 | import org.gradle.api.Project
14 |
15 | class AutoInjectTransform extends Transform {
16 |
17 | private Project project
18 |
19 | public AutoInjectTransform(Project project) {
20 | this.project = project
21 | }
22 |
23 | @Override
24 | public String getName() {
25 | return "autoInject"
26 | }
27 |
28 | @Override
29 | Set getInputTypes() {
30 | return TransformManager.CONTENT_CLASS
31 | }
32 |
33 | @Override
34 | Set super QualifiedContent.Scope> getScopes() {
35 | return TransformManager.SCOPE_FULL_PROJECT
36 | }
37 |
38 | @Override
39 | boolean isIncremental() {
40 | return false
41 | }
42 |
43 | @Override
44 | void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
45 | AutoInjector.clearBowArrow()
46 | transformInvocation.getInputs().each { TransformInput input ->
47 | input.directoryInputs.each { DirectoryInput directoryInput ->
48 | AutoInjector.findBowAndArrow(directoryInput.file)
49 | }
50 | input.jarInputs.each { JarInput jarInput ->
51 | AutoInjector.findBowAndArrow(jarInput.file)
52 | }
53 | }
54 |
55 | transformInvocation.getInputs().each { TransformInput input ->
56 | input.directoryInputs.each { DirectoryInput directoryInput ->
57 | def dest = transformInvocation.outputProvider.getContentLocation(directoryInput.name,
58 | directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY)
59 | FileUtils.copyDirectory(directoryInput.file, dest)
60 | AutoInjector.findTargetAndInject(dest)
61 | }
62 | input.jarInputs.each { JarInput jarInput ->
63 | def dest = transformInvocation.outputProvider.getContentLocation(jarInput.name, jarInput.contentTypes, jarInput.scopes, Format.JAR)
64 | FileUtils.copyFile(jarInput.file, dest)
65 | AutoInjector.findTargetAndInject(dest)
66 | }
67 | }
68 |
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/AutoInjector.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject
2 |
3 | import com.eastwood.tools.plugins.autoinject.adapter.BowArrowClassAdapter
4 | import com.eastwood.tools.plugins.autoinject.adapter.OnMethodInjectListener
5 | import com.eastwood.tools.plugins.autoinject.adapter.TargetClassAdapter
6 | import org.objectweb.asm.ClassReader
7 | import org.objectweb.asm.ClassWriter
8 |
9 | import java.util.jar.JarEntry
10 | import java.util.jar.JarFile
11 | import java.util.jar.JarOutputStream
12 | import java.util.zip.ZipEntry
13 |
14 | class AutoInjector {
15 |
16 | public static List autoBowInfoList = new ArrayList<>()
17 | public static List autoArrowInfoList = new ArrayList()
18 |
19 | public static String[] ignorePackages
20 |
21 | private final static BowArrowClassAdapter bowArrowClassAdapter = new BowArrowClassAdapter()
22 |
23 | public static void findBowAndArrow(File source) {
24 | if (source.isDirectory()) {
25 | source.eachFileRecurse { File file ->
26 | String filename = file.getName()
27 | if (filterClass(filename)) return
28 | ClassReader classReader = new ClassReader(file.readBytes())
29 | classReader.accept(bowArrowClassAdapter, 0)
30 | }
31 | } else {
32 | JarFile jarFile = new JarFile(source)
33 | Enumeration entries = jarFile.entries()
34 | while (entries.hasMoreElements()) {
35 | JarEntry entry = entries.nextElement()
36 | String filename = entry.getName()
37 | if (filterPackage(filename)) break
38 |
39 | if (filterClass(filename)) continue
40 |
41 | InputStream stream = jarFile.getInputStream(entry)
42 | if (stream != null) {
43 | ClassReader classReader = new ClassReader(stream.bytes)
44 | classReader.accept(bowArrowClassAdapter, 0)
45 | stream.close()
46 | }
47 | }
48 | jarFile.close()
49 | }
50 | }
51 |
52 | private final static TargetClassAdapter targetClassAdapter = new TargetClassAdapter()
53 |
54 | public static void findTargetAndInject(File source) {
55 | if (source.isDirectory()) {
56 | source.eachFileRecurse { File file ->
57 | String filename = file.getName()
58 | if (filterClass(filename)) {
59 | return
60 | }
61 | byte[] bytes = findTargetAndInject(source, file.readBytes())
62 | if (bytes != null) {
63 | Logger.i('-- replace class [' + file.absolutePath + ']')
64 | FileOutputStream outputStream = new FileOutputStream(file)
65 | outputStream.write(bytes)
66 | outputStream.close()
67 | }
68 | }
69 | } else {
70 | Map tempModifiedClassByteMap = new HashMap()
71 |
72 | JarFile jarFile = new JarFile(source)
73 | Enumeration jarEntryEnumeration = jarFile.entries()
74 | while (jarEntryEnumeration.hasMoreElements()) {
75 | JarEntry jarEntry = jarEntryEnumeration.nextElement()
76 | String filename = jarEntry.getName()
77 | if (filterPackage(filename)) {
78 | break
79 | }
80 |
81 | if (filterClass(filename)) {
82 | continue
83 | }
84 |
85 | InputStream inputStream = jarFile.getInputStream(jarEntry)
86 | if (inputStream != null) {
87 | byte[] bytes = findTargetAndInject(source, inputStream.bytes)
88 | if (bytes != null) {
89 | tempModifiedClassByteMap.put(filename, bytes)
90 | }
91 | }
92 | inputStream.close()
93 | }
94 |
95 | if (tempModifiedClassByteMap.size() != 0) {
96 | File tempJar = new File(source.absolutePath.replace('.jar', 'temp.jar'))
97 | if (tempJar.exists())
98 | tempJar.delete()
99 |
100 | jarEntryEnumeration = jarFile.entries()
101 | JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(tempJar))
102 | while (jarEntryEnumeration.hasMoreElements()) {
103 | JarEntry jarEntry = jarEntryEnumeration.nextElement()
104 | String filename = jarEntry.getName()
105 | ZipEntry zipEntry = new ZipEntry(filename)
106 | jarOutputStream.putNextEntry(zipEntry)
107 | if (tempModifiedClassByteMap.containsKey(filename)) {
108 | jarOutputStream.write(tempModifiedClassByteMap.get(filename))
109 | } else {
110 | InputStream inputStream = jarFile.getInputStream(jarEntry)
111 | jarOutputStream.write(inputStream.bytes)
112 | inputStream.close()
113 | }
114 | jarOutputStream.closeEntry()
115 | }
116 | jarOutputStream.close()
117 |
118 | Logger.i('-- replace jar [' + source.absolutePath + ']')
119 | FileOutputStream outputStream = new FileOutputStream(source)
120 | outputStream.write(tempJar.bytes)
121 | outputStream.close()
122 | tempJar.delete()
123 | }
124 | jarFile.close()
125 | }
126 | }
127 |
128 | protected static byte[] findTargetAndInject(File source, byte[] bytes) {
129 | ClassWriter classWriter = new ClassWriter(0)
130 | // TraceClassVisitor traceClassVisitor = new TraceClassVisitor(classWriter, new PrintWriter(System.out))
131 | boolean methodInject
132 | OnMethodInjectListener onMethodInjectListener = new OnMethodInjectListener() {
133 | @Override
134 | void onInject() {
135 | methodInject = true
136 | }
137 | }
138 | targetClassAdapter.set(classWriter, onMethodInjectListener)
139 | try {
140 | ClassReader classReader = new ClassReader(bytes)
141 | classReader.accept(targetClassAdapter, 0)
142 | } catch (Exception e) {
143 | String tip = "\nRead class failed when find target in source: " + source.name + "[" + source.absolutePath + "]."
144 | tip += "\n* Try:\n" + " use autoInject { ignorePackages = ... } to ignore this source If necessary."
145 | tip += "\n* Exception:\n " + e.toString()
146 | for (int i = 0; i < e.stackTrace.size(); i++) {
147 | tip += "\n " + e.stackTrace[i].toString()
148 | }
149 | Logger.e(tip)
150 | }
151 | if (methodInject) {
152 | return classWriter.toByteArray()
153 | } else {
154 | return null
155 | }
156 | }
157 |
158 | public static void clearBowArrow() {
159 | autoBowInfoList = new ArrayList<>()
160 | autoArrowInfoList = new ArrayList()
161 | }
162 |
163 | public static void addBowArrow(AutoClassInfo autoClassInfo) {
164 | Logger.i("-- find : " + autoClassInfo.toString())
165 | AutoType autoType = autoClassInfo.getAutoType()
166 | if (autoType == AutoType.BOW || autoType == AutoType.BOW_ARROW) {
167 | autoBowInfoList.add(autoClassInfo)
168 | } else if (autoType == AutoType.ARROW) {
169 | autoArrowInfoList.add(autoClassInfo)
170 | }
171 | }
172 |
173 | public static List getBowArrowList(String targetName) {
174 | List bowClassInfoList = new ArrayList<>()
175 | for (int i = 0; i < autoBowInfoList.size(); i++) {
176 | AutoClassInfo autoClassInfo = autoBowInfoList.get(i)
177 | if (autoClassInfo.target == targetName) {
178 | bowClassInfoList.add(autoClassInfo)
179 | }
180 | }
181 |
182 | AutoClassInfoComparator comparator = new AutoClassInfoComparator()
183 | Collections.sort(bowClassInfoList, comparator)
184 |
185 | List bowArrowInfoList = new ArrayList<>()
186 |
187 | for (int i = 0; i < bowClassInfoList.size(); i++) {
188 | AutoClassInfo bowClassInfo = bowClassInfoList.get(i)
189 | bowArrowInfoList.add(bowClassInfo)
190 | if (bowClassInfo.getAutoType() == AutoType.BOW_ARROW) continue
191 |
192 | List arrowClassInfoList = new ArrayList<>()
193 | for (int j = 0; j < autoArrowInfoList.size(); j++) {
194 | AutoClassInfo arrowClassInfo = autoArrowInfoList.get(j)
195 | if (arrowClassInfo.model == bowClassInfo.model) {
196 | arrowClassInfoList.add(arrowClassInfo)
197 | }
198 | }
199 | Collections.sort(arrowClassInfoList, comparator)
200 | bowArrowInfoList.addAll(arrowClassInfoList)
201 | }
202 | return bowArrowInfoList
203 | }
204 |
205 | public static boolean filterPackage(String filename) {
206 | if (ignorePackages == null) return false
207 |
208 | for (int i = 0; i < ignorePackages.size(); i++) {
209 | if (filename.startsWith(ignorePackages[i])) {
210 | return true
211 | }
212 | }
213 | return false
214 | }
215 |
216 | public static boolean filterClass(String filename) {
217 | if (!filename.endsWith(".class")
218 | || filename.contains('R$')
219 | || filename.contains('R.class')
220 | || filename.contains("BuildConfig.class")) {
221 | return true
222 | }
223 |
224 | return false
225 | }
226 |
227 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/AutoType.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject;
2 |
3 | public enum AutoType {
4 | NONE, TARGET, BOW, ARROW, BOW_ARROW
5 | }
6 |
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/Logger.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject
2 |
3 | import org.gradle.api.Project
4 |
5 | public class Logger {
6 |
7 | public static Project project
8 | public static boolean showLog
9 |
10 | public static void i(String message) {
11 | if (showLog) project.getLogger().println(message)
12 | }
13 |
14 | public static void e(String message) {
15 | project.getLogger().error(message)
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/adapter/AnnotationAdapter.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject.adapter
2 |
3 | import org.objectweb.asm.AnnotationVisitor
4 | import org.objectweb.asm.Opcodes
5 |
6 | class AnnotationAdapter extends AnnotationVisitor {
7 |
8 | OnAnnotationValueListener valueListener
9 |
10 | AnnotationAdapter(AnnotationVisitor annotationVisitor, OnAnnotationValueListener listener) {
11 | super(Opcodes.ASM5, annotationVisitor)
12 | this.valueListener = listener
13 | }
14 |
15 | @Override
16 | void visit(String name, Object value) {
17 | super.visit(name, value)
18 | valueListener.onValue(name, value)
19 | }
20 |
21 | @Override
22 | AnnotationVisitor visitArray(String name) {
23 | if (name == "name") {
24 | return new AnnotationAdapter(super.visitArray(name), valueListener)
25 | } else {
26 | return super.visitArray(name)
27 | }
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/adapter/BowArrowClassAdapter.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject.adapter
2 |
3 | import com.eastwood.tools.plugins.autoinject.AutoClassInfo
4 | import com.eastwood.tools.plugins.autoinject.AutoInjector
5 | import com.eastwood.tools.plugins.autoinject.AutoType
6 | import org.gradle.api.GradleScriptException
7 | import org.objectweb.asm.*
8 |
9 | class BowArrowClassAdapter extends ClassVisitor {
10 |
11 | private final static String AUTO_BOW_ANNOTATION_BYTECODE = "Lcom/eastwood/common/autoinject/AutoBow;"
12 | private final static String AUTO_ARROW_ANNOTATION_BYTECODE = "Lcom/eastwood/common/autoinject/AutoArrow;"
13 | private final static String AUTO_BOW_ARROW_ANNOTATION_BYTECODE = "Lcom/eastwood/common/autoinject/AutoBowArrow;"
14 | private final static String AUTO_BOW_INTERFACE_BYTECODE = "com/eastwood/common/autoinject/IAutoBow"
15 | private final static String AUTO_ARROW_INTERFACE_BYTECODE = "com/eastwood/common/autoinject/IAutoArrow"
16 | private final static String AUTO_BOW_ARROW_INTERFACE_BYTECODE = "com/eastwood/common/autoinject/IAutoBowArrow"
17 | private final static String AUTO_ARROW_SIGNATURE_PREFIX_BYTECODE = "Lcom/eastwood/common/autoinject/IAutoArrow<"
18 |
19 | private String classname
20 | private String signature
21 | private String[] interfaces
22 | private AutoClassInfo autoClassInfo
23 |
24 | private boolean context
25 | private List getMethodDescList
26 |
27 | BowArrowClassAdapter() {
28 | super(Opcodes.ASM5)
29 | }
30 |
31 | @Override
32 | void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
33 | super.visit(version, access, name, signature, superName, interfaces)
34 | this.classname = name
35 | this.signature = signature
36 | this.interfaces = interfaces
37 | autoClassInfo = null
38 | context = false
39 | getMethodDescList = null
40 | }
41 |
42 | @Override
43 | AnnotationVisitor visitAnnotation(String desc, boolean visible) {
44 | if (AUTO_BOW_ANNOTATION_BYTECODE == desc || AUTO_ARROW_ANNOTATION_BYTECODE == desc || AUTO_BOW_ARROW_ANNOTATION_BYTECODE == desc) {
45 | AnnotationVisitor annotationVisitor = super.visitAnnotation(desc, visible)
46 | OnAnnotationValueListener valueListener = new OnAnnotationValueListener() {
47 | @Override
48 | void onValue(String name, Object value) {
49 | if (autoClassInfo == null) {
50 | autoClassInfo = new AutoClassInfo()
51 | getMethodDescList = new ArrayList<>()
52 | }
53 |
54 | switch (name) {
55 | case "target":
56 | autoClassInfo.target = value
57 | break
58 | case "model":
59 | autoClassInfo.model = value
60 | break
61 | case "priority":
62 | autoClassInfo.priority = value
63 | break
64 | case "context":
65 | context = value
66 | break
67 | }
68 |
69 | }
70 | }
71 | return new AnnotationAdapter(annotationVisitor, valueListener)
72 | }
73 | return super.visitAnnotation(desc, visible)
74 | }
75 |
76 | @Override
77 | MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
78 | if (autoClassInfo != null) {
79 | if (name == '') {
80 | if (autoClassInfo.initDesc != null) {
81 | throw new GradleScriptException("class " + classname + " has multi-.", null)
82 | } else if (context && desc == '()V') {
83 | throw new GradleScriptException("class " + classname + " need context, but has a non-parameter constructor.", null)
84 | } else if (!context && desc != '()V') {
85 | throw new GradleScriptException("class " + classname + " is not need context, but without a non-parameter constructor.", null)
86 | }
87 | autoClassInfo.initDesc = desc
88 | } else if (name == 'get' && autoClassInfo.getAutoType() == AutoType.ARROW) {
89 | if (Type.getArgumentTypes(desc).length == 0) {
90 | getMethodDescList.add(desc)
91 | }
92 | }
93 | }
94 | return super.visitMethod(access, name, desc, signature, exceptions)
95 | }
96 |
97 | @Override
98 | void visitEnd() {
99 | super.visitEnd()
100 | if (autoClassInfo != null) {
101 | AutoType autoType = autoClassInfo.getAutoType()
102 | if (autoType == AutoType.ARROW) {
103 | if (!interfaces.contains(AUTO_ARROW_INTERFACE_BYTECODE)) {
104 | throw new GradleScriptException("class " + classname + " should implements " + AUTO_ARROW_INTERFACE_BYTECODE + " directly.", null)
105 | }
106 |
107 | if (this.signature == null) {
108 | throw new GradleScriptException("class " + classname + " implements " + AUTO_ARROW_INTERFACE_BYTECODE + " must specified generic type.", null)
109 | }
110 |
111 | String returnDesc = getReturnDesc()
112 | if (returnDesc == null) {
113 | throw new GradleScriptException("can't find returnDesc of class " + classname + ".get(...).", null)
114 | }
115 | autoClassInfo.returnDesc = returnDesc
116 | } else if (autoType == AutoType.BOW) {
117 | if (!interfaces.contains(AUTO_BOW_INTERFACE_BYTECODE)) {
118 | throw new GradleScriptException("class " + classname + " should implements " + AUTO_BOW_INTERFACE_BYTECODE + " directly.", null)
119 | }
120 | } else if (autoType == AutoType.BOW_ARROW) {
121 | if (!interfaces.contains(AUTO_BOW_ARROW_INTERFACE_BYTECODE)) {
122 | throw new GradleScriptException("class " + classname + " should implements " + AUTO_BOW_ARROW_INTERFACE_BYTECODE + " directly.", null)
123 | }
124 | }
125 | autoClassInfo.className = classname
126 | AutoInjector.addBowArrow(autoClassInfo)
127 | }
128 | }
129 |
130 |
131 | String getReturnDesc() {
132 | String returnDesc = null
133 | for (int i = 0; i < getMethodDescList.size(); i++) {
134 | String desc = getMethodDescList.get(i)
135 | desc = desc.subSequence(2, desc.length() - 1)
136 | if (this.signature.contains(AUTO_ARROW_SIGNATURE_PREFIX_BYTECODE + desc)) {
137 | returnDesc = desc + ';'
138 | break
139 | }
140 | }
141 | return returnDesc
142 | }
143 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/adapter/OnAnnotationValueListener.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject.adapter
2 |
3 | interface OnAnnotationValueListener {
4 |
5 | void onValue(String name, Object value)
6 |
7 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/adapter/OnMethodInjectListener.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject.adapter
2 |
3 | interface OnMethodInjectListener {
4 |
5 | void onInject()
6 |
7 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/adapter/TargetClassAdapter.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject.adapter
2 |
3 | import org.objectweb.asm.ClassVisitor
4 | import org.objectweb.asm.MethodVisitor
5 | import org.objectweb.asm.Opcodes
6 |
7 | class TargetClassAdapter extends ClassVisitor {
8 |
9 | TargetClassMethodAdapter targetClassMethodAdapter = new TargetClassMethodAdapter()
10 |
11 | String className
12 |
13 | TargetClassAdapter() {
14 | super(Opcodes.ASM5)
15 | }
16 |
17 | void set(ClassVisitor classWriter, OnMethodInjectListener onMethodInjectListener) {
18 | this.cv = classWriter
19 | targetClassMethodAdapter.setMethodInjectListener(onMethodInjectListener)
20 | }
21 |
22 | @Override
23 | void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
24 | super.visit(version, access, name, signature, superName, interfaces)
25 | this.className = name
26 | }
27 |
28 |
29 | @Override
30 | MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
31 | MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions)
32 | targetClassMethodAdapter.set(methodVisitor, className, name, desc)
33 | return targetClassMethodAdapter
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/groovy/com/eastwood/tools/plugins/autoinject/adapter/TargetClassMethodAdapter.groovy:
--------------------------------------------------------------------------------
1 | package com.eastwood.tools.plugins.autoinject.adapter
2 |
3 | import com.eastwood.tools.plugins.autoinject.AutoClassInfo
4 | import com.eastwood.tools.plugins.autoinject.AutoInjector
5 | import com.eastwood.tools.plugins.autoinject.AutoType
6 | import com.eastwood.tools.plugins.autoinject.Logger
7 | import org.gradle.api.GradleScriptException
8 | import org.objectweb.asm.AnnotationVisitor
9 | import org.objectweb.asm.MethodVisitor
10 | import org.objectweb.asm.Opcodes
11 | import org.objectweb.asm.Type
12 |
13 | class TargetClassMethodAdapter extends MethodVisitor {
14 |
15 | private final static String AUTO_TARGET_ANNOTATION_BYTECODE = "Lcom/eastwood/common/autoinject/AutoTarget;"
16 |
17 | String className
18 | String methodName
19 | String methodDesc
20 |
21 | private boolean needInject
22 | private OnMethodInjectListener onInjectListener
23 | private List targetNames
24 | private int currentLocals = 1
25 | private boolean contextStack
26 |
27 | TargetClassMethodAdapter() {
28 | super(Opcodes.ASM5)
29 | }
30 |
31 | void set(MethodVisitor methodVisitor, String className, String methodName, String desc) {
32 | this.mv = methodVisitor
33 | this.className = className
34 | this.methodName = methodName
35 | targetNames = new ArrayList<>()
36 | methodDesc = desc
37 | }
38 |
39 | void setMethodInjectListener(OnMethodInjectListener listener) {
40 | this.onInjectListener = listener
41 | }
42 |
43 | @Override
44 | AnnotationVisitor visitAnnotation(String desc, boolean visible) {
45 | if (AUTO_TARGET_ANNOTATION_BYTECODE == desc) {
46 | needInject = true
47 | AnnotationVisitor annotationVisitor = super.visitAnnotation(desc, visible)
48 | OnAnnotationValueListener valueListener = new OnAnnotationValueListener() {
49 | @Override
50 | void onValue(String name, Object value) {
51 | if (name == null && value != null) {
52 | if (!targetNames.contains(value)) {
53 | targetNames.add(value)
54 | }
55 | } else if (name != null && name == "name") {
56 | if (!targetNames.contains(value)) {
57 | targetNames.add(value)
58 | }
59 | }
60 | }
61 | }
62 | return new AnnotationAdapter(annotationVisitor, valueListener)
63 | }
64 | return super.visitAnnotation(desc, visible)
65 | }
66 |
67 | @Override
68 | void visitCode() {
69 | if (!needInject) {
70 | mv.visitCode()
71 | return
72 | }
73 |
74 | if (targetNames.isEmpty()) {
75 | targetNames.add(methodName)
76 | }
77 |
78 | List autoClassInfoList = new ArrayList<>()
79 | for (int i = 0; i < targetNames.size(); i++) {
80 | autoClassInfoList.addAll(AutoInjector.getBowArrowList(targetNames[i]))
81 | }
82 | if (autoClassInfoList.size() == 0) {
83 | needInject = false
84 | mv.visitCode()
85 | return
86 | }
87 | Logger.i("--\n-- inject class: " + className + ", method: " + methodName + ", targets: " + targetNames.toString())
88 | onInjectListener.onInject()
89 |
90 | String currentBowClassName
91 | int currentBowPosition
92 | int offset = getTypesSize(Type.getArgumentTypes(methodDesc))
93 | for (int i = 0; i < autoClassInfoList.size(); i++) {
94 | AutoClassInfo autoClassInfo = autoClassInfoList.get(i)
95 | AutoType autoType = autoClassInfo.getAutoType()
96 | if (autoType == AutoType.BOW || autoType == AutoType.BOW_ARROW) {
97 | currentLocals += 1
98 | currentBowPosition = offset + i + 1
99 | currentBowClassName = autoClassInfo.className
100 | mv.visitTypeInsn(Opcodes.NEW, autoClassInfo.className)
101 | mv.visitInsn(Opcodes.DUP)
102 | if (autoClassInfo.initDesc != "()V") {
103 | contextStack = true
104 | mv.visitVarInsn(Opcodes.ALOAD, 0)
105 | }
106 | mv.visitMethodInsn(Opcodes.INVOKESPECIAL, autoClassInfo.className, '', autoClassInfo.initDesc, false)
107 | mv.visitVarInsn(Opcodes.ASTORE, offset + i + 1)
108 |
109 | if (autoType == AutoType.BOW_ARROW) {
110 | mv.visitVarInsn(Opcodes.ALOAD, currentBowPosition)
111 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, currentBowClassName, 'shoot', '()V', false)
112 | Logger.i("-- bow_arrow , " + "target: " + autoClassInfo.target + ', className: ' + autoClassInfo.className)
113 | } else {
114 | Logger.i("-- bow , " + "target: " + autoClassInfo.target + ", model: " + autoClassInfo.model + ', className: ' + autoClassInfo.className)
115 | }
116 | } else if (autoType == AutoType.ARROW) {
117 | currentLocals += 1
118 | mv.visitTypeInsn(Opcodes.NEW, autoClassInfo.className)
119 | mv.visitInsn(Opcodes.DUP)
120 | if (autoClassInfo.initDesc != "()V") {
121 | contextStack = true
122 | mv.visitVarInsn(Opcodes.ALOAD, 0)
123 | }
124 | mv.visitMethodInsn(Opcodes.INVOKESPECIAL, autoClassInfo.className, '', autoClassInfo.initDesc, false)
125 | mv.visitVarInsn(Opcodes.ASTORE, offset + i + 1)
126 |
127 | mv.visitVarInsn(Opcodes.ALOAD, currentBowPosition)
128 | mv.visitVarInsn(Opcodes.ALOAD, offset + i + 1)
129 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, autoClassInfo.className, 'get', '()' + autoClassInfo.returnDesc, false)
130 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, currentBowClassName, 'shoot', '(' + autoClassInfo.returnDesc + ')V', false)
131 |
132 | Logger.i("-- arrow , model: " + autoClassInfo.model + ', className: ' + autoClassInfo.className + ', returnDesc: ' + autoClassInfo.returnDesc)
133 | } else {
134 | throw new GradleScriptException("Unknown AutoType: " + autoClassInfo.toString(), null)
135 | }
136 | }
137 | }
138 |
139 | @Override
140 | void visitMaxs(int maxStack, int maxLocals) {
141 | if (needInject) {
142 | if (maxStack < 2) {
143 | if (contextStack) {
144 | maxStack = 3
145 | } else {
146 | maxStack = 2
147 | }
148 | }
149 |
150 | if (maxLocals < currentLocals) {
151 | maxLocals = currentLocals
152 | }
153 | }
154 | super.visitMaxs(maxStack, maxLocals)
155 | }
156 |
157 | @Override
158 | void visitEnd() {
159 | super.visitEnd()
160 | needInject = false
161 | targetNames = new ArrayList<>()
162 | currentLocals = 1
163 | contextStack = false
164 |
165 | }
166 |
167 | private static int getTypesSize(Type[] types) {
168 | int size = 0
169 | for (int i = 0; i < types.size(); i++) {
170 | size += types[i].getSize()
171 | }
172 | return size
173 | }
174 |
175 | }
--------------------------------------------------------------------------------
/auto-inject-plugin/src/main/resources/META-INF/gradle-plugins/auto-inject.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.eastwood.tools.plugins.autoinject.AutoInjectPlugin
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.2.1'
11 |
12 | classpath 'com.eastwood.tools.plugins:auto-inject'
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jan 10 14:12:09 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/modules/moduleB/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 26
5 |
6 | defaultConfig {
7 | minSdkVersion 14
8 | targetSdkVersion 26
9 | versionCode 1
10 | versionName "1.0"
11 |
12 | javaCompileOptions {
13 | annotationProcessorOptions {
14 | arguments = [
15 | eventBusIndex : 'com.eastwood.demo.eventbus.ModuleBEventBusIndex',
16 | showLog:'false'
17 | ]
18 | }
19 | }
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | }
29 |
30 | dependencies {
31 | api project(':auto-inject-core')
32 |
33 | implementation 'org.greenrobot:eventbus:3.1.1'
34 | annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
35 | }
36 |
--------------------------------------------------------------------------------
/modules/moduleB/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flagParamValue in this file are appended to flagParamValue specified
3 | # in F:\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 |
--------------------------------------------------------------------------------
/modules/moduleB/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/modules/moduleB/src/main/java/com/eastwood/demo/module/b/ModuleBActivity.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.demo.module.b;
2 |
3 | import android.app.Activity;
4 | import android.widget.Toast;
5 |
6 | import org.greenrobot.eventbus.EventBus;
7 | import org.greenrobot.eventbus.Subscribe;
8 | import org.greenrobot.eventbus.ThreadMode;
9 |
10 | public class ModuleBActivity extends Activity {
11 |
12 | @Subscribe(threadMode = ThreadMode.MAIN)
13 | public void onMessageEvent(String action) {
14 | Toast.makeText(this, action, Toast.LENGTH_SHORT).show();
15 | }
16 |
17 | @Override
18 | public void onStart() {
19 | super.onStart();
20 | EventBus.getDefault().register(this);
21 | }
22 |
23 | @Override
24 | public void onStop() {
25 | EventBus.getDefault().unregister(this);
26 | super.onStop();
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/modules/moduleB/src/main/java/com/eastwood/demo/module/b/ModuleBAutoArrow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.demo.module.b;
2 |
3 | import com.eastwood.common.autoinject.AutoArrow;
4 | import com.eastwood.common.autoinject.IAutoArrow;
5 | import com.eastwood.demo.eventbus.ModuleBEventBusIndex;
6 |
7 | import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
8 |
9 | /**
10 | * @author eastwood
11 | * createDate: 2018-06-19
12 | */
13 | @AutoArrow(model = "eventBusIndex")
14 | public class ModuleBAutoArrow implements IAutoArrow {
15 |
16 | @Override
17 | public SubscriberInfoIndex get() {
18 | return new ModuleBEventBusIndex();
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/modules/moduleB/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ModuleB
3 |
4 |
--------------------------------------------------------------------------------
/modules/moduleC/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 26
5 |
6 | defaultConfig {
7 | minSdkVersion 14
8 | targetSdkVersion 26
9 | versionCode 1
10 | versionName "1.0"
11 |
12 | javaCompileOptions {
13 | annotationProcessorOptions {
14 | arguments = [
15 | eventBusIndex : 'com.eastwood.demo.eventbus.ModuleCEventBusIndex',
16 | showLog:'false'
17 | ]
18 | }
19 | }
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | }
29 |
30 | dependencies {
31 | api project(':auto-inject-core')
32 |
33 | implementation 'org.greenrobot:eventbus:3.1.1'
34 | annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
35 | }
36 |
--------------------------------------------------------------------------------
/modules/moduleC/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flagParamValue in this file are appended to flagParamValue specified
3 | # in F:\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 |
--------------------------------------------------------------------------------
/modules/moduleC/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/modules/moduleC/src/main/java/com/eastwood/demo/module/c/ModuleCActivity.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.demo.module.c;
2 |
3 | import android.app.Activity;
4 | import android.widget.Toast;
5 |
6 | import org.greenrobot.eventbus.EventBus;
7 | import org.greenrobot.eventbus.Subscribe;
8 | import org.greenrobot.eventbus.ThreadMode;
9 |
10 | public class ModuleCActivity extends Activity {
11 |
12 | @Subscribe(threadMode = ThreadMode.MAIN)
13 | public void onMessageEvent(String action) {
14 | Toast.makeText(this, action, Toast.LENGTH_SHORT).show();
15 | }
16 |
17 | @Override
18 | public void onStart() {
19 | super.onStart();
20 | EventBus.getDefault().register(this);
21 | }
22 |
23 | @Override
24 | public void onStop() {
25 | EventBus.getDefault().unregister(this);
26 | super.onStop();
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/modules/moduleC/src/main/java/com/eastwood/demo/module/c/ModuleCAutoArrow.java:
--------------------------------------------------------------------------------
1 | package com.eastwood.demo.module.c;
2 |
3 | import com.eastwood.common.autoinject.AutoArrow;
4 | import com.eastwood.common.autoinject.IAutoArrow;
5 | import com.eastwood.demo.eventbus.ModuleCEventBusIndex;
6 |
7 | import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
8 |
9 | /**
10 | * @author eastwood
11 | * createDate: 2018-06-19
12 | */
13 | @AutoArrow(model = "eventBusIndex")
14 | public class ModuleCAutoArrow implements IAutoArrow {
15 |
16 | @Override
17 | public SubscriberInfoIndex get() {
18 | return new ModuleCEventBusIndex();
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/modules/moduleC/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | moduleC
3 |
4 |
--------------------------------------------------------------------------------
/picture/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EastWoodYang/AutoInject/07510fbe26c1ac18863d96d39cccf9cfc0c3efdc/picture/1.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', 'modules:moduleB', 'modules:moduleC'
2 |
3 | include ':auto-inject-core'
4 |
5 | includeBuild './auto-inject-plugin'
--------------------------------------------------------------------------------