本项目是用于模拟自动注入,通过添加注解@Autowired,方便在安卓下能够更好的自动注入自动添加对象,不需要自己给每一个类进行Instance维护。

文章修改记录

  • 2021年2月6日有修改
  • 12月30日有修改
  • 12月28日有修改

在 Android 应用中使用 Dagger

Dagger 效果更好,建议使用,当前项目就作为一个学习的参考项目吧。

https://developer.android.com/training/dependency-injection/dagger-android?hl=zh-cn

安卓模拟自动注入

源码位置 : https://github.com/kekxv/AndroidAutoWired

代码下载 : AndroidAutoWired.zip

仓库 : Repo

API 文档

  1. 扫描所有(或手动传入)带有 @Service 的自动注入类
  2. 手动或继承IAutoWired 自动调用 IAutoWired.inject(this); 进行注入。
  3. 为保证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 类型。

public static void init(Context context);

init 手动指定类型

初始化,手动指定类型,且不进行自动扫描

public static void init(Class<?>[]list);

registered 注册对象

手动注册对象,多用于注册Context.classActivity.class;例如:IAutoWired.registered(Context.class, this); ;IAutoWired.registered(Activity.class, this);

/**
 * 手动注册 Sign 为 空 的 对象
 *
 * @param cls    对象类
 * @param object 对象
 */
public static void registered(Class<?> cls,Object object);
/**
 * 手动注册对象
 *
 * @param cls    对象类
 * @param object 对象
 * @param Sign   sign
 */
public static void registered(Class<?> cls,Object object,String Sign);

inject 注入对象

将会将 source 对象内的带有@AutoWired进行注入。

public static void inject(Object source);

injectView 注入 View 对象

将会将 source 对象内的带有@injectView进行注入。

public static void InjectView(Activity activity);

引入方式

以下配置均在项目build.gradle

方式一

  1. 生成 GitHub Token,教程点击本链接
  2. 配置 gradle ,增加仓库https://maven.pkg.github.com/kekxv/JavaRepo/ ,github 的仓库需要授权(公开的也要),自己配置一下 GITHUB_USERGITHUB_PERSONAL_ACCESS_TOKEN,建议在家目录的gradle.properties进行配置。
    //配置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']
             }
         }
     }
    }
    
  3. 增加 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

public interface ITeacher {
    void teach();
}

学生 IStudent

public interface IStudent {
    void setName(String name);

    void learn();

    void rollCall();
}

作业 IHomework

public interface IHomework {
    void Assignment();
}

创建实现类

教师 TeacherImpl

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

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

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

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", ">>>>>>>>>>>>>> 布置语文作业");
    }
}

学校

  1. School
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();
    }

}
  1. mSchool
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();
    }
}

主类

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(this);
        // 手动设置
        IAutoWired.init(new Class<?>[]{
                StudentImpl.class,
                TeacherImpl.class,
                mSchool.class,
                School.class,
                HomeworkChineseImpl.class,
                HomeworkMathImpl.class,
        });
        // 将注册 Context 自动注入
        IAutoWired.registered(Context.class, this);

        IAutoWired.inject(this);

        // 后注册测试 将注册 Activity 自动注入
        IAutoWired.registered(Activity.class, this);

        student.setName("小兰");
        student_A.setName("小红");
        student_B.setName("小明");

        school.work();
        mSchool.work();
    }
}

测试输出

在例子中,schoolmSchool包括其内部的@AutoWired均自动注入成功,执行后输出结果为:

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 增加Serviceservice标记,用于开启调用start()
  • 20210114 自动注入后注入,可在更新之后自动注入之前未找到的注入字段。
  • 20210114 在原有自动扫描的基础上,增加手动传入 Service 类。
  • 20201226 增加Sign标记,用于区分各个不一样的实例。
  • 20201226 增加IAutoWired.registered手动注册,可用于自动注入Context之类。

原理说明:

  1. 扫描所有(或手动传入)带有 @Service 的自动注入类
  2. 手动或继承IAutoWired 自动调用 IAutoWired.inject(this); 进行注入。
  3. 为保证private也能注入成功;通过反射以及setAccessible(true);修改权限进行newInstance()以及赋值
Constructor<?> constructor=cla.getDeclaredConstructor();
        constructor.setAccessible(true);
        constructor.newInstance()
Field[]fields=source.getClass().getDeclaredFields();
        for(Field field:fields){
        field.setAccessible(true);
        field.set(source,target);
        }

本项目是用于模拟自动注入,通过添加注解@AutoWired,举个例子:Usage

注意:所有自动注入的同类型类,为同一个对象。

为了能够区分对应的自动注入实例 ,则需要添加注解@Service,方便AutoWired确认。

不足之处

  1. 目前还是有些不足的地方 ,例如当有多个实现类,希望能够根据指定参数或者指定注解,自动注入不一样的实现类,从而更灵活智能,但这个只能看后期项目需求或者是否有时间以及是否有好心人帮忙完善了(已在 0.2.6版本加上)。
  2. 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