【问题标题】:Understanding Dagger 2 for Android development了解用于 Android 开发的 Dagger 2
【发布时间】:2015-08-28 17:28:09
【问题描述】:

这是我的代码,它基于互联网上的一些旧教程。 Dagger 2的主站点上应该有一些例子,我发现如何实现这一切真的很难理解。

要运行这么简单的应用程序确实需要大量工作。我有两个问题:

我是否必须在每个类中调用 DaggerLoggerComponent 我想获得一些组件,比如我的 Logger 类?

另外,我怎样才能使 Logger 类的范围成为单例?现在每个按钮单击都会创建一个新的记录器实例。

可能我不了解一些底层概念,我之前只在 Spring 中使用过依赖注入,这一切对我来说似乎很奇怪。

public class MainActivity extends AppCompatActivity {

    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init(){
        button = (Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LoggerComponent component = DaggerLoggerComponent.builder().loggerModule(new LoggerModule()).build();
                component.getLogger().log("Hello!",MainActivity.this);
            }
        });
    }

}


public class Logger {

    private static int i = 0;

    public Logger(){
        i++;
    }

    public static int getI() {
        return i;
    }

    public void log(String text, Context context){
        Toast.makeText(context,text+" "+i,Toast.LENGTH_SHORT).show();
    }
}


@Singleton
@Component(modules={LoggerModule.class})
public interface LoggerComponent {

    Logger getLogger();

}


@Module
public class LoggerModule {
    @Provides
    @Singleton
    Logger provideLogger(){
        return new Logger();
    }
}

