在Dagger 2中异步构建依赖关系图

10

这是一个更加理论性的问题。如果我走错了方向,请告诉我。

在Dagger 2中,有没有一种异步/并行加载一些图形依赖项的方式?在Dagger的上下文中是否应该考虑这个问题?

我的问题主要与应用程序启动时间有关。许多外部依赖项,如Mixpanel、Crashlytics/Fabric、Retrofit(RestAdapter)会导致应用程序的预热时间超过1秒。

Lazy<>接口对我有很大帮助,但最终效果仍然无法令我满意。

有什么想法吗?

示例

应用程序具有SplashActivity,它取决于SplashActivityPresenter,后者又取决于:Mixpanel、RestAdapter和Crashlytics库(以及几个“较小”的对象)。每个对象都有一个.init()方法,需要很长时间(Mixpanel在Nexus 5上需要大约200ms来初始化,Android M。因此,在用户看到Splash屏幕之前,需要大约2秒钟。

有没有一种在并行构造这些对象的方式?


请纠正我如果我错了,但是Dagger 2依赖于生成的代码,所以不应该影响启动时间。 - Egor
为什么你的SplashActivity依赖于那些库?它真的需要吗?如果不需要,那么你可以在创建这些对象之前显示闪屏,但这并不能解决问题。对于闪屏来说,2秒甚至太长了。 - Michał Z.
这只是用来描述问题的依赖关系图的简化版本。在实际解决方案中,Presenter 依赖于 AnalyticsManager 和 UserManager。AnalyticsManager 依赖于 GoogleAnalytics 和 Mixpanel,UserManager 依赖于 LocalStoreManager 和 RestAdapter。以此类推... - froger_mcs
我明白了,但我不明白为什么你需要在SplashActivity中拥有所有这些。SplashActivity不能是一个简单的Activity,在创建后创建所有的管理器吗? - Michał Z.
将这些初始化用RxJava包围起来并返回Observable,可能已经足够了吗? - Przemysław Piechota. kibao
显示剩余2条评论
2个回答

9
保持图形创建同步,并使用Rx的defer延迟对象创建,直到订阅。通过Subject可以通知下游注入,在对象创建时。
@Module public static class AppModule{

    @Provides @Singleton
    public Observable<A> provideA(){
        return Observable.defer(
                () -> Observable.just(new A()) 
        );
    }
}

public class MainActivity extends AppCompatActivity{

    @Inject Observable<A> aObservable;
    A a;
    AppComponent appComponent;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        if(appComponent == null){ 
            appComponent = ((App) getApplication())
                .getAppComponent(); 
        }
        appComponent.inject(this);

        aObservable.subscribeOn(Schedulers.io())
                   .observeOn(AndroidSchedulers.mainThread())
                   .subscribe(new Action1<A>(){
                       @Override public void call(final A aa){
                           a = aa;
                           Log.d("MainActivity", "a = aa");
                       }
                   });
    }
}

注意事项:必须手动检查对象是否为null,或依赖于主题作为事件总线,传递下游注入对象必须等待的对象布尔状态。

这个解决方案对我来说是一个好方法,但在这种情况下,你应该注意尚未初始化的对象,正如你所提到的。如果你只使用一些依赖项,则可以工作,但当你需要在任何地方都要关心它(比如在某些活动、Presenter、服务等中使用),就不能在其他地方做非空检查了。我只是在临时解决方案上工作,但最终可能需要一些注释处理。 - froger_mcs

1
Dagger 2 的对象图创建没有什么特别之处。如果您想在后台线程中完成它,只需在后台线程上调用 DaggerYourComponent.create()DaggerYourComponent.Builder.build()(使用您喜欢的方法来执行此操作 - 例如 AsyncTask)。
如果您有任何假定将在 UI 线程上运行的 @Inject 构造函数,则必须修改这些构造函数,但否则您不应该有任何问题。

1
对象构建的位置不是我的关注点。我面临的主要问题是图形是一个接一个地构建的对象。应用程序启动需要200毫秒的Mixpanel.init + 200毫秒的Crashlytics.init + 200毫秒的RestAdapter.init + (...)。我希望能够一次性并行构建这些对象。 - froger_mcs
4
你可能需要更新你的问题 - 你要求异步构建而不是并行构建。 - vaughandroid
异步的意思是同时处理多个任务,而不是在另一个线程上(因为在我看来这仍然是同步的)。 - froger_mcs
@froger_mcs 我同意 vaughandroid 的观点,你的问题确实表明你想在后台线程中完成所有操作并推迟应用程序/活动/组件的创建。如果你想并行调用 init,为什么不将它们放在单独的模块中,在后台调度程序上创建它们,并使用 RxJava 组合以一次获取 3 个?现在无法提供适当的答案,但我相信如果你编辑了你的问题,它会更清晰地表达你想要实现的目标。 - wasyl

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