本项目是用于模拟自动注入,通过添加注解@Autowired
,方便在安卓下能够更好的自动注入自动添加对象,不需要自己给每一个类进行Instance
维护。
在 Android 应用中使用 Dagger
Dagger
效果更好,建议使用,当前项目就作为一个学习的参考项目吧。
https://developer.android.com/training/dependency-injection/dagger-android?hl=zh-cn
安卓模拟自动注入
源码位置 : https://github.com/kekxv/AndroidAutoWired
代码下载 : AndroidAutoWired.zip
仓库 : Repo 。
API 文档
- 扫描所有(或手动传入)带有
@Service
的自动注入类
- 手动或继承
IAutoWired
自动调用 IAutoWired.inject(this);
进行注入。
- 为保证
private
也能注入成功;通过反射以及setAccessible(true);
修改权限进行newInstance()
以及赋值
。
说明
建议结合仓库源码以及 Usage.md 一起查看
Service : @interface
用于扫描标记,当使用自动扫描的方式,会查找带有 @Service
标记的类进行记录,用于后期进行自动注入创建实例类。
也可以通过手动注入的方式进行传入。
参数 service
是否是服务类型,如果为 true
; 则自动调用 start
无参函数 ; 默认为 false
。
AutoWired : @interface
标记为需要自动注入。如果一个对象需要自动注入,则需要添加该注解,将会自动查找实例类并注入。
参数 Sign
使用不同的 Sign
可以创建不同的对象,默认为空。同一个Sign
则会注入同一个对象。
参数 dependencies
是否在构造函数依赖,如果设置为 true
则会在其他之后注入。默认为false
。
参数 Interpretation
当自动注入类型为 interface
情况下作用;用于获取注入 interface
的对应 class
,如果只有一个class
,将会忽略该标注。
InjectView : @interface
需要 setContentView
之后调用 IAutoWired.inject
;只支持 Activity
或者带有自动注入 Activity
的对象。
该注解将会自动查找相应 View
并进行赋值。
参数 value
指定查找的的 R.id.*
,例如 @InjectView(R.id.text)
。
IAutoWired : class
注入工具类;可以直接 extends IAutoWired
自动调用注册当前对象。
IAutoWired
可以直接 extends IAutoWired
将自动调用注册以及注入当前对象,对 new
也起作用。
init 自动扫描初始化
初始化,并且自动扫描所有 @Service
类型。
1
| public static void init(Context context);
|
init 手动指定类型
初始化,手动指定类型,且不进行自动扫描
1
| public static void init(Class<?>[]list);
|
registered 注册对象
手动注册对象,多用于注册Context.class
与Activity.class
;例如:IAutoWired.registered(Context.class, this);
;IAutoWired.registered(Activity.class, this);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public static void registered(Class<?> cls,Object object);
public static void registered(Class<?> cls,Object object,String Sign);
|
inject 注入对象
将会将 source
对象内的带有@AutoWired
进行注入。
1
| public static void inject(Object source);
|
injectView 注入 View 对象
将会将 source
对象内的带有@injectView
进行注入。
1
| public static void InjectView(Activity activity);
|
引入方式
以下配置均在项目build.gradle
下
方式一
- 生成 GitHub Token,教程点击本链接
- 配置
gradle
,增加仓库https://maven.pkg.github.com/kekxv/JavaRepo/
,github 的仓库需要授权(公开的也要),自己配置一下 GITHUB_USER
、GITHUB_PERSONAL_ACCESS_TOKEN
,建议在家目录的gradle.properties
进行配置。1 2 3 4 5 6 7 8 9 10 11 12 13
| //配置gradle android { repositories { maven { name = "GitHubPackages" url = uri("https://maven.pkg.github.com/kekxv/JavaRepo/") credentials { username = System.getenv('ACCESS_USER') ?: project.properties['GITHUB_USER'] password = System.getenv('ACCESS_PERSONAL_ACCESS_TOKEN') ?: project.properties['GITHUB_PERSONAL_ACCESS_TOKEN'] } } } }
|
- 增加
dependencies
implementation 'com.kekxv:autowired:0.2.6'
(0.2.6
为版本号,可更改为最新版本)
方式二
直接下载前往 仓库 下载autowired-0.2.6.aar
导入到项目。
根据情况,可能需要将autowired-0.2.6.aar
拷贝到libs
目录并 implementation fileTree(dir: "libs", include: ["*.jar"])
更改为implementation fileTree(dir: "libs", include: ["*.jar","*.aar"])
使用方法
先按照 引入方式 Introduce 引入。
API 文档参照 API。
创建基础接口
教师 ITeacher
1 2 3
| public interface ITeacher { void teach(); }
|
学生 IStudent
1 2 3 4 5 6 7
| public interface IStudent { void setName(String name);
void learn();
void rollCall(); }
|
作业 IHomework
1 2 3
| public interface IHomework { void Assignment(); }
|
创建实现类
教师 TeacherImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import android.util.Log;
import com.example.test_autowired.testClass.ITeacher; import com.kekxv.AutoWired.Service;
@Service(service = true) public class TeacherImpl implements ITeacher { int count = 0;
public void teach() { Log.i("TeacherImpl", String.format(">>>>>>>>>>>>>> teacher teach %d \n", count++)); }
public void start() { Log.i("TeacherImpl", ">>>>>>>>>>>>>> teacher teach 开始上课 \n"); } }
|
学生 StudentImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import android.util.Log;
import com.example.test_autowired.testClass.IStudent; import com.kekxv.AutoWired.Service;
@Service(service = true) public class StudentImpl implements IStudent { int count = 0; String name = "";
@Override public void setName(String name) { this.name = name; }
public void start() { Log.i("StudentImpl", String.format(">>>>>>>>>>>>>> student %s 进入课堂", name)); }
public void learn() { Log.i("StudentImpl", String.format(">>>>>>>>>>>>>> student %s learn %d", name, count++)); }
public void rollCall() { Log.i("StudentImpl", String.format(">>>>>>>>>>>>>> student %s here", name)); } }
|
作业
数学 HomeworkMathImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import android.util.Log;
import com.example.test_autowired.testClass.IHomework; import com.kekxv.AutoWired.IAutoWired; import com.kekxv.AutoWired.Service;
@Service public class HomeworkMathImpl extends IAutoWired implements IHomework { public HomeworkMathImpl() { Log.i("HomeworkMathImpl", ">>>>>>>>>>>>>> 数学作业"); }
public void Assignment() { Log.i("HomeworkMathImpl", ">>>>>>>>>>>>>> 布置数学作业"); } }
|
语文 HomeworkChineseImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import android.util.Log;
import com.example.test_autowired.testClass.IHomework; import com.kekxv.AutoWired.IAutoWired; import com.kekxv.AutoWired.Service;
@Service public class HomeworkChineseImpl extends IAutoWired implements IHomework { public HomeworkChineseImpl() { Log.i("HomeworkChineseImpl", ">>>>>>>>>>>>>> 语文作业"); }
public void Assignment() { Log.i("HomeworkMathImpl", ">>>>>>>>>>>>>> 布置语文作业"); } }
|
学校
- School
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.util.Log; import android.widget.TextView; import com.example.test_autowired.R; import com.example.test_autowired.testClass.Impl.HomeworkChineseImpl; import com.kekxv.AutoWired.AutoWired; import com.kekxv.AutoWired.IAutoWired; import com.kekxv.AutoWired.InjectView; import com.kekxv.AutoWired.Service;
@Service public class School extends IAutoWired { @AutoWired() private IStudent student; @AutoWired(Sign = "小红") private IStudent student_A; @AutoWired(Sign = "小明") private IStudent student_B; @AutoWired private ITeacher teacher; @AutoWired Context context; @AutoWired Activity activity;
@SuppressLint("NonConstantResourceId") @InjectView(R.id.text) TextView text;
@AutoWired(Sign = "Chinese", Interpretation = "getIHomework") private IHomework iHomework;
Class<?> getIHomework() { return HomeworkChineseImpl.class; }
public School() { student.rollCall(); student_A.rollCall(); student_B.rollCall(); }
public void work() { teacher.teach(); student.learn(); student_A.learn(); student_B.learn(); Log.d("context", context.toString()); if (text != null) { text.setText("开始上课"); } iHomework.Assignment(); }
}
|
- mSchool
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import com.kekxv.AutoWired.AutoWired; import com.kekxv.AutoWired.IAutoWired; import com.kekxv.AutoWired.Service;
@Service public class mSchool extends IAutoWired { @AutoWired() public IStudent student; @AutoWired(Sign = "小红") private IStudent student_A; @AutoWired(Sign = "小明") private IStudent student_B; @AutoWired private ITeacher teacher;
@AutoWired(Sign = "Math", Interpretation = "getIHomework") private IHomework iHomework;
public String getIHomework() { return "HomeworkMathImpl"; }
private mSchool() { student.rollCall(); student_A.rollCall(); student_B.rollCall(); }
public void work() { teacher.teach(); student.learn(); student_A.learn(); student_B.learn();
iHomework.Assignment(); } }
|
主类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.TextView;
import com.example.test_autowired.testClass.IStudent; import com.example.test_autowired.testClass.Impl.HomeworkChineseImpl; import com.example.test_autowired.testClass.Impl.HomeworkMathImpl; import com.example.test_autowired.testClass.Impl.StudentImpl; import com.example.test_autowired.testClass.Impl.TeacherImpl; import com.example.test_autowired.testClass.School; import com.example.test_autowired.testClass.mSchool; import com.kekxv.AutoWired.AutoWired; import com.kekxv.AutoWired.IAutoWired; import com.kekxv.AutoWired.InjectView;
public class MainActivity extends AppCompatActivity { @AutoWired School school; @AutoWired mSchool mSchool; @AutoWired() private IStudent student; @AutoWired(Sign = "小红") private IStudent student_A; @AutoWired(Sign = "小明") private IStudent student_B; @SuppressLint("NonConstantResourceId") @InjectView(R.id.text) TextView text;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); IAutoWired.init(new Class<?>[]{ StudentImpl.class, TeacherImpl.class, mSchool.class, School.class, HomeworkChineseImpl.class, HomeworkMathImpl.class, }); IAutoWired.registered(Context.class, this);
IAutoWired.inject(this);
IAutoWired.registered(Activity.class, this);
student.setName("小兰"); student_A.setName("小红"); student_B.setName("小明");
school.work(); mSchool.work(); } }
|
测试输出
在例子中,school
与mSchool
包括其内部的@AutoWired
均自动注入成功,执行后输出结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| I/StudentImpl: >>>>>>>>>>>>>> student 进入课堂 I/chatty: uid=10152(com.example.test_autowired) identical 1 line I/StudentImpl: >>>>>>>>>>>>>> student 进入课堂 I/TeacherImpl: >>>>>>>>>>>>>> teacher teach 开始上课 I/HomeworkChineseImpl: >>>>>>>>>>>>>> 语文作业 I/StudentImpl: >>>>>>>>>>>>>> student here I/chatty: uid=10152(com.example.test_autowired) identical 1 line I/StudentImpl: >>>>>>>>>>>>>> student here I/HomeworkMathImpl: >>>>>>>>>>>>>> 数学作业 I/StudentImpl: >>>>>>>>>>>>>> student here I/chatty: uid=10152(com.example.test_autowired) identical 1 line I/StudentImpl: >>>>>>>>>>>>>> student here I/TeacherImpl: >>>>>>>>>>>>>> teacher teach 0 I/StudentImpl: >>>>>>>>>>>>>> student 小兰 learn 0 >>>>>>>>>>>>>> student 小红 learn 0 I/StudentImpl: >>>>>>>>>>>>>> student 小明 learn 0 D/context: com.example.test_autowired.MainActivity@4d23216 I/HomeworkMathImpl: >>>>>>>>>>>>>> 布置语文作业 I/TeacherImpl: >>>>>>>>>>>>>> teacher teach 1 I/StudentImpl: >>>>>>>>>>>>>> student 小兰 learn 1 >>>>>>>>>>>>>> student 小红 learn 1 I/StudentImpl: >>>>>>>>>>>>>> student 小明 learn 1 I/HomeworkMathImpl: >>>>>>>>>>>>>> 布置数学作业
|
更新记录
20210127 修复 interface
接口包含变量没有注入问题。
20210126 修复重复依赖陷入无限回调的问题。
20210126 增加 Interpretation
用于 多个 interface
继承类型判断注入对象。
20210124 修复重复依赖陷入无限回调的问题。
20210124 增加 InjectView
自动findViewById
并赋值;需要 setContentView
之后调用。
20210114 增加Service
内service
标记,用于开启调用start()
。
20210114 自动注入后注入,可在更新之后自动注入之前未找到的注入字段。
20210114 在原有自动扫描的基础上,增加手动传入 Service
类。
20201226 增加Sign
标记,用于区分各个不一样的实例。
20201226 增加IAutoWired.registered
手动注册,可用于自动注入Context
之类。
原理说明:
- 扫描所有(或手动传入)带有
@Service
的自动注入类
- 手动或继承
IAutoWired
自动调用 IAutoWired.inject(this);
进行注入。
- 为保证
private
也能注入成功;通过反射以及setAccessible(true);
修改权限进行newInstance()
以及赋值
。
1 2 3
| Constructor<?> constructor=cla.getDeclaredConstructor(); constructor.setAccessible(true); constructor.newInstance()
|
1 2 3 4 5
| Field[]fields=source.getClass().getDeclaredFields(); for(Field field:fields){ field.setAccessible(true); field.set(source,target); }
|
本项目是用于模拟自动注入,通过添加注解@AutoWired
,举个例子:Usage
注意:所有自动注入的同类型类,为同一个对象。
为了能够区分对应的自动注入实例 ,则需要添加注解@Service
,方便AutoWired
确认。
不足之处
- 目前还是有些不足的地方
,例如当有多个实现类,希望能够根据指定参数或者指定注解,自动注入不一样的实现类,从而更灵活智能,但这个只能看后期项目需求或者是否有时间以及是否有好心人帮忙完善了(已在 0.2.6版本加上)。
DexFile
已经被 API 29
以上列为过时(Deprecated
),这个在扫描当前类时候用上,需要寻找一个替换方案。不过在虚拟机Android11
上面能够正常使用。
参考文档
仿springboot @Autowired自动注入:https://blog.csdn.net/qq_38527695/article/details/104217397
其他互联网文档
备注:由于找资料比较多和杂,部分资料查看后关闭页面,导致没有加入到参考文档内,如果您发现本文章内有借鉴您的文档的部分,请将您的文献地址提交Issues
或者提交 PR(pull requests
)
到https://github.com/kekxv/AndroidAutoWired