#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
}
}
Компонент:
- Если компонент внедряет службы с ограниченной областью действия (@Singleton — это область действия), сам компонент должен быть ограничен
- Если вы хотите внедрить в класс 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
классе:
- Способ создания экземпляра
ApplicationComponent
неверен - Это не ошибка, но вам действительно не нужны методы
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);
в activity2. @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);