【问题讨论】:

    标签: android dependency-injection dagger-2


    【解决方案1】:

    答案是

    public class MainActivity extends AppCompatActivity {
        @OnClick(R.id.button) //ButterKnife
        public void onClickButton() {
            logger.log("Hello!");
        }
    
        @Inject
        Logger logger;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Injector.INSTANCE.getApplicationComponent().inject(this);
            ButterKnife.bind(this);
        }
    
        @Override
        protected void onDestroy() {
            ButterKnife.unbind(this);
            super.onDestroy();
        }
    }
    
    public class Logger {
        private static int i = 0;
    
        private CustomApplication customApplication;
    
        public Logger(CustomApplication application) {
            this.customApplication = application;
            i++;
        }
    
        public static int getI() {
            return i;
        }
    
        public void log(String text){
            Toast.makeText(customApplication, text + " " + i,Toast.LENGTH_SHORT).show();
        }
    }
    
    
    public interface LoggerComponent {
        Logger logger();
    }
    
    @Module
    public class ApplicationModule {
        private CustomApplication customApplication;
    
        public ApplicationModule(CustomApplication customApplication) {
            this.customApplication = customApplication;
        }
    
        @Provides
        public CustomApplication customApplication() {
            return customApplication;
        }
    }
    
    @Module
    public class LoggerModule {
        @Provides
        @Singleton
        Logger provideLogger(){
            return new Logger();
        }
    }
    
    
    @Singleton
    @Component(modules={LoggerModule.class, ApplicationModule.class})
    public interface ApplicationComponent extends LoggerComponent {
        CustomApplication customApplication();
    
        void inject(MainActivity mainActivity);
    }
    
    public class CustomApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            Injector.INSTANCE.initializeApplicationComponent(this);
        }
    }
    
    public enum Injector {
        INSTANCE;
    
        private ApplicationComponent applicationComponent;
    
        public ApplicationComponent getApplicationComponent() {
            return applicationComponent;
        }
    
        void initializeApplicationComponent(CustomApplication customApplication) {
            this.applicationComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(customApplication))
                .build();
        }
    }
    

    This is currently our Dagger2 architecture.

    编辑:这是来自我们正在制作的应用程序中改造内容的实际代码:

    public interface RecordingService {    
        ScheduledRecordsXML getScheduledRecords(long userId)
                throws ServerErrorException;
    }
    
    public class RecordingServiceImpl
            implements RecordingService {
    
        private static final String TAG = RecordingServiceImpl.class.getSimpleName();
    
        private RetrofitRecordingService retrofitRecordingService;
    
        public RecordingServiceImpl(RetrofitRecordingService retrofitRecordingService) {
            this.retrofitRecordingService = retrofitRecordingService;
        }
    
        @Override
        public ScheduledRecordsXML getScheduledRecords(long userId)
                throws ServerErrorException {
            try {
                return retrofitRecordingService.getScheduledPrograms(String.valueOf(userId));
            } catch(RetrofitError retrofitError) {
                Log.e(TAG, "Error occurred in downloading XML file.", retrofitError);
                throw new ServerErrorException(retrofitError);
            }
        }
    }
    
    @Module
    public class NetworkClientModule {
        @Provides
        @Singleton
        public OkHttpClient okHttpClient() {
            OkHttpClient okHttpClient = new OkHttpClient();
            okHttpClient.interceptors().add(new HeaderInterceptor());
            return okHttpClient;
        }
    }
    
    @Module(includes = {NetworkClientModule.class})
    public class ServiceModule {
        @Provides
        @Singleton
        public RecordingService recordingService(OkHttpClient okHttpClient, Persister persister, AppConfig appConfig) {
            return new RecordingServiceImpl(
                    new RestAdapter.Builder().setEndpoint(appConfig.getServerEndpoint())
                            .setConverter(new SimpleXMLConverter(persister))
                            .setClient(new OkClient(okHttpClient))
                            .setLogLevel(RestAdapter.LogLevel.NONE)
                            .build()
                            .create(RetrofitRecordingService.class));
        }
    
        //...
    }
    
    public interface RetrofitRecordingService {
        @GET("/getScheduledPrograms")
        ScheduledRecordsXML getScheduledPrograms(@Query("UserID") String userId);
    }
    
    public interface ServiceComponent {
        RecordingService RecordingService();
    
        //...
    }
    
    public interface AppDomainComponent
            extends InteractorComponent, ServiceComponent, ManagerComponent, ParserComponent {
    }
    
    @Singleton
    @Component(modules = {
            //...
            InteractorModule.class,
            ManagerModule.class,
            ServiceModule.class,
            ParserModule.class
        //...
    })
    public interface ApplicationComponent
            extends AppContextComponent, AppDataComponent, AppDomainComponent, AppUtilsComponent, AppPresentationComponent {
        void inject(DashboardActivity dashboardActivity);
        //...
    }
    

    【讨论】:

    • 老实说,“参考”是官方文档,太糟糕了,我从教程、示例代码、我之前使用 Dagger1(基于 Android Bootstrap)中学到的,并在我们正在工作的项目(并弄清楚例如,您需要为模块提供程序方法提供范围以获取范围提供程序)。除此之外,请查看 Stack Overflow 上的 Dagger-2 标签,根据我目前的经验,我已经回答了一大堆问题——尽管我还没有写过关于 正确 方式的文章还在嘲笑呢。
    • 没有问题 :) 只要确保你知道我不是任何官员或任何人,随着我们的进步,我会学到关于 Dagger2 和其他东西的新东西 :P 所以请始终保持批判性的眼光.我一直在思考如何进行适当的模拟,但我不想使用特定的“提供者”实例对每个模块进行参数化以进行测试和生产(你不能扩展模块..-_-) .... 所以是的,如果你想出正确的嘲笑方式,请告诉我!这可能与“真实”和“测试”组件的提供者和注入器方法的继承有关。
    • 从技术上讲,我们所做的是我们为每个模块都有一个___Component 接口,但实际上,它们只是存储提供方法的接口。它们不是实际的组件。我们的组件中只有一个组件,但您也可以根据需要添加子范围组件(例如,活动的组件并拥有自己的活动范围模块)。如此快速的答案,I need to add an additional inject method to the ApplicationComponentyes,对于后半部分,您只需向模块添加一个提供程序方法(不要忘记范围)并为组件提供。是的
    • 无论如何,我从我们的应用中添加了一些代码来展示我们为改造相关的东西做了什么。
    • 感谢您的帮助!你太棒了:)
    【解决方案2】:

    我是否必须在每个类中调用 DaggerLoggerComponent 我想获得一些组件,比如我的 Logger 类?

    是的,适用于系统创建的所有类,如应用程序、活动和服务。但是对于您自己的课程,您不需要它。只需使用 @inject 注释您的构造函数,dagger 将提供您的依赖项。

    另外,我怎样才能使 Logger 类的范围成为单例?正确的 现在每个按钮单击都会创建一个新的记录器实例。

    您的单例设置是正确的。但是您必须在创建活动(onCreate)后初始化一次组件,以便让匕首注入所有字段。如果您不需要立即使用 Logger 对象,也可以使用延迟注入功能。

        @Inject
        Logger logger;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            LoggerComponent component = DaggerLoggerComponent.builder().loggerModule(new LoggerModule()).build();
    
            component.inject(this);
    
            init();
        }
    

    然后您可以访问您的对象,而无需从组件中获取引用:

    private void init(){
            button = (Button)findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    logger.log("Hello!",MainActivity.this);
                }
            });
        }
    

    总结: 您必须在所有使用字段注入的类中初始化组件。

    更新: 要进行实际的注入,您必须在组件中声明 inject() 方法,并且 dagger 会自动实现它。此方法将负责提供任何带有 @Inject 注释的对象。

    【讨论】:

    • 但是有可能以某种方式用匕首包装系统创建的类吗?我在某处看到它,扩展应用程序类等,但我找不到一个很好的例子。你知道怎么做吗?因为如果我只想拥有给定服务的一个实例,我无法在我所做的每个活动中创建 DaggerLoggerComponent。我认为应该在某种容器中管理活动
    • 您的代码也不起作用。 @Inject Logger 是一个空指针,你确定在 onCreate 中创建一个本地 LoggerComponent 就足够了吗?
    • 我更新了答案。我忘了在组件上调用注入方法。你的组件接口应该有 inject(MainActivity activity) 方法。
    • 是的,它奏效了。我还是不太喜欢这个。记录器组件与 MainActivity 紧密耦合。我并没有真正看到使用这个库的好处......似乎真的需要做很多工作来做简单的事情
    • 实际上,您真正关心的应该是组件的范围与组件实例相关联,这意味着您的“单例记录器”将与包含的活动一起被杀死并重新创建组件。
    猜你喜欢
    • 1970-01-01
    • 2015-10-27
    • 1970-01-01
    • 2019-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 1970-01-01
    相关资源
    最近更新 更多