{"id":22016307,"url":"https://github.com/ganyao114/lazyinject","last_synced_at":"2025-05-07T01:24:43.452Z","repository":{"id":150175694,"uuid":"65276497","full_name":"ganyao114/lazyInject","owner":"ganyao114","description":"被动依赖注入框架 for Android","archived":false,"fork":false,"pushed_at":"2019-01-16T02:23:54.000Z","size":1075,"stargazers_count":18,"open_issues_count":0,"forks_count":10,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-31T05:01:42.150Z","etag":null,"topics":["android","dependency-injection","di","weave"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ganyao114.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-2.0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-08-09T08:17:01.000Z","updated_at":"2025-01-17T16:55:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"b04b136b-b1d9-421c-93fb-82e8c62b42f4","html_url":"https://github.com/ganyao114/lazyInject","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ganyao114%2FlazyInject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ganyao114%2FlazyInject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ganyao114%2FlazyInject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ganyao114%2FlazyInject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ganyao114","download_url":"https://codeload.github.com/ganyao114/lazyInject/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252794625,"owners_count":21805222,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","dependency-injection","di","weave"],"created_at":"2024-11-30T04:34:16.522Z","updated_at":"2025-05-07T01:24:43.444Z","avatar_url":"https://github.com/ganyao114.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 1.lazyInject\n\n被动依赖注入框架 for Android. \n\n [ ![Version](https://api.bintray.com/packages/ganyao114/maven/lib/images/download.svg) ](https://bintray.com/ganyao114/maven/lib/_latestVersion)\n\n## 特点\n\n- 被动注入，通过编译期间 hook field access 实现，无需手动调用 inject\n- 懒加载 or 实时更新，由于 hook 了每一个 GETFIELD/GETSTATIC 指令，使懒加载或者实时更新成为可能\n- 完整的范型匹配\n- 较少的反射，大多数情况下可以在编译期间直接匹配到 provider 方法\n- 支持静态变量注入\n- 跨进程注入\n- 支持增量编译/Instant Run\n- 支持 Proguard\n\n## 项目结构\n\n- app：example\n- annotation：注入使用的注解\n- aopweave：gradle plugin，编译期间 hook FiledAccess 并注入代码的织入器\n- compiler：注解处理器，用于组装注入容器 Component 和其实现\n- lib：运行时库\n- aspectjsupport：如果不使用内置的 aopweave hook FiledAccess，可以依赖此使用 aspectj hook\n- kotlinsupport：使用 kotlin 原生特性代理实现注入\n\n## 编译调试项目\n\n- 打包 annotation 到本地 maven repo\n./gradlew :annotation:uploadArchives\n- 打包 aopweave 到本地 maven repo\n./gradleW :aopweave:uploadArchives\n- 开始调试 gradle plugin\n./gradlew assembleDebug -Dorg.gradle.daemon=false -Dorg.gradle.debug=true --stacktrace  \n\n\n# 2.配置\n## Gradle  \n根目录/build.gradle  \n```groovy\nbuildscript {\n    dependencies {\n        ...\n        classpath 'com.trend.lazyinject:aopweave:3.4.0-beta'\n        ...\n    }\n}\n```\napp/build.gradle\n```groovy\napply plugin: 'lazyinject'\n\nlazyinject {\n    //是否开启注入\n    enable true\n    //启用编译期间类型匹配，可以减少运行期间反射，建议开启\n    optimize true\n    //包名数组，过滤需要注入的包，加快编译\n    includes \"your pkg scope\"\n}\n\ndependencies {\n    ...\n    annotationProcessor 'com.trend.lazyinject:compiler:3.4.0-beta'\n    ...\n}\n\n```  \n## 混淆  \n```proguard\n-ignorewarning\n-keepattributes *Annotation*\n#保留部分泛型信息，必要!\n-keepattributes Signature\n#手动启用support keep注解\n#http://tools.android.com/tech-docs/support-annotations\n-dontskipnonpubliclibraryclassmembers\n-keep,allowobfuscation @interface android.support.annotation.Keep\n\n-keep @android.support.annotation.Keep class * {\n*;\n}\n\n-keepclassmembers class * {\n    @android.support.annotation.Keep *;\n}\n#手动启用Component注解\n#http://tools.android.com/tech-docs/support-annotations\n-keep,allowobfuscation @interface com.trend.lazyinject.annotation.Component\n\n-keep,allowobfuscation @com.trend.lazyinject.annotation.Component class * {\n*;\n}\n\n-keepclassmembers,allowobfuscation class * {\n    @com.trend.lazyinject.annotation.Provide \u003cmethods\u003e;\n}\n\n-keepclassmembers class * {\n     @com.trend.lazyinject.annotation.Inject \u003cfields\u003e;\n}\n\n-keepclassmembers class * {\n     @com.trend.lazyinject.annotation.InjectComponent \u003cfields\u003e;\n}\n\n-dontwarn javassist.**\n```  \n# 3.Example  \n## Component  \n\u0026nbsp;\u0026nbsp;参考 Dagger2，在 LazyInject 中 Component 为注入容器。  \n### Component 定义  \n```java\n@Component\npublic interface TestComponent {\n    @Provide\n    List\u003cString\u003e provide1();\n    @Provide\n    ArrayList\u003cInteger\u003e provide2();\n    @Provide\n    ArrayList\u003c? extends BaseModel\u003e provide3();\n    @Provide(singleton = true)\n    ModelA provide4();\n    @Provide\n    Map\u003cString,BaseModel\u003e provide5();\n    @Provide\n    Map\u003cString, ? extends ModelA\u003e provide6();\n}\n```\n\u0026nbsp;\u0026nbsp;打上 @Provide 注解的方法将被暴露为依赖的提供者，每个 provide 方法可单独配置为 singleton\n### Component 实现\n```java\n@ComponentImpl\npublic class TestComponentImpl implements TestComponent {\n    @Override\n    public List\u003cString\u003e provide1() {\n        return new ArrayList\u003c\u003e();\n    }\n    @Override\n    public ArrayList\u003cInteger\u003e provide2() {\n        return new ArrayList\u003c\u003e();\n    }\n    @Override\n    public ArrayList\u003c? extends BaseModel\u003e provide3() {\n        return new ArrayList\u003c\u003e();\n    }\n    @Override\n    public ModelA provide4() {\n        return new ModelA();\n    }\n    @Override\n    public Map\u003cString, BaseModel\u003e provide5() {\n        return new HashMap\u003c\u003e();\n    }\n    @Override\n    public Map\u003cString, ? extends ModelA\u003e provide6() {\n        return new HashMap\u003c\u003e();\n    }\n}\n```\n```java@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.PACKAGE, ElementType.TYPE})\npublic @interface ComponentImpl {\n    String component() default \"\";\n    String name() default \"default\";\n    boolean cache() default true;\n}\n```\n需要注意两个参数:  \n1. name 指定后，如果某个 Component 存在多个实现类，可在编译时指定具体的实现。  \n2. cache = false 时，每次注入都将会调用下面所说的 build 静态方法(也就是说默认会 new 一个新的 Component)，一般适用于当 Component 为 MVP 中的 Presenter 时。\n### Component 管理\n\u0026nbsp;\u0026nbsp;Component 默认在进程中全局单例\n####手动管理\n```java\nLazyInject.registerComponent(component, instance);\nLazyInject.getComponent(component, instance);\nLazyInject.removeComponent(component, instance);\n```\n#### 自动管理\n##### 编写 BuildMap\n\u0026nbsp;\u0026nbsp;为了让框架找到对应 Component 的构造方法，你需要实现一个完全由对应静态方法构成的类，该类默认会由注解处理器自动生成\n```java\n@Keep\npublic class Auto_ComponentBuildMap {\n  public static TestComponent buildTestComponentImpl() {\n    return new com.trend.lazyinject.demo.component.TestComponentImpl();\n  }\n}\n```\n\u0026nbsp;\u0026nbsp;需要做的就是在 Application 初始化时调用\n```java\npublic class DemoApplication extends Application {\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        LazyInject.init(this);\n        LazyInject.addBuildMap(Auto_ComponentBuildMap.class);\n    }\n}\n```\n\u0026nbsp;\u0026nbsp;当然也可以模仿这个手动编写\n## 注入\n### 被动注入\n\u0026nbsp;\u0026nbsp;被动注入的原理是利用 AspectJ 编译时 hook field get 操作。所以注入是被动的。  \n\u0026nbsp;\u0026nbsp;除了加上 @Inject 注解并不需要做其他操作。\n#### @Inject\n```java\n@Target(FIELD)\n@Retention(RUNTIME)\npublic @interface Inject {\n    //定义该注入对象所在的 Component 容器类型,默认取注入类型的外部类。\n    Class\u003c?\u003e component() default None.class;\n    //为 true 时每次 get 该 field 都会注入一个新的值\n    boolean alwaysRefresh() default false;\n    //可以向注入方法传递 String 类型的参数\n    String[] args() default {};  \n    //当无法找到合适的值时,会自动虚拟一个对象以避免空指针异常。  \n    boolean nullProtect() default false;  \n\n    class None {}\n\n}\n```\n//定义该注入对象所在的 Component 容器类型,默认取注入类型的外部类。  \nClass\u003c?\u003e component() default None.class;  \n//为 true 时每次 get 该 field 都会注入一个新的值  \nboolean alwaysRefresh() default false;  \n//可以向注入方法传递 String 类型的参数  \nString[] args() default {};  \n//当无法找到合适的值时,会自动虚拟一个对象以避免空指针异常。 \nboolean nullProtect() default false;  \n\nExample\n```java\npublic class MainActivity extends AppCompatActivity {\n\n    @Inject(component = TestComponent.class)\n    List\u003cInteger\u003e integers;\n    @InjectComponent\n    TestComponent testComponent;\n    @Inject(component = TestComponent.class)\n    List\u003cString\u003e strings;\n    @Inject(component = TestComponent.class, alwaysRefresh = true)\n    BaseModel baseModel;\n    @Inject(component = TestComponent.class)\n    Map\u003cString,ModelA\u003e map;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        if (integers != null) {\n            LOG.LOGD(\"test\", \"List\u003cInteger\u003e inject success = \" + integers.toString());\n        }\n        if (testComponent != null) {\n            LOG.LOGD(\"test\", \"TestComponent inject success = \" + testComponent.toString());\n        }\n        if (strings != null) {\n            LOG.LOGD(\"test\", \"List\u003cString\u003e inject success = \" + strings.toString());\n        }\n        if (baseModel != null) {\n            LOG.LOGD(\"test\", \"BaseModel inject success = \" + baseModel.toString());\n        }\n        if (map != null) {\n            LOG.LOGD(\"test\", \"Map\u003cString,ModelA\u003e inject success = \" + map.toString());\n        }\n    }\n}\n```\n#### @InjectComponent\n\u0026nbsp;\u0026nbsp;@InjectComponent 注解用于注入 Component 容器。  \n\n\u0026nbsp;\u0026nbsp;一般使用 MVP 时常用到。\n```java\npublic interface LoginMVP {\n    interface View {\n        void loginSuccess();\n        void loginError();\n    }\n    @Component\n    interface Presenter {\n        void attachView(View view);\n        void login(String name, String pass);\n        void dettachView();\n        @Provide\n        User loginedUser();\n    }\n    interface UserManager {\n        User getUser();\n        void setUser(User user);\n        void logout();\n    }\n    class User implements Serializable {\n        public String name;\n        public String sessionId;\n    }\n}\n```\n\n```java\n//Presenter 不 cache\n@ComponentImpl(cache = false, name = \"Product\")\npublic class LoginPresenter implements LoginMVP.Presenter {\n    LoginMVP.User user = new LoginMVP.User();\n    @Override\n    public void attachView(LoginMVP.View view) {}\n\n    @Override\n    public void login(String name, String pass) {}\n    @Override\n    public void dettachView() {}\n\n    @Override\n    public LoginMVP.User loginedUser() {\n        return user;\n    }\n}\n```\n\n```java\npublic class LoginActivity extends AppCompatActivity implements LoginMVP.View {\n    @InjectComponent\n    LoginMVP.Presenter loginPresenter;\n    @Inject\n    LoginMVP.User lastUser;\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        loginPresenter.attachView(this);\n        //注入 lastUser\n        LazyInject.inject(this, loginPresenter);\n    }\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        loginPresenter.dettachView();\n    }\n    @Override\n    public void loginSuccess() {}\n    @Override\n    public void loginError() {}\n}\n```\n### 主动注入\n\u0026nbsp;\u0026nbsp;主动注入不需要开启 AspectJ  \n\u0026nbsp;\u0026nbsp;区别在于需要用户手动调用 LazyInject.inject(this);  \n\u0026nbsp;\u0026nbsp;不支持 alwaysRefresh\n\n### Provider 方法参数依赖注入\n\u0026nbsp;\u0026nbsp;类似 Dagger Provider 方法可以带待注入的参数\n1. 如果不在参数列表上加 Inject 注解，则默认会在本模块中搜索合适的依赖\n2. 加上 Inject 或者 InjectComponent 则会注入对应模块的依赖，参考上面 Field 注入写法\n3. 搜索不到则为 Null，String 类型参数可从注解传入\n\n```java\n    @Provide\n    ModelA provide4(Map\u003cString,BaseModel\u003e strings,String test ,@InjectComponent TestComponent testComponent);\n```\n\n\n### Kotlin Support\n#### 使用 Kotlin 特性动态代理\n```kotlin\nval map: Map\u003cAny,Any\u003e by provideElement(TestComponent::class)\n```\n\u0026nbsp;\u0026nbsp;缺点在于不能混淆,混淆会丢失类型元数据，请等待 proguard 修复，从设计角度来说 kotlin side 难以修复。  \n\u0026nbsp;\u0026nbsp;case:https://youtrack.jetbrains.com/issue/KT-21869  \n#### 继续使用注解，和 Java 类似\n```kotlin\n@Provide(component = TestComponent::class, alwaysRefresh = true)\nvar strs: List\u003cString\u003e? = null;\n```\n## Build 配置\n\u0026nbsp;\u0026nbsp;对应子 Module/build.gradle\n```groovy\nandroid {\n    ...\n    defaultConfig {\n        ...  \n        javaCompileOptions {\n            annotationProcessorOptions {\n                arguments = [ targetPackage : 'com.trend.lazyinject.demo' , targetClassName : 'AppBuildMap', defaultComponent : 'Product']\n            }\n        }\n\n    }\n}\n```\n\u0026nbsp;\u0026nbsp;自动生成 com.trend.lazyinject.demo.AppBuildMap，并且选用 Product 实现  \n\n## Debug 开关\n\u0026nbsp;\u0026nbsp;打开将显示 log  \n```java\nLazyInject.setDebug(true);\n```\n\n## 在子进程实现 Component\nComponent 的实现可以在子进程\n\n```java\n@ComponentImpl(process = \"com.trend.lazyinject.demo.p1\")\npublic class TestComponentImpl implements TestComponent {\n    ...\n    Serializable/Parcelable/IBinder remoteProvider(Serializable/Parcelable/IBinder pars){}\n    ...\n}\n```\n\n需要在 Manifest 中为子进程注册一个 StubProvider, authorities 必须与子进程名相同。注意不要 exported, 否则会有安全风险\n```xml\n\u003cprovider\n      android:authorities=\"com.trend.lazyinject.demo.p1\"\n      android:name=\"com.trend.lazyinject.lib.ipc.InjectIPCProvider\"\n      android:process=\"com.trend.lazyinject.demo.p1\" /\u003e\n```\n\n因为需要 IPC，所以所有 Provider 方法的参数和返回值必须继承自 Serializable/Parcelable/IBinder\n你也可以直接调用 Component 中的方法，这些都是支持 IPC 的\n# 实现原理  \n[Design Document](https://github.com/ganyao114/lazyInject/blob/master/doc/di_design.md)\n\n# 使用中\n\n\u003cimg src=\"https://github.com/ganyao114/lazyInject/raw/master/doc/dr_safety.png\" width=\"120\" hegiht=\"120\" align=left /\u003e\n\n[Dr.Safety](https://play.google.com/store/apps/details?id=com.trendmicro.freetmms.gmobi)\n\n\u003cimg src=\"https://github.com/ganyao114/lazyInject/raw/master/doc/dr_safety_main.png\" width=\"400\" hegiht=\"700\" align=center /\u003e  \n\n# Email\n939543405@qq.com  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fganyao114%2Flazyinject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fganyao114%2Flazyinject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fganyao114%2Flazyinject/lists"}