RoboGuice 3.0 (三)总结篇

经过前两篇的介绍,我们了解了如何使用RoboGuice方便的为我们注入需要的对象,这篇将着重说明原理。

一.Guice与RoboGuice

Guise是Google开发的一个轻量级的依赖注入框架,主要针对Java使用的。

RoboGuice是基于Guice库开发,目的为Android提供一套简单易用的依赖注入框架。

上两篇中所提到的POJO注入,说白了就是对象注入,大部分方法都是Guice框架中的。RoboGuice主要在视图注入及Android个性化的注入上下功夫。

二.RoboGuice注入对象

就算没用过RoboGuice,但是大家也都听过,RoboGuice是通过反射来实现注入的。

为了了解实现的原理,我们先看下RoboActivity的代码。
其中eventManager初始化方式使用的就是之前提过的RoboGuice.getInjector(),其内部提供了各种事件的注册,反注册,分发等等功能。

public class RoboActivity extends Activity implements RoboContext {
    protected EventManager eventManager;
    protected HashMap<Key<?>,Object> scopedObjects = new HashMap<Key<?>, Object>();

    @Inject ContentViewListener ignored; // BUG find a better place to put this
    private Stopwatch stopwatch;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        stopwatch = new Stopwatch();
        final RoboInjector injector = RoboGuice.getInjector(this);
        stopwatch.resetAndLog("RoboActivity creation of injector");
        eventManager = injector.getInstance(EventManager.class);
        stopwatch.resetAndLog("RoboActivity creation of eventmanager");
        injector.injectMembersWithoutViews(this);
        stopwatch.resetAndLog("RoboActivity inject members without views");
        super.onCreate(savedInstanceState);
        stopwatch.resetAndLog("RoboActivity super onCreate");
        eventManager.fire(new OnCreateEvent<Activity>(this,savedInstanceState));
        stopwatch.resetAndLog("RoboActivity fire event");
    }
}

其次注意到被注入的ContentViewListener,这就是为了实现在Activity上的ContentView的注解,这里的方法是

@ContextSingleton
public class ContentViewListener {
    @Inject protected Activity activity;

    public void optionallySetContentView( @Observes OnCreateEvent<?> ignored ) {
        Class<?> c = activity.getClass();
        while( c != Context.class ) {
            final ContentView annotation = c.getAnnotation(ContentView.class);
            if( annotation!=null ) {
                activity.setContentView(annotation.value());
                return;
            }
            c = c.getSuperclass();
        }
    }
}

这里观察的是OnCreate的事件,事件的分发代码在OnCreate方法中,实现了setContentView的方法。

onCreate方法简化后如下。可以发现inject对象的时机是在super的onCreate之前的,

@Override
    protected void onCreate(Bundle savedInstanceState) {
        final RoboInjector injector = RoboGuice.getInjector(this);
        eventManager = injector.getInstance(EventManager.class);
        injector.injectMembersWithoutViews(this);
        super.onCreate(savedInstanceState);
        eventManager.fire(new OnCreateEvent<Activity>(this,savedInstanceState));
    }

RoboInjector其实是RoboGuice内部的一个Guice Injector,大部分注入工作交给了Guice。

反射注入这一块就不深入讨论了,下面贴出了Guice的部分代码。

private static void computeInjectableMembers(TypeLiteral<?> type, boolean statics, Errors errors, InjectionPoint.InjectableMembers injectableMembers, InjectionPoint.OverrideIndex overrideIndex, HierarchyTraversalFilter filter) {
    Class rawType = type.getRawType();
    if(isWorthScanning(filter, rawType)) {
        Class parentRawType = rawType.getSuperclass();
        if(isWorthScanning(filter, parentRawType)) {
            computeInjectableMembers(type.getSupertype(parentRawType), statics, errors, injectableMembers, overrideIndex, filter);
            overrideIndex.position = InjectionPoint.Position.MIDDLE;
        } else {
            overrideIndex.position = InjectionPoint.Position.TOP;
        }

        Set allFields = filter.getAllFields(Inject.class.getName(), rawType);
        if(allFields != null) {
            Iterator allMethods = allFields.iterator();

            while(allMethods.hasNext()) {
                Field i$ = (Field)allMethods.next();
                if(Modifier.isStatic(i$.getModifiers()) == statics) {
                    Annotation method = getAtInject(i$);
                    if(method != null) {
                        InjectionPoint.InjectableField atInject = new InjectionPoint.InjectableField(type, i$, method);
                        if(atInject.jsr330 && Modifier.isFinal(i$.getModifiers())) {
                            errors.cannotInjectFinalField(i$);
                        }

                        injectableMembers.add(atInject);
                    }
                }
            }
        }

        Set allMethods1 = filter.getAllMethods(Inject.class.getName(), rawType);
        if(allMethods1 != null) {
            Iterator i$1 = allMethods1.iterator();

            while(true) {
                while(true) {
                    while(true) {
                        Method method1;
                        do {
                            if(!i$1.hasNext()) {
                                return;
                            }

                            method1 = (Method)i$1.next();
                        } while(!isEligibleForInjection(method1, statics));

                        Annotation atInject1 = getAtInject(method1);
                        if(atInject1 != null) {
                            InjectionPoint.InjectableMethod removed2 = new InjectionPoint.InjectableMethod(type, method1, atInject1);
                            if(!checkForMisplacedBindingAnnotations(method1, errors) && isValidMethod(removed2, errors)) {
                                if(statics) {
                                    injectableMembers.add(removed2);
                                } else {
                                    overrideIndex.removeIfOverriddenBy(method1, true, removed2);
                                    overrideIndex.add(removed2);
                                }
                            } else {
                                boolean removed1 = overrideIndex.removeIfOverriddenBy(method1, false, removed2);
                                if(removed1) {
                                    logger.log(Level.WARNING, "Method: {0} is not a valid injectable method (because it either has misplaced binding annotations or specifies type parameters) but is overriding a method that is valid. Because it is not valid, the method will not be injected. To fix this, make the method a valid injectable method.", method1);
                                }
                            }
                        } else {
                            boolean removed = overrideIndex.removeIfOverriddenBy(method1, false, (InjectionPoint.InjectableMethod)null);
                            if(removed) {
                                logger.log(Level.WARNING, "Method: {0} is not annotated with @Inject but is overriding a method that is annotated with @javax.inject.Inject.  Because it is not annotated with @Inject, the method will not be injected. To fix this, annotate the method with @Inject.", method1);
                            }
                        }
                    }
                }
            }
        }
    }
}

三.总结

这三篇过来,依赖注入给我们带来了什么?

解耦。

当我们在一个对象中,不需要关心它所依赖的成员如何初始化,只关心用来使用或获取属性,依赖注入为我们实现了解耦。

再就是RoboGuice的贴心,将Android基本组件考虑在内,为我们实现了很多注入,减少了我们调用系统服务或组件的代码,再就是RoboGuice考虑到了Android生命周期的特殊问题,将注入的成员对象生命周期保持与Context生命周期一致。

再说说反射,尽管RoboGuice强调,使用roboblender会优化很大一部分注解性能,但是反射对于移动端设备参差不齐的配置,还是让人有一点点担心,如果项目足够大,且使用了大量的注解及注入,那么性能一定是有影响的。

最后,RoboGuice确实是一个值得使用的框架,使用简单、上手较快、能实现模块解耦。光凭这几点优点便足以打动人心。

posted @ 2016-03-09 10:11  pedro_neer  阅读(765)  评论(0编辑  收藏  举报