Dagger 2 创建单例实例

Dagger 2 create singleton instance

考虑我在 Presenter A 调用 Webservice 并在同一个 Presenter 保存响应数据的场景。我想在演示者 E 上使用相同的响应数据。但是我无法将响应对象传递给每个演示者 B、C、D。因此,我尝试将我的响应对象存储在单独的 Holder class 和 getter & setter。我使用 Dagger Inject 构造函数注释初始化了 Holder class 并尝试在 Presenter E 上使用它。但是我得到的是 Empty 响应而不是我的 datas 。谁能建议我以最好的方式处理这种情况。提前致谢

我可以想象这个解决你的问题。

关键是,你需要从你的演示者中删除 ApiRequest 并使用 interactor,两个演示者都会收到注入到他的构造函数中的这个 Interactor,在这种情况下,他们将共享相同的交互者实例(如果他是单身人士)。交互器负责做缓存,如果你使用 OkHttpClient 你可以不使用 holder class 来做缓存(由你决定),在这个解决方案中你不会执行 2 api要求相同的数据。

像这样:

public class PresenterA (){
    private UserInteractor userInteractor;
    private ViewA view;

    public PresenterA(UserInteractor interactor, ViewA view){
        this.interactor = interactor;
        this.view = view;
    }

    public void getUser(){
        interactor.findFirst(new MyCallback(){
            @Override
            public void onSuccess(User user){
                view.loadUserName(user.getName());
            }
        });
    }
}

public class PresenterB (){
    private UserInteractor userInteractor;
    private ViewB view;

    public PresenterA(UserInteractor interactor, ViewB view){
        this.interactor = interactor;
        this.view = view;
    }

    public void getUser(){
        interactor.findFirst(new MyCallback(){
            @Override
            public void onSuccess(User user){
                view.loadAddress(user.getAddress().getLine1());
            }
        });
    }
}

public class UserInteractor (){
    private MyHolderData holder;
    private MyApi api;

    public UserInteractor(MyHolderData holder, MyApi api){
        this.holder = holder;
        this.api = api;
    }

    public User getUser(){
        if(holder.hasCache()){
            return holder.getUser();
        }
        api.requestUser(new MyApiCallback(){
            @Override
            public void onSuccess(User user){
                return user;
            }
        })
    }

}

你有很多关于你如何使用 Dagger2 的基本问题,但我的时间有限,所以我暂时回答关于 Mortar - 所有我可以说在某些设备上,getSystemService()Application 中比 onCreate() 更早被调用,这意味着你应该像这样初始化你的根迫击炮范围:

@Override
public Object getSystemService(String name) {
    if(rootScope == null) {
        rootScope = MortarScope.buildRootScope()
                .withService(InjectorService.TAG, new InjectorService(this))
                .build("Root");
    }
    if(rootScope.hasService(name)) { // if the additional "Context" service is within Mortar
        return rootScope.getService(name);
    }
    return super.getSystemService(name); // otherwise return application level context system service
}

就个人而言,我在 onCreate()

@Override
public void onCreate() {
    super.onCreate();
    Fabric.with(this, new Crashlytics());
    realmHolder = new RealmHolder();
    ApplicationHolder.INSTANCE.setApplication(this);
    appConfig = new AppConfig(this);
    InjectorService.obtain().inject(this); // <--- this one obtains component
    initializeRealm();
}

并且在 InjectorService 中:

public static ApplicationComponent obtain() {
    return ((InjectorService) MortarScope.getScope(ApplicationHolder.INSTANCE.getApplication())
            .getService(TAG)).getComponent();
}

因此,在最坏的情况下,getSystemService() 在启动时或创建单例匕首组件时初始化了我的 RootScope。

此解决方案目前对多进程不友好(因此 Firebase 崩溃报告会通过在 CustomApplication 中调用 onCreate() 两次来终止它)

编辑:喷油器服务代码

public class InjectorService {
    public static final String TAG = "InjectorService";

    private ApplicationComponent applicationComponent; //dagger2 app level component

    InjectorService(CustomApplication customApplication) {
        AppContextModule appContextModule = new AppContextModule(customApplication);
        RealmModule realmModule = new RealmModule();
        applicationComponent = DaggerApplicationComponent.builder()
                .appContextModule(appContextModule)
                .realmModule(realmModule)
                .build();
    }

    public ApplicationComponent getInjector() { //return the app component to inject `this` with it
        return applicationComponent;
    }

    public static InjectorService get(Context context) {
        //this is needed otherwise the compiler is whining. -_-
        //noinspection ResourceType
        return (InjectorService) context.getSystemService(TAG);
    }

    public static ApplicationComponent obtain() {
        return ((InjectorService) MortarScope.getScope(ApplicationHolder.INSTANCE.getApplication())
                .getService(TAG)).getInjector();
    }
}

至于你最初的问题,这是因为你在你的构造函数中添加了 @Inject 注释,但没有在 class 本身

上包含 @Singleton
@Singleton
public class Blah {
    @Inject
    public Blah() {
    }
}

编辑:

我度假回家,所以初始错误是

Error:(40, 5) error: com.hari.daggerpoc.application.App.Component scoped with @com.hari.daggerpoc.frameworks.dagger.DaggerScope may not reference bindings with different scopes: @Singleton class com.hari.daggerpoc.cache.ResponseCache

App中的这个class:

@dagger.Component(modules = {Module.class})
@DaggerScope(Component.class)
public interface Component extends AppDependencies {

    void inject(App app);
}

继承自这个class:

@Module(includes = {Utils.class, ResponseCache.class})
public interface AppDependencies {

    Utils utils();

    ResponseCache responseCache();

}

...这完全不是一个模块,所以注释是不必要的,但是嘿。

无论如何,现在的问题是,虽然依赖是有范围的,但它来自不同的范围(我不知道没有使用单例范围),所以如果你改变

@Singleton
public class ResponseCache {
    @Inject
    public ResponseCache(){    
    }

@DaggerScope(App.Component.class)
public class ResponseCache {

    @Inject
    public ResponseCache(){

    }

然后如果在ScreenA你改变

    public Callback<WeatherResponse> configServiceCallback = new Callback<WeatherResponse>() {
        @Override
        public void onResponse(Call<WeatherResponse> call, Response<WeatherResponse> response) {
            Log.d("ScreenA","Response data -->"+response.body().toString());
            Flow.get(context).setHistory(History.single(new ScreenB()), Flow.Direction.FORWARD);
            responseCache.setWeatherResponse(response.body());
        }

    public Callback<WeatherResponse> configServiceCallback = new Callback<WeatherResponse>() {
        @Override
        public void onResponse(Call<WeatherResponse> call, Response<WeatherResponse> response) {
            Log.d("ScreenA","Response data -->"+response.body().toString());
            responseCache.setWeatherResponse(response.body());
            Flow.get(context).setHistory(History.single(new ScreenB()), Flow.Direction.FORWARD);
        }

然后它说

08-28 18:12:48.369 31253-31253/com.hari.daggerpoc D/ScreenA: Response data -->WeatherResponse{endpoint=Endpoint{url='http://www.waynedgrant.com/weather/api/weather.json', version=1.7, githubProject='null', copyright='Copyright © 2016 Wayne D Grant (www.waynedgrant.com)'}}
08-28 18:12:48.369 31253-31253/com.hari.daggerpoc D/ScreenB: Response cache -->WeatherResponse{endpoint=Endpoint{url='http://www.waynedgrant.com/weather/api/weather.json', version=1.7, githubProject='null', copyright='Copyright © 2016 Wayne D Grant (www.waynedgrant.com)'}}