安卓如何使用依赖注入来处理简单的自定义类。

3
在网上搜索了解这个功能后,大多数主题或帖子都使用依赖注入来使用Retrofit或其他有用的Android库,但我有一些自定义类想要使用DI,但我无法完成它。例如,我有一个简单的自定义类用于使用SharePreference,并将其用作Singleton类。
在我的代码中,我无法将正确的Dagger分配给SpApplication类的组件,以便在活动或片段中使用它。
public class SP {

    private SharedPreferences preferences;
    private Context context;

    public SP(Context context) {
        this.context = context;
    }

    private SharedPreferences getPrefs() {
        return preferences = PreferenceManager.getDefaultSharedPreferences(context);
    }

    public String getString(SharedPrefsTypes propertyName) {
        return getPrefs().getString(propertyName.toString(), "");
    }


    public int getInt(SharedPrefsTypes propertyName) {
        return getPrefs().getInt(propertyName.toString(), 0);
    }
    ...
    public enum SharedPrefsTypes {
        Login
    }
}

我将尝试使用依赖注入来实现这个目标:

AppModules 类:

@Module
public class AppModules {
    private Context context;

    public AppModules(Context context) {
        this.context = context;
    }

    @Provides
    @Singleton
    SP provideSharePreferences() {
        SP sharePreference = new SP(context);
        return sharePreference;
    }
}

ApplicationComponent 类:

@Component(modules = AppModules.class)
public interface ApplicationComponent {
    void inject(ActivityMain activity);
}

SpApplication 类:

public class SpApplication extends Application {
    private static SpApplication self;
    private ApplicationComponent component;

    @Override
    public void onCreate() {
        super.onCreate();

        self = this;
        component = DaggerApplicationComponent.builder().build();
    }


    public static SpApplication get(Context context) {
        return (SpApplication) context.getApplicationContext();
    }

    public static SpApplication getInstance() {
        return self;
    }

    public ApplicationComponent getComponent() {
        return component;
    }
}

我的ActivityMain类:

public class ActivityMain extends AppCompatActivity {
    @Inject
    SP sharePreference;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((SpApplication) getApplication()).getComponent().inject(this);
        sharePreference.setInt(SP.SharedPrefsTypes.Login, 0);
    }
}

我收到了这个错误信息:
android.app.Application cannot be cast to com.pishguy.yendir.SpApplication

感谢您的提前帮助。
1个回答

2
我猜你正在尝试注入到ActivityMain中,但由于你没有提供它的源代码,让我向你展示如何注入到SpApplication中。然后只需将相关部分复制到你的Activity中即可。
我认为你需要更改几件事情。
模块:
你的AppModules类通常是可以的,但我建议你更改使用Context的方式-不要将其用作字段,而是像任何其他服务一样进行注入。它看起来像这样:
@Module
public class AppModules {
    private Context context;

    public AppModules(Context context) {
        this.context = context;
    }

    @Provides // this can be non-scoped because anyway the same instance is always returned
    Context provideContext() {
        return this.context;
    }

    @Provides
    @Singleton
    SP provideSharePreferences(Context context) {
        return new SP(context); // use method-local Context
    }
}

组件:
  1. 如果组件注入了作用域服务(@Singleton 是一种作用域),则组件本身必须具有作用域
  2. 如果您想要将其注入到 SpApplication 类中,则在组件中将其声明为注入客户端
考虑到这两点,ApplicationComponent 应该如下所示:
@Singleton // injects @Singleton scoped services
@Component(modules = AppModules.class)
public interface ApplicationComponent {
    void inject(SpApplication application); // SpApplication is DI client (injection target)
}

客户:

我想在您的SpApplication类中更改一些内容:

  1. 您实例化ApplicationComponent的方式是不正确的
  2. 这不是一个错误,但您实际上不需要get(Context)getInstance()方法

此外,由于我正在展示如何注入到SpApplication中,因此我还将添加注入逻辑(您应该将其复制到实际的客户端中)。

因此,SpApplication(即DI客户端)应该类似于以下内容:

public class SpApplication extends Application {

    @Inject SP sp; // the dependency that should be injected

    private ApplicationComponent component;

    @Override
    public void onCreate() {
        super.onCreate();

        getComponent().inject(this); // this is when the actual injection takes place
    }


    public ApplicationComponent getComponent() {
        if (component == null) {
            // this is the way Dagger components should be instantiated
            component = DaggerApplicationComponent.builder()
                .appModules(new AppModules(this))
                .build();
        }
        return component;
    }
}

如果你进行了上述更改,我倾向于相信你会没事的。
顺便说一下,我最近写了一篇关于 Dagger 2 作用域的博客文章。如果你想认真学习依赖注入,可以去看看。链接在这里:blog post

非常感谢,但是在ApplicationComponent类中添加void inject(ActivityMain target)以便注入到ActivityMain后,我发现在活动中这一行代码sharePreference.setInt(SP.SharedPrefsTypes.Login,0);出现了null object reference的问题。 - mahdi pishguy
@mahdipishguy 那么你就需要将活动的代码添加到问题中... - Vasiliy
我已经添加了,谢谢。 - mahdi pishguy
@mahdipishguy,你实际上并没有向这个客户端注入任何东西。为了执行注入,你应该添加((SpApplication)getApplication()).getComponent().inject(this)。在使用注入服务之前,应该先执行此操作。 - Vasiliy
是的先生。我忘记添加它了。现在在添加了这行代码 ((SpApplication) getApplication()).getComponent().inject(this); 后,我得到了一个 android.app.Application cannot be cast to com.pishguy.yendir.SpApplication 的错误。 - mahdi pishguy
@mahdipishguy,那么我猜您也忘记在AndroidManifest中将SpApplication注册为您的自定义Application类了。 - Vasiliy

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