經過前兩篇的介紹,我們瞭解瞭如何使用RoboGuice方便的為我們註入需要的對象,這篇將著重說明原理。 一.Guice與RoboGuice Guise是Google開發的一個輕量級的依賴註入框架,主要針對Java使用的。 RoboGuice是基於Guice庫開發,目的為Android提供一套簡單易用
經過前兩篇的介紹,我們瞭解瞭如何使用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確實是一個值得使用的框架,使用簡單、上手較快、能實現模塊解耦。光憑這幾點優點便足以打動人心。