RoboGuice 解析

背景:

网上各路资料都是浅层次的使用说明,即使是作为供他人使用的接口说明也是不合格的,所以还是得看看源码看官方文档。

Guice产生的源头

通常我们的一个Service 依赖多个其他的模块A,B,C, D,在Service里初始化这些A,B,C,D呢?大家都知道,搞UT的时候最麻烦最恶心的地方就是要考虑如何去Mock掉这些依赖,所以这样当然不是很好的方式。
那么通过setter来将它们设置进来呢?你会看到在实例化Service前有一大坨的对依赖的初始化代码。而且这些过程可能比较容易出错,比如你写的库给别人使用,其中的某个服务有较多的依赖,那么即使你写的很好的doc,而别人在使用的时候,会痛苦,最严重的是会出错。

那么Guice的作用就是 将依赖的处理也给默默的做了,那么在实例化服务的时候将会非常的清爽。
下面是SomeServices服务,其有两个依赖Depends2,Depends1

public class SomeServices {
    Depends2 depends2;
    @Inject
    Depends1 depends1;

    @Inject
    public SomeServices( Depends2 depends2) {
        this.depends2 = depends2;

    }

    public void start() {
        depends2.start();
        depends1.start();
    }
}

实例化SomeServices时候,只有简单的一行。

SomeServices someServices = Guice.createInjector(new SomeServicesModule()).getInstance(SomeServices.class);

Bindings

借助注解@Inject就能够将依赖给解决掉?这背后是如何实现的呢?
通过注解@Inject我们只知道依赖模块的类类型,也许他还是抽象类,离真正的解决依赖还有一段距离,那么就是通过Binding把这个差距给“连接”起来。

它在哪里实现

创建一个继承自AbstractModule的子类,然后实现configure方法。
Guice使用EDSL语言来使得创建bindings更简单可读性更好,
比如 bind ….to..
见:https://github.com/google/guice/wiki/Bindings
那么可以理解Bindings (实质就是对configure方法的实现过程)就是生成相应Injecter的一个配置。

那么RoboGuice又是?

RoboGuice 是Guice的扩展,增加了Android的支持。
从头说起,Guice跟Android平台无关,如果我们想使用一个fragment,他里面也是有很多依赖,依赖外界资源,依赖其他服务,依赖,依赖View组件,这堆依赖初始化也挺费事,直接用guice可以!但是你需要自己写Bindings, 比如@Inject Button btn;
那么RoboGuice就是帮你将Android平台下的Bindings给写好了。这就是他存在的意义。

继承RoboFragment?

那为什么没有见Guice.createInjector.getInstance之流?而是继承个RoboFragment?那么进入到RoboFragment中看看就明白了,在onCreate中

final RoboInjector injector = RoboGuice.getInjector(this);
injector.injectMembersWithoutViews(this);

这里写图片描述

getInjector 根据上下文环境获取适合的Injector

Injector是如何获取到的。

似乎Guice 直接getInjector(this) 就拿到了injector,那么Module是在哪里声明的呢?没有看到Module,我们就无法知晓注入的具体实现。

    public static RoboInjector getInjector(Context context) {
        final Application application = (Application)context.getApplicationContext();
        return new ContextScopedRoboInjector(context, getBaseApplicationInjector(application), getViewListener(application));
    }

再看getBaseApplicationInjector

    protected static WeakHashMap<Application,Injector> injectors = new WeakHashMap<Application,Injector>();
 public static Injector getBaseApplicationInjector(Application application) {
        Injector rtrn = injectors.get(application);
        if( rtrn!=null )
            return rtrn;

        synchronized (RoboGuice.class) {
            rtrn = injectors.get(application);
            if( rtrn!=null )
                return rtrn;

            return setBaseApplicationInjector(application, DEFAULT_STAGE);
        }
    }

injectors类似一个Injector的缓存,初始的时候当然是为空的。所以再看setBaseApplicationInjector

 public static Injector setBaseApplicationInjector(Application application, Stage stage) {

        synchronized (RoboGuice.class) {
            int id = modulesResourceId;
            if (id == 0)
                id = application.getResources().getIdentifier("roboguice_modules", "array", application.getPackageName());

            final String[] moduleNames = id>0 ? application.getResources().getStringArray(id) : new String[]{};
            final ArrayList<Module> modules = new ArrayList<Module>();
            final DefaultRoboModule defaultRoboModule = newDefaultRoboModule(application);

            modules.add(defaultRoboModule);

            try {
                for (String name : moduleNames) {
                    final Class<? extends Module> clazz = Class.forName(name).asSubclass(Module.class);

                    try {
                        modules.add(clazz.getDeclaredConstructor(Context.class).newInstance(application));
                    } catch( NoSuchMethodException ignored ) {
                        modules.add( clazz.newInstance() );
                    }

                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            final Injector rtrn = setBaseApplicationInjector(application, stage, modules.toArray(new Module[modules.size()]));
            injectors.put(application,rtrn);
            return rtrn;
        }

    }

解析上面的代码可以了解到:
1.Guice会默认去读取代码中名为roboguice_modules的string array里面的值,该值存什么呢?存储的是自定义AbstractModule的类全名,胡根据反射机制来实例化该AbstractModule。

2.Guice自己默认的AbstractModule为DefaultRoboModule,如果想了解一下其注入的实现可看看。

然后调用injector的injectMembersWithoutViews方法,该方法主要就是注入非View的Java对象。

在onViewCreated中

RoboGuice.getInjector(getActionBarActivity()).injectViewMembers(this);

这里写图片描述

注:
所有对View的插入实现都在ViewMembersInjector中。

几个常用注解

注解名意义
@InjectView(R.id.name)
@InjectResource(R.drawable.icon)
@Inject
@Singleton我们想要使用单例模式(Singleton Pattern)来获取对象,即 One Instance in the application,你可以在实现类上使用 Singleton 注释去标记。
@ProvidedBy(AddProvider.class)可以直接在接口声明处使用 ProvidedBy 注释来指定该接口的 Provider 类型
@ImplementedBy(SimpleAdd.class)如果 Add 接口有多个实现类,但是我们希望 SimpleAdd 是 Add 的默认实现类
@InjectExtra (“Extra1” ) String extra1??

几个常用Bindings写法

类型意义
binder.bind(Add.class).to(SimpleAdd.class)
bind(CityController.class).in(Singleton.class)没有指明目标实现类,那么要求类CityController应该由@ImplementedBy或@ProvidedBy标注过(也就是间接的定义了目标实现类类型)
toProvider(new CategoryProvider())指出目标实现类的Provider
annotatedWith(Names.named(“deal”))在该接口声明的地方是否有注解Names(“deal”),一般当接口有多个实现类的时候,通过注解Names来标明最终是用哪个实现类实例化

指明目标实现类的方法

一.在configure方法中通过bind().to(Target.class);
二.创建实现Provider接口的类,并在configure方法中通过bind().toProvider(new TargetProvider());
三.在AbstractModule的子类中,编写方法,返回相应类型,并用注解@Provides标注该方法,和方法二类似。
四.在接口定义上以@ImplementedBy(SimpleAdd.class)或 @ProvidedBy(AddProvider.class)标注

其他

install

在一个Module中

protected void configure {
        install(new NetModule(application));
}

在官方文档上很简单的说明:

void install(Module module)
Uses the given module to configure more bindings.

在宿主Module中引入NetModule中定义的bindings,类似允许一种封装及扩展吧。

参考链接:

Guice guide

Google Guice 系列教程 - 基础实践

Introduction to Google Guice

RoboGuice guide

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值