Android использует внедрение зависимостей для простого пользовательского класса

#java #android #dependency-injection

#java #Android #внедрение зависимостей

Вопрос:

после поиска в Интернете информации об этой функции в большинстве тем или сообщений использовалась внедрение зависимостей для Retrofit или других полезных библиотек Android, но у меня есть некоторый пользовательский класс, который я хочу использовать с DI, и я не могу этого сделать, например, у меня есть простой пользовательский класс для использования SharePreference , и я использую его как одноэлементный класс

в моем коде я не могу назначить правильный 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
    }
}
  

теперь я пытаюсь использовать DI для этого:

Класс 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:

Я предполагаю, что вы пытаетесь внедрить в 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. Возможно, вы захотите проверить это, если собираетесь серьезно относиться к внедрению зависимостей.

Комментарии:

1. Большое спасибо, но после добавления void inject(ActivityMain target); в ApplicationComponent класс для внедрения в ActivityMain я получаю null object reference для этой строки как sharePreference.setInt(SP.SharedPrefsTypes.Login, 0); в activity

2. @mahdipishguy Тогда вам нужно будет добавить код activity к вопросу…

3. Я добавил это, спасибо

4. @mahdipishguy, на самом деле вы ничего не внедряете в этот клиент. Чтобы выполнить внедрение, вы должны добавить ((SpApplication)getApplication()).getComponent().inject(this) . Это следует сделать перед использованием внедренных сервисов

5. да, сэр. я забыл добавить это. теперь я получаю android.app.Application cannot be cast to com.pishguy.yendir.SpApplication ошибку после добавления этой строки ((SpApplication) getApplication()).getComponent().inject(this);