Actually i am new to dependency injection and dagger, i have been writing boiler plate code all these time and am trying to learn dagger
I have a global class to save preference values
#Module(injects = AppPrefes.class)
public class AppPrefes {
private SharedPreferences appSharedPrefs;
private Editor prefsEditor;
public AppPrefes(Context context, String Preferncename) {
this.appSharedPrefs = context.getSharedPreferences(Preferncename,
Activity.MODE_PRIVATE);
this.prefsEditor = appSharedPrefs.edit();
}
/****
*
* getdata() get the value from the preference
*
* */
#Provides
public String getData(String key) {
return appSharedPrefs.getString(key, "");
}
/****
*
* SaveData() save the value to the preference
*
* */
#Provides
public void SaveData(String Tag, String text) {
prefsEditor.putString(Tag, text);
prefsEditor.commit();
}
}
How could i possibly inject this class in My activity
In my activity oncreate i have put like this
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ObjectGraph objectGraph = ObjectGraph.create();
AppPrefes app = objectGraph.get(AppPrefes.class);
}
but how should i pass dependency i.e the context and the preference name to AppPrefes class,i am completely new to dependency injection please correct me if i am wrong
Since dagger documentation seems to be little tough for me am asking this question.
I believe if you are willing to take the risk of creating a static variable to store the instance of the application,
public enum ApplicationHolder
{
INSTANCE;
private MyApplication application;
public MyApplication getApplication()
{
return application;
}
public void setApplication(MyApplication application)
{
this.application = application;
}
}
public MyApplication extends Application
{
#Override
public void onCreate()
{
super.onCreate();
ApplicationHolder.INSTANCE.setApplication(this);
objectGraph.create(new RootModule());
}
}
Afterwards, you can create a Dagger module which can provide the Context of the Application instance.
#Module(complete = false, library = true)
public class ContextModule
{
#Provides
public Context providesContext()
{
return ApplicationHolder.INSTANCE.getApplication().getApplicationContext();
}
}
#Module(
includes =
{
ContextModule.class
},
injects =
{
AppPrefes.class
}
)
public class RootModule
{
}
Then you can inject this in your Activity with #Inject, however you need to provide the String you want to inject as a constructor parameter like this: https://stackoverflow.com/a/18105271/2413303
The structure of modules and stuff for me were like here: How to set up DAGGER dependency injection from scratch in Android project?
Related
I'm getting this error while i'm trying to Build/Rebuild :
error: ir.atlaspio.roshdbookstore.DI.Components.ApplicationComponent scoped with #ir.atlaspio.roshdbookstore.DI.Scopes.ApplicationScope may not reference bindings with different scopes:
#Singleton class ir.atlaspio.roshdbookstore.Data.Prefs.AtlasPreferencesHelper
I'm trying to improve my code base on some projects and tutorials, right now i'm trying to doing dependency injection for SharedPreferences, which i've got above problem. It's not long time since i'm messing with Dagger 2 And DI, so excuse my beginner question and help me about this to find out where i'm doing this wrong. also there are some codes which they will inject SharedPrefereces but what i want is to use My own DataManager to do it right.
So here's how i created my project(Which is base on some tutorials):
Explaining Section(Which could be obvious for you guys): adding some method to set data into sharedpref.
AtlasPreferencesHelper :
#Singleton
public class AtlasPreferencesHelper implements PreferencesHelper {
private static final String PREF_KEY_USER_LOGGED_IN_MODE = "PREF_KEY_USER_LOGGED_IN_MODE";
private static final String PREF_KEY_FIRST_TIME_RUN_MODE = "PREF_KEY_FIRST_TIME_RUN_MODE";
private final SharedPreferences atlasPrefs;
#Inject
public AtlasPreferencesHelper(#ApplicationContext Context context,
#PreferenceInfo String prefFileName) {
atlasPrefs = context.getSharedPreferences(prefFileName, Context.MODE_PRIVATE);
}
#Override
public int getCurrentUserLoggedInMode() {
return atlasPrefs.getInt(PREF_KEY_USER_LOGGED_IN_MODE,
DataManager.LoggedInMode.LOGGED_IN_MODE_LOGGED_OUT.getType());
}
#Override
public void setCurrentUserLoggedInMode(DataManager.LoggedInMode mode) {
atlasPrefs.edit().putInt(PREF_KEY_USER_LOGGED_IN_MODE, mode.getType()).apply();
}
#Override
public int getCurrentFirstTimeStat() {
return atlasPrefs.getInt(PREF_KEY_FIRST_TIME_RUN_MODE,
DataManager.FirstTimeRun.FIRST_TIME_RUN_TRUE.getFrType());
}
#Override
public void setCurrentFirstTimeStat(DataManager.FirstTimeRun fMode) {
atlasPrefs.edit().putInt(PREF_KEY_FIRST_TIME_RUN_MODE, fMode.getFrType()).apply();
}
}
Explains : interface to be implemented in AtlasPreferencesHelper.
PreferenceHelper :
public interface PreferencesHelper {
int getCurrentUserLoggedInMode();
void setCurrentUserLoggedInMode(DataManager.LoggedInMode mode);
int getCurrentFirstTimeStat();
void setCurrentFirstTimeStat(DataManager.FirstTimeRun fMode);
}
Explains: Doing Job and getting access to activity to use methods.
AppDataManager :
public class AppDataManager implements DataManager {
private static final String TAG = "AppDataManager";
private final Context mContext;
private final PreferencesHelper mPrefencesHelper;
#Inject
public AppDataManager(#ApplicationContext Context context, PreferencesHelper prefencesHelper) {
mContext = context;
mPrefencesHelper = prefencesHelper;
}
#Override
public void setUserAssLoggedOut() {
setCurrentUserLoggedInMode(LoggedInMode.LOGGED_IN_MODE_LOGGED_OUT);
}
#Override
public int getCurrentUserLoggedInMode() {
return mPrefencesHelper.getCurrentUserLoggedInMode();
}
#Override
public void setCurrentUserLoggedInMode(LoggedInMode mode) {
mPrefencesHelper.setCurrentUserLoggedInMode(mode);
}
#Override
public int getCurrentFirstTimeStat() {
return mPrefencesHelper.getCurrentFirstTimeStat();
}
#Override
public void setCurrentFirstTimeStat(FirstTimeRun fMode) {
mPrefencesHelper.setCurrentFirstTimeStat(fMode);
}
}
Explains : DataManager interface implemented to AppDataManager
DataManager :
public interface DataManager extends PreferencesHelper {
void setUserAssLoggedOut();
enum LoggedInMode {
LOGGED_IN_MODE_LOGGED_OUT(0),
LOGGED_IN_MODE_SERVER(1);
private final int mType;
LoggedInMode(int type)
{
mType = type;
}
public int getType()
{
return mType;
}
}
enum FirstTimeRun {
FIRST_TIME_RUN_FALSE(0),
FIRST_TIME_RUN_TRUE(1);
private final int frType;
FirstTimeRun(int rType){
frType = rType;
}
public int getFrType()
{
return frType;
}
}
}
Explains : Components which, Context provide context, Retrofit Provide retrofit and SharedPref will provide Datamanager and SharedPref
ApplicationComponent :
#ApplicationScope
#Component(modules = {ContextModule.class,
RetrofitModule.class,
SharedPrefModule.class})
public interface ApplicationComponent {
MyAtlasAPI getApiReference();
DataManager getDataManager();
#ApplicationContext
Context getContext();
void injectApplication(AtlasApplication atlasApplication);
}
Explains : Module that will be implement in component for datamanager and sharedpref
SharedPrefModule :
#Module
public class SharedPrefModule {
#Provides
#PreferenceInfo
String providePreferenceName() {
return AppConstants.PREF_NAME;
}
#Provides
#Singleton
DataManager provideDataManager(AppDataManager appDataManager)
{
return appDataManager;
}
#Provides
#Singleton
PreferencesHelper providePreferencesHelper(AtlasPreferencesHelper atlasPreferencesHelper)
{
return atlasPreferencesHelper;
}
}
The error you're getting is telling it all. You're trying to reference to a module with a different scope than your component.
Looking at you ApplicationComponent:
#ApplicationScope
#Component(modules = {ContextModule.class,
RetrofitModule.class,
SharedPrefModule.class})
public interface ApplicationComponent { }
It's using the ApplicationScope
Your ApplicationComponent is then referring to SharedPrefModule.
If we take a look at that class:
#Module
public class SharedPrefModule {
#Provides
#Singleton
PreferencesHelper providePreferencesHelper(AtlasPreferencesHelper atlasPreferencesHelper) {
return atlasPreferencesHelper;
}
}
This module is using the Singleton scope.
Finally your AtlasPreferencesHelper is also scoped with Singleton:
#Singleton
public class AtlasPreferencesHelper {}
What you wanna do is align these scopes.
So either annotate the ApplicationComponent with #Singleton or use the #Application for all the modules within your ApplicationComponent
The question is in the subject, yet I'll repeat it again:
Is there a difference between the way Dagger2 treats #Singleton and custom sopes?
Also, if a class is annotated with some scope, is there a convenient way to expose it as a different scope (or unscoped), or do I need to write a provider method?
There is no difference between the way Dagger2 treats #Singleton and custom sopes.
Lets say we are using #User
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface User {
}
#Module
public class TwitterModule {
private final String user;
public TwitterModule(String user) {
this.user = user;
}
#Provides
#User
Tweeter provideTweeter(TwitterApi twitterApi) {
return new Tweeter(twitterApi, user);
}
#Provides
#User
Timeline provideTimeline(TwitterApi twitterApi) {
return new Timeline(twitterApi, user);
}
}
#Module
public class NetworkModule {
#Provides
#Singleton
OkHttpClient provideOkHttpClient() {
return new OkHttpClient();
}
#Provides
#Singleton
TwitterApi provideTwitterApi(OkHttpClient okHttpClient) {
return new TwitterApi(okHttpClient);
}
}
#Singleton
#Component(modules = {NetworkModule.class})
public interface ApiComponent {
TwitterApi api();
TwitterComponent twitterComponent(TwitterModule twitterModule);
}
#User
#Subcomponent(modules = {TwitterModule.class})
public interface TwitterComponent {
TwitterApplication app();
}
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
TwitterComponent twitterComponentForUserOne,twitterComponentForUserTwo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ApiComponent apiComponent = DaggerApiComponent.create();
twitterComponentForUserOne = apiComponent.twitterComponent(new TwitterModule("Amit Shekhar"));
twitterComponentForUserTwo = apiComponent.twitterComponent(new TwitterModule("Sumit Shekhar"));
// use twitterComponentOne and twitterComponentTwo for two users independently
}
#Override
protected void onDestroy() {
super.onDestroy();
twitterComponentForUserOne = null;
twitterComponentForUserTwo = null;
}
}
Here just we have to make sure that when we do not need the twitterComponent for that user. We have to assign null so that it gets garbage collected as I am doing here in onDestroy();
Finally, everything depends on component,if you have an instance of component in Application class it is not going to be garbage collected for whole application life-cycle.
Been blocked on this for a number of days.
For some reason my the 'application' member field in AndroidModule.java is null after injection.
AndroidModule.java
#Module(
library = true
)
public class AndroidModule {
#Inject MittoApplication application;
#Provides #Singleton
SharedPreferences provideSharedPreferences() {
return PreferenceManager.getDefaultSharedPreferences( application );
}
}
ApplicationModule.java
#Module(
includes = { ApiModule.class, AndroidModule.class },
library = true
)
public class ApplicationModule {
private Application application;
public ApplicationModule( Application application ) {
this.application = application;
}
#Provides #Singleton
Application providesApplication() {
return application;
}
}
BaseActivity.java
public class BaseActivity extends AppCompatActivity {
private ObjectGraph objectGraph;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
objectGraph = MittoApplication.getApplication(this).buildScopedObjectGraph(getModules());
objectGraph.inject(this);
}
#Override
protected void onDestroy() {
objectGraph = null;
super.onDestroy();
}
protected List<Object> getModules( ) {
return Arrays.<Object>asList(
);
}
}
MittoApplication.java
public class MittoApplication extends Application {
private static Context context;
private ObjectGraph objectGraph;
public void onCreate() {
super.onCreate();
MittoApplication.context = getApplicationContext();
initializeObjectGraph();
}
public static MittoApplication getApplication( Context context ) {
MittoApplication mittoApplication = (MittoApplication) context.getApplicationContext();
return mittoApplication;
}
public ObjectGraph getObjectGraph() {
return this.objectGraph;
}
public ObjectGraph buildObjectGraph( List<Object> modules ) {
return ObjectGraph.create(modules.toArray() );
}
public ObjectGraph buildScopedObjectGraph( List<Object> modules ) {
return objectGraph.plus(modules.toArray() );
}
private ObjectGraph buildInitialObjectGraph( List<Object> modules ) {
return ObjectGraph.create( modules.toArray() );
}
private void initializeObjectGraph() {
objectGraph = buildInitialObjectGraph( Arrays.<Object>asList( new ApplicationModule(this) ));
}
public static Context getContext() {
return MittoApplication.context;
}
}
I've spend ages pondering over this, I've looked at countless examples and blog sites. Would love someone smarter then I to assist.
Thanks for reading.
Field injection works only with direct invocation of the object graph. To obtain the application instance for your provider method, you need to reference it as a parameter of the provider method.
//#Inject MittoApplication application; //this won't work
#Provides #Singleton
SharedPreferences provideSharedPreferences(Application application) { //this will
return PreferenceManager.getDefaultSharedPreferences( application );
}
Also, you never actually provided a MittoApplication, only an Application.
And it's worth noting that you are using Dagger1, so I believe you will need to change your AndroidModule to be complete=false:
#Module(
library = true,
complete = false
)
public class AndroidModule {
There is alot of info on this topic but none of it assist me.
I have several global variables/fields inside the app's application class:
public class App extends Application {
private static App instance
public string PUBNUB_SUB = BuildConfig.PUBNUB_SUB_KEY;
public string PUBNUB_PUB = BuildConfig.PUBNUB_PUB_KEY;
public void onCreate() {
instance = this;
}
public static App getInstance(){
return instance;
}
}
In Activities/Fragments I'm successfully accessing those variables like so:
class ActivityA extend Activity {
App baseApp;
Pubnub pubnub;
public void onCreate(Bundle savedInstanceState){
baseApp = new(App)getApplicationContext();
Pubnub(baseApp.PUBNUB_SUB, baseApp.PUBNUB_PUB)
}
Now How do I access getApplicationContext() in a non-Activity/non-Fragment Class?
public class Events {
App baseApp;
Context mContext;
app = new(LoQooApp)mContext.getApplication(); ???
public Events(Context context) {
app = new(LoQooApp)mContext.getApplication(); ???
}
The above doesnt work, where should
app = new(LoQooApp) getApplication(); go?
Looks like PUBNUB_SUB and PUBNUB_PUB are constants, so you could declare them as public static final and then access using a static reference: App.PUBNUB_SUB and App.PUBNUB_PUB
I want to do something like this
private static final String url;
private static final String pass;
private static final String user;
static {
Bundle metadata = ctx.getPackageManager().getApplicationInfo(ctx.getPackageName(), PackageManager.GET_META_DATA).metaData;
url = (String) metadata.get("JMSQueueURL");
user = (String) metadata.get("JMSQueueUsername");
pass = (String) metadata.get("JMSQueuePassword");
}
So far it was in activity (but not as static), so that I was possible to get package manager, but now i want to move this piece of code to another class which doesn't inherits ContextWrapper (from where we can get package manager). Is it possible somehow? This class is something like util class.
You can pass the Context from the calling method to the method in the Util class and use the context there to get the details you want. This way you can call the method in the Util class from different modules in your application, with different contexts.
// Calling the Util method
Bundle metadata = Util.getMetaData(context);
...
// Inside the Util class
public static Bundle getMetaData(Context context) {
return context.getPackageManager().getApplicationInfo(ctx.getPackageName(), PackageManager.GET_META_DATA).metaData;
}
This allows me to access the metadata from anywhere in my application, without a context:
public class MyAndroidApp extends Application {
private static MyAndroidApp instance;
#Override
public void onCreate() {
super.onCreate();
instance = this;
}
public static MyAndroidApp getInstance() {
return instance;
}
public static Bundle getMetadata() {
try {
return getInstance()
.getPackageManager()
.getApplicationInfo(getInstance().getPackageName(), PackageManager.GET_META_DATA)
.metaData;
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
}