如何使用Dagger注入静态类?

14

我想通过Dagger来介绍依赖注入到一个项目中。以下代码是用来描述向静态类注入依赖问题的示例。

setupTextView()静态方法从多个类中被调用:

public abstract class TextViewHelper {
    public static void setupTextView(TextView textView, 
                                     Spanned text, 
                                     TrackingPoint trackingPoint) {
        textView.setText(text, TextView.BufferType.SPANNABLE);
        textView.setMovementMethod(LinkMovementMethod.getInstance());
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyApp.getTracker().track(trackingPoint);
            }
        });
    }
}

下面是一个使用辅助方法的例子:

TextViewHelper.setupTextView(this, R.id.some_text_view, 
                             R.string.some_text,
                             TrackingPoint.SomeTextClick);

帮助方法中使用的跟踪是由应用程序类提供的:

public class MyApp extends Application {

    private static Tracking mTracking;

    public void onCreate() {
        super.onCreate();
        mTracking = getTracking(getApplicationContext());
    }

    private Tracking getTracking(Context context) {
        if (BuildConfig.DEBUG) {
            return new NoTracking();
        } else {
            return new NsaTracking(context);
        }
    }

    public static Tracking getTracker() {
        return mTracking;
    }

}

现在,我想通过Dagger注入追踪功能。当我重构代码时,我注意到我需要从我的Activity或Fragment将追踪对象传递给静态帮助程序,因为我无法直接注入静态类:

TextViewHelper.setupTextView(this, R.id.some_text_view, 
                             R.string.some_text, 
                             TrackingPoint.SomeTextClick,
                             Tracking tracking);

这不像是一个好的设计模式 - 因为我传递了TrackPointTracking对象。你会如何改进这个?


11
NSA跟踪 - ligi
1个回答

17

在你的TextViewHelper中创建一个带有追踪器的静态字段。

public class TextViewHelper {

    private TextViewHelper(){}

    @Inject
    static Tracking sTracker;

    public static void setupTextView(TextView textView, 
                                     Spanned text, 
                                     TrackingPoint trackingPoint) {
        textView.setText(text, TextView.BufferType.SPANNABLE);
        textView.setMovementMethod(LinkMovementMethod.getInstance());
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sTracker.track(trackingPoint);
            }
        });
    }
}

以下是配置该模块的步骤:

@Module(staticInjections = TextViewHelper.class)
public class TrackerModule {
...
}

最重要的是,在您的图形上调用injectStatics。

mObjectGraph = ObjectGraph.create(new TrackerModule());
mObjectGraph.injectStatics();

编辑:

正如您指出的,Dagger的文档说明静态注入“应该谨慎使用,因为静态依赖关系很难测试和重复使用。”这是完全正确的,但因为您询问如何将对象注入到实用程序类中,这是最佳解决方案。

但是,如果您希望代码更易于测试,请创建以下模块:

@Module(injects = {classes that utilizes TextViewHelper})
public class TrackerModule {

      @Provides
      Tracking provideTracker() {
           ...
      }

      @Provides
      @Singleton
      TextViewHelper provideTextViewHelper(Tracking tracker) {
           return new TextViewHelper(tracker);
      }
}
现在你可以从TextViewHelper方法中移除static关键字,因为这个实用工具类将使用dagger进行注入。
public class TextViewHelper {

    private final Tracking mTracker;

    public TextViewHelper(Tracking tracker){
        mTracker = tracker;
    }

    public void setupTextView(TextView textView, 
                              Spanned text, 
                              TrackingPoint trackingPoint) {
         ...
    }
}

如果你想遵循良好的实践,这就是应该采取的方法。两种解决方案都可以工作,所以选择哪一种由你决定。


1
关于静态注入Dagger文档指出:“这个特性应该谨慎使用,因为静态依赖很难测试和重用。”你认为这个可以被很好地测试吗? - JJD
在我看来,TextViewHelper不应该是一个带有静态方法的类。将其视为另一个要注入的对象,这样您将不再需要静态注入。在您的模块中创建两个方法,一个提供跟踪器,另一个提供TextViewHelper。在需要TextViewHelper的类中只需创建一个带有@Inject的字段。这样,您的代码将非常易于测试,而且您将不再需要静态注入。 - tomrozb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